2 * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.openflowplugin.impl.device.initialization;
11 import com.google.common.base.Function;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.AsyncFunction;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.Objects;
20 import java.util.Optional;
21 import java.util.concurrent.Future;
22 import java.util.stream.Collectors;
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
26 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
27 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
28 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
29 import org.opendaylight.openflowplugin.impl.common.MultipartReplyTranslatorUtil;
30 import org.opendaylight.openflowplugin.impl.datastore.MultipartWriterProvider;
31 import org.opendaylight.openflowplugin.impl.services.multilayer.MultiLayerMultipartCollectorService;
32 import org.opendaylight.openflowplugin.impl.services.singlelayer.SingleLayerMultipartCollectorService;
33 import org.opendaylight.openflowplugin.impl.util.DeviceInitializationUtil;
34 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
35 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterFeatures;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.multipart.types.rev170112.MultipartReply;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.Capabilities;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
41 import org.opendaylight.yangtools.yang.common.RpcResult;
42 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 public class OF13DeviceInitializer extends AbstractDeviceInitializer {
48 private static final Logger LOG = LoggerFactory.getLogger(OF13DeviceInitializer.class);
51 protected Future<Void> initializeNodeInformation(@Nonnull final DeviceContext deviceContext,
52 final boolean switchFeaturesMandatory,
53 final boolean skipTableFeatures,
54 @Nullable final MultipartWriterProvider multipartWriterProvider,
55 @Nullable final ConvertorExecutor convertorExecutor) {
56 final ConnectionContext connectionContext =
57 Preconditions.checkNotNull(deviceContext.getPrimaryConnectionContext());
58 final DeviceState deviceState = Preconditions.checkNotNull(deviceContext.getDeviceState());
59 final DeviceInfo deviceInfo = Preconditions.checkNotNull(deviceContext.getDeviceInfo());
60 final Capabilities capabilities = connectionContext.getFeatures().getCapabilities();
61 LOG.debug("Setting capabilities for device {}", deviceInfo);
62 DeviceStateUtil.setDeviceStateBasedOnV13Capabilities(deviceState, capabilities);
64 // First process description reply, write data to DS and write consequent data if successful
65 return Futures.transformAsync(
66 requestMultipart(MultipartType.OFPMPDESC, deviceContext),
67 (AsyncFunction<RpcResult<List<OfHeader>>, Void>) input -> {
68 translateAndWriteResult(
69 MultipartType.OFPMPDESC,
72 multipartWriterProvider,
75 final List<ListenableFuture<RpcResult<List<OfHeader>>>> futures = new ArrayList<>();
76 futures.add(requestAndProcessMultipart(MultipartType.OFPMPMETERFEATURES, deviceContext,
77 skipTableFeatures, multipartWriterProvider, convertorExecutor));
78 futures.add(requestAndProcessMultipart(MultipartType.OFPMPGROUPFEATURES, deviceContext,
79 skipTableFeatures, multipartWriterProvider, convertorExecutor));
80 futures.add(requestAndProcessMultipart(MultipartType.OFPMPTABLEFEATURES, deviceContext,
81 skipTableFeatures, multipartWriterProvider, convertorExecutor));
82 futures.add(requestAndProcessMultipart(MultipartType.OFPMPPORTDESC, deviceContext, skipTableFeatures,
83 multipartWriterProvider, convertorExecutor));
85 return Futures.transform(
86 (switchFeaturesMandatory ? Futures.allAsList(futures) : Futures.successfulAsList(futures)),
87 new Function<List<RpcResult<List<OfHeader>>>, Void>() {
90 public Void apply(@Nullable final List<RpcResult<List<OfHeader>>> input) {
91 LOG.info("Static node {} successfully finished collecting",
92 deviceContext.getDeviceInfo());
101 * Request multipart of specified type and then run some processing on it.
103 * @param type multipart type
104 * @param deviceContext device context
105 * @param skipTableFeatures skip collecting of table features
106 * @param multipartWriterProvider multipart writer provider
107 * @param convertorExecutor convertor executor
108 * @return list of multipart messages unified to parent interface
110 private static ListenableFuture<RpcResult<List<OfHeader>>> requestAndProcessMultipart(final MultipartType type,
111 final DeviceContext deviceContext,
112 final boolean skipTableFeatures,
113 final MultipartWriterProvider multipartWriterProvider,
114 @Nullable final ConvertorExecutor convertorExecutor) {
115 final ListenableFuture<RpcResult<List<OfHeader>>> rpcResultListenableFuture =
116 MultipartType.OFPMPTABLEFEATURES.equals(type) && skipTableFeatures
117 ? RpcResultBuilder.<List<OfHeader>>success().buildFuture()
118 : requestMultipart(type, deviceContext);
120 createCallback(type, rpcResultListenableFuture, deviceContext, multipartWriterProvider, convertorExecutor);
121 return rpcResultListenableFuture;
125 * Inject callback ti future for specified multipart type. This callback will translate and write
126 * result of multipart messages.
128 * @param type multipart type
129 * @param future multipart collection future
130 * @param deviceContext device context
131 * @param multipartWriterProvider multipart writer provider
132 * @param convertorExecutor convertor executor
134 private static void createCallback(final MultipartType type,
135 final ListenableFuture<RpcResult<List<OfHeader>>> future,
136 final DeviceContext deviceContext,
137 @Nullable final MultipartWriterProvider multipartWriterProvider,
138 @Nullable final ConvertorExecutor convertorExecutor) {
139 Futures.addCallback(future, new FutureCallback<RpcResult<List<OfHeader>>>() {
141 public void onSuccess(final RpcResult<List<OfHeader>> result) {
142 if (Objects.nonNull(result.getResult())) {
143 LOG.info("Static node {} info: {} collected", deviceContext.getDeviceInfo(), type);
144 translateAndWriteResult(
148 multipartWriterProvider,
151 result.getErrors().forEach(rpcError -> {
152 LOG.warn("Failed to retrieve static node {} info: {}", type, rpcError.getMessage());
154 if (LOG.isTraceEnabled() && Objects.nonNull(rpcError.getCause())) {
155 LOG.trace("Detailed error:", rpcError.getCause());
159 // If table features are disabled or returned nothing, at least make empty tables
160 if (MultipartType.OFPMPTABLEFEATURES.equals(type)) {
161 DeviceInitializationUtil.makeEmptyTables(
163 deviceContext.getDeviceInfo(),
164 deviceContext.getPrimaryConnectionContext().getFeatures().getTables());
170 public void onFailure(@Nonnull final Throwable throwable) {
171 LOG.warn("Request of type {} for static info of node {} failed.",
172 type, deviceContext.getDeviceInfo());
178 * Translate and write multipart messages from OpenflowJava.
180 * @param type multipart type
181 * @param result multipart messages
182 * @param deviceContext device context
183 * @param multipartWriterProvider multipart writer provider
184 * @param convertorExecutor convertor executor
186 @SuppressWarnings("checkstyle:IllegalCatch")
187 private static void translateAndWriteResult(final MultipartType type,
188 final List<OfHeader> result,
189 final DeviceContext deviceContext,
190 @Nullable final MultipartWriterProvider multipartWriterProvider,
191 @Nullable final ConvertorExecutor convertorExecutor) {
192 if (Objects.nonNull(result)) {
194 result.forEach(reply -> {
195 // First, translate collected data to proper openflowplugin representation
196 MultipartReplyTranslatorUtil
199 deviceContext.getDeviceInfo(),
201 deviceContext.oook())
202 .ifPresent(translatedReply -> {
203 // If we collected meter features, check if we have support for meters
204 // and pass this information to device context
205 if (MultipartType.OFPMPMETERFEATURES.equals(type)
206 && translatedReply instanceof MeterFeatures) {
207 final MeterFeatures meterFeatures = (MeterFeatures) translatedReply;
209 if (meterFeatures.getMaxMeter().getValue() > 0) {
210 deviceContext.getDeviceState().setMeterAvailable(true);
214 // Now. try to write translated collected features
215 Optional.ofNullable(multipartWriterProvider)
216 .flatMap(provider -> provider.lookup(type))
217 .ifPresent(writer -> writer.write(translatedReply, false));
220 } catch (final Exception e) {
221 LOG.warn("Failed to write node {} to DS ", deviceContext.getDeviceInfo(), e);
224 LOG.warn("Failed to write node {} to DS because we failed to gather device info.",
225 deviceContext.getDeviceInfo());
230 * Send request to device and unify different possible reply types from OpenflowJava to common parent interface.
232 * @param multipartType multipart type
233 * @param deviceContext device context
234 * @return unified replies
236 private static ListenableFuture<RpcResult<List<OfHeader>>> requestMultipart(final MultipartType multipartType,
237 final DeviceContext deviceContext) {
238 if (deviceContext.canUseSingleLayerSerialization()) {
239 final SingleLayerMultipartCollectorService service =
240 new SingleLayerMultipartCollectorService(deviceContext, deviceContext);
242 return Futures.transform(service.handleServiceCall(multipartType),
243 new Function<RpcResult<List<MultipartReply>>, RpcResult<List<OfHeader>>>() {
246 public RpcResult<List<OfHeader>> apply(final RpcResult<List<MultipartReply>> input) {
247 if (Objects.isNull(input.getResult()) && input.isSuccessful()) {
248 final List<OfHeader> temp = null;
249 return RpcResultBuilder.success(temp).build();
252 return input.isSuccessful()
253 ? RpcResultBuilder.success(input
256 .map(OfHeader.class::cast)
257 .collect(Collectors.toList()))
259 : RpcResultBuilder.<List<OfHeader>>failed()
260 .withRpcErrors(input.getErrors())
266 final MultiLayerMultipartCollectorService service =
267 new MultiLayerMultipartCollectorService(deviceContext, deviceContext);
269 return Futures.transform(service.handleServiceCall(multipartType), new Function<RpcResult<List<org
270 .opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply>>,
271 RpcResult<List<OfHeader>>>() {
274 public RpcResult<List<OfHeader>> apply(final RpcResult<List<org.opendaylight.yang.gen.v1.urn.opendaylight
275 .openflow.protocol.rev130731.MultipartReply>> input) {
276 if (Objects.isNull(input.getResult()) && input.isSuccessful()) {
277 final List<OfHeader> temp = null;
278 return RpcResultBuilder.success(temp).build();
281 return input.isSuccessful()
282 ? RpcResultBuilder.success(input
285 .map(OfHeader.class::cast)
286 .collect(Collectors.toList()))
288 : RpcResultBuilder.<List<OfHeader>>failed()
289 .withRpcErrors(input.getErrors())