Encapsulate OpenFlowPlugin configuration
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / device / initialization / OF13DeviceInitializer.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.openflowplugin.impl.device.initialization;
10
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;
45
46 public class OF13DeviceInitializer extends AbstractDeviceInitializer {
47
48     private static final Logger LOG = LoggerFactory.getLogger(OF13DeviceInitializer.class);
49
50     @Override
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 = Preconditions.checkNotNull(deviceContext.getPrimaryConnectionContext());
57         final DeviceState deviceState = Preconditions.checkNotNull(deviceContext.getDeviceState());
58         final DeviceInfo deviceInfo = Preconditions.checkNotNull(deviceContext.getDeviceInfo());
59         final Capabilities capabilities = connectionContext.getFeatures().getCapabilities();
60         LOG.debug("Setting capabilities for device {}", deviceInfo.getLOGValue());
61         DeviceStateUtil.setDeviceStateBasedOnV13Capabilities(deviceState, capabilities);
62
63         // First process description reply, write data to DS and write consequent data if successful
64         return Futures.transformAsync(
65             requestMultipart(MultipartType.OFPMPDESC, deviceContext),
66             (AsyncFunction<RpcResult<List<OfHeader>>, Void>) input -> {
67                 translateAndWriteResult(
68                     MultipartType.OFPMPDESC,
69                     input.getResult(),
70                     deviceContext,
71                     multipartWriterProvider,
72                     convertorExecutor);
73
74                 final List<ListenableFuture<RpcResult<List<OfHeader>>>> futures = new ArrayList<>();
75                 futures.add(requestAndProcessMultipart(MultipartType.OFPMPMETERFEATURES, deviceContext, skipTableFeatures, multipartWriterProvider, convertorExecutor));
76                 futures.add(requestAndProcessMultipart(MultipartType.OFPMPGROUPFEATURES, deviceContext, skipTableFeatures, multipartWriterProvider, convertorExecutor));
77                 futures.add(requestAndProcessMultipart(MultipartType.OFPMPTABLEFEATURES, deviceContext, skipTableFeatures, multipartWriterProvider, convertorExecutor));
78                 futures.add(requestAndProcessMultipart(MultipartType.OFPMPPORTDESC, deviceContext, skipTableFeatures, multipartWriterProvider, convertorExecutor));
79
80                 return Futures.transform(
81                     (switchFeaturesMandatory ? Futures.allAsList(futures) : Futures.successfulAsList(futures)),
82                     new Function<List<RpcResult<List<OfHeader>>>, Void>() {
83                         @Nullable
84                         @Override
85                         public Void apply(@Nullable final List<RpcResult<List<OfHeader>>> input) {
86                             LOG.info("Static node {} successfully finished collecting", deviceContext.getDeviceInfo().getLOGValue());
87                             return null;
88                         }
89                     });
90             });
91
92     }
93
94     /**
95      * Request multipart of specified type and then run some processing on it
96      * @param type multipart type
97      * @param deviceContext device context
98      * @param skipTableFeatures skip collecting of table features
99      * @param multipartWriterProvider multipart writer provider
100      * @param convertorExecutor convertor executor
101      * @return list of multipart messages unified to parent interface
102      */
103     private static ListenableFuture<RpcResult<List<OfHeader>>> requestAndProcessMultipart(final MultipartType type,
104                                                                                           final DeviceContext deviceContext,
105                                                                                           final boolean skipTableFeatures,
106                                                                                           final MultipartWriterProvider multipartWriterProvider,
107                                                                                           @Nullable final ConvertorExecutor convertorExecutor) {
108         final ListenableFuture<RpcResult<List<OfHeader>>> rpcResultListenableFuture =
109             MultipartType.OFPMPTABLEFEATURES.equals(type) && skipTableFeatures
110                 ? RpcResultBuilder.<List<OfHeader>>success().buildFuture()
111                 : requestMultipart(type, deviceContext);
112
113         createCallback(type, rpcResultListenableFuture, deviceContext, multipartWriterProvider, convertorExecutor);
114         return rpcResultListenableFuture;
115     }
116
117     /**
118      * Inject callback ti future for specified multipart type. This callback will translate and write
119      * result of multipart messages
120      * @param type multipart type
121      * @param future multipart collection future
122      * @param deviceContext device context
123      * @param multipartWriterProvider multipart writer provider
124      * @param convertorExecutor convertor executor
125      */
126     private static void createCallback(final MultipartType type,
127                                        final ListenableFuture<RpcResult<List<OfHeader>>> future,
128                                        final DeviceContext deviceContext,
129                                        @Nullable final MultipartWriterProvider multipartWriterProvider,
130                                        @Nullable final ConvertorExecutor convertorExecutor) {
131         Futures.addCallback(future, new FutureCallback<RpcResult<List<OfHeader>>>() {
132             @Override
133             public void onSuccess(final RpcResult<List<OfHeader>> result) {
134                 if (Objects.nonNull(result.getResult())) {
135                     LOG.info("Static node {} info: {} collected", deviceContext.getDeviceInfo().getLOGValue(), type);
136                     translateAndWriteResult(
137                         type,
138                         result.getResult(),
139                         deviceContext,
140                         multipartWriterProvider,
141                         convertorExecutor);
142                 } else {
143                     result.getErrors().forEach(rpcError -> {
144                         LOG.warn("Failed to retrieve static node {} info: {}", type, rpcError.getMessage());
145
146                         if (LOG.isTraceEnabled() && Objects.nonNull(rpcError.getCause())) {
147                             LOG.trace("Detailed error:", rpcError.getCause());
148                         }
149                     });
150
151                     // If table features are disabled or returned nothing, at least make empty tables
152                     if (MultipartType.OFPMPTABLEFEATURES.equals(type)) {
153                         DeviceInitializationUtil.makeEmptyTables(
154                             deviceContext,
155                             deviceContext.getDeviceInfo(),
156                             deviceContext.getPrimaryConnectionContext().getFeatures().getTables());
157                     }
158                 }
159             }
160
161             @Override
162             public void onFailure(@Nonnull final Throwable t) {
163                 LOG.warn("Request of type {} for static info of node {} failed.", type, deviceContext.getDeviceInfo().getLOGValue());
164             }
165         });
166     }
167
168     /**
169      * Translate and write multipart messages from OpenflowJava
170      * @param type multipart type
171      * @param result multipart messages
172      * @param deviceContext device context
173      * @param multipartWriterProvider multipart writer provider
174      * @param convertorExecutor convertor executor
175      */
176     private static void translateAndWriteResult(final MultipartType type,
177                                                 final List<OfHeader> result,
178                                                 final DeviceContext deviceContext,
179                                                 @Nullable final MultipartWriterProvider multipartWriterProvider,
180                                                 @Nullable final ConvertorExecutor convertorExecutor) {
181         if (Objects.nonNull(result)) {
182             try {
183                 result.forEach(reply -> {
184                     // First, translate collected data to proper openflowplugin representation
185                     MultipartReplyTranslatorUtil
186                         .translate(
187                             reply,
188                             deviceContext.getDeviceInfo(),
189                             convertorExecutor,
190                             deviceContext.oook())
191                         .ifPresent(translatedReply -> {
192                             // If we collected meter features, check if we have support for meters
193                             // and pass this information to device context
194                             if (MultipartType.OFPMPMETERFEATURES.equals(type) &&
195                                 translatedReply instanceof MeterFeatures) {
196                                 final MeterFeatures meterFeatures = (MeterFeatures) translatedReply;
197
198                                 if (meterFeatures.getMaxMeter().getValue() > 0) {
199                                     deviceContext.getDeviceState().setMeterAvailable(true);
200                                 }
201                             }
202
203                             // Now. try to write translated collected features
204                             Optional.ofNullable(multipartWriterProvider)
205                                 .flatMap(provider -> provider.lookup(type))
206                                 .ifPresent(writer -> writer.write(translatedReply, false));
207                         });
208                 });
209             } catch (final Exception e) {
210                 LOG.warn("Failed to write node {} to DS ", deviceContext.getDeviceInfo().getLOGValue(), e);
211             }
212         } else {
213             LOG.warn("Failed to write node {} to DS because we failed to gather device info.",
214                 deviceContext.getDeviceInfo().getLOGValue());
215         }
216     }
217
218     /**
219      * Send request to device and unify different possible reply types from OpenflowJava to common parent interface
220      * @param multipartType multipart type
221      * @param deviceContext device context
222      * @return unified replies
223      */
224     private static ListenableFuture<RpcResult<List<OfHeader>>> requestMultipart(final MultipartType multipartType,
225                                                                                 final DeviceContext deviceContext) {
226         if (deviceContext.canUseSingleLayerSerialization()) {
227             final SingleLayerMultipartCollectorService service =
228                 new SingleLayerMultipartCollectorService(deviceContext, deviceContext);
229
230             return Futures.transform(service.handleServiceCall(multipartType), new Function<RpcResult<List<MultipartReply>>, RpcResult<List<OfHeader>>>() {
231                 @Nonnull
232                 @Override
233                 public RpcResult<List<OfHeader>> apply(final RpcResult<List<MultipartReply>> input) {
234                     if (Objects.isNull(input.getResult()) && input.isSuccessful()) {
235                         final List<OfHeader> temp = null;
236                         return RpcResultBuilder.success(temp).build();
237                     }
238
239                     return input.isSuccessful()
240                         ? RpcResultBuilder.success(input
241                             .getResult()
242                         .stream()
243                         .map(OfHeader.class::cast)
244                         .collect(Collectors.toList()))
245                         .build()
246                         : RpcResultBuilder.<List<OfHeader>>failed()
247                         .withRpcErrors(input.getErrors())
248                         .build();
249                 }
250             });
251         }
252
253         final MultiLayerMultipartCollectorService service =
254             new MultiLayerMultipartCollectorService(deviceContext, deviceContext);
255
256         return Futures.transform(service.handleServiceCall(multipartType), new Function<RpcResult<List<org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply>>, RpcResult<List<OfHeader>>>() {
257             @Nonnull
258             @Override
259             public RpcResult<List<OfHeader>> apply(final RpcResult<List<org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply>> input) {
260                 if (Objects.isNull(input.getResult()) && input.isSuccessful()) {
261                     final List<OfHeader> temp = null;
262                     return RpcResultBuilder.success(temp).build();
263                 }
264
265                 return input.isSuccessful()
266                     ? RpcResultBuilder.success(input
267                     .getResult()
268                     .stream()
269                     .map(OfHeader.class::cast)
270                     .collect(Collectors.toList()))
271                     .build()
272                     : RpcResultBuilder.<List<OfHeader>>failed()
273                     .withRpcErrors(input.getErrors())
274                     .build();
275             }
276         });
277     }
278
279 }