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 com.google.common.util.concurrent.MoreExecutors;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Objects;
21 import java.util.Optional;
22 import java.util.concurrent.Future;
23 import java.util.stream.Collectors;
24 import javax.annotation.Nonnull;
25 import javax.annotation.Nullable;
26 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
27 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
28 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
29 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
30 import org.opendaylight.openflowplugin.impl.common.MultipartReplyTranslatorUtil;
31 import org.opendaylight.openflowplugin.impl.datastore.MultipartWriterProvider;
32 import org.opendaylight.openflowplugin.impl.services.multilayer.MultiLayerMultipartCollectorService;
33 import org.opendaylight.openflowplugin.impl.services.singlelayer.SingleLayerMultipartCollectorService;
34 import org.opendaylight.openflowplugin.impl.util.DeviceInitializationUtil;
35 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
36 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterFeatures;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.multipart.types.rev170112.MultipartReply;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.Capabilities;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
42 import org.opendaylight.yangtools.yang.common.RpcResult;
43 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 public class OF13DeviceInitializer extends AbstractDeviceInitializer {
49 private static final Logger LOG = LoggerFactory.getLogger(OF13DeviceInitializer.class);
52 protected Future<Void> initializeNodeInformation(@Nonnull final DeviceContext deviceContext,
53 final boolean switchFeaturesMandatory,
54 final boolean skipTableFeatures,
55 @Nullable final MultipartWriterProvider multipartWriterProvider,
56 @Nullable final ConvertorExecutor convertorExecutor) {
57 final ConnectionContext connectionContext =
58 Preconditions.checkNotNull(deviceContext.getPrimaryConnectionContext());
59 final DeviceState deviceState = Preconditions.checkNotNull(deviceContext.getDeviceState());
60 final DeviceInfo deviceInfo = Preconditions.checkNotNull(deviceContext.getDeviceInfo());
61 final Capabilities capabilities = connectionContext.getFeatures().getCapabilities();
62 LOG.debug("Setting capabilities for device {}", deviceInfo);
63 DeviceStateUtil.setDeviceStateBasedOnV13Capabilities(deviceState, capabilities);
65 // First process description reply, write data to DS and write consequent data if successful
66 return Futures.transformAsync(
67 requestMultipart(MultipartType.OFPMPDESC, deviceContext),
68 (AsyncFunction<RpcResult<List<OfHeader>>, Void>) input -> {
69 translateAndWriteResult(
70 MultipartType.OFPMPDESC,
73 multipartWriterProvider,
76 final List<ListenableFuture<RpcResult<List<OfHeader>>>> futures = new ArrayList<>();
77 futures.add(requestAndProcessMultipart(MultipartType.OFPMPMETERFEATURES, deviceContext,
78 skipTableFeatures, multipartWriterProvider, convertorExecutor));
79 futures.add(requestAndProcessMultipart(MultipartType.OFPMPGROUPFEATURES, deviceContext,
80 skipTableFeatures, multipartWriterProvider, convertorExecutor));
81 futures.add(requestAndProcessMultipart(MultipartType.OFPMPTABLEFEATURES, deviceContext,
82 skipTableFeatures, multipartWriterProvider, convertorExecutor));
83 futures.add(requestAndProcessMultipart(MultipartType.OFPMPPORTDESC, deviceContext, skipTableFeatures,
84 multipartWriterProvider, convertorExecutor));
86 return Futures.transform(
87 (switchFeaturesMandatory ? Futures.allAsList(futures) : Futures.successfulAsList(futures)),
88 new Function<List<RpcResult<List<OfHeader>>>, Void>() {
91 public Void apply(@Nullable final List<RpcResult<List<OfHeader>>> input) {
92 LOG.info("Static node {} successfully finished collecting",
93 deviceContext.getDeviceInfo());
102 * Request multipart of specified type and then run some processing on it.
104 * @param type multipart type
105 * @param deviceContext device context
106 * @param skipTableFeatures skip collecting of table features
107 * @param multipartWriterProvider multipart writer provider
108 * @param convertorExecutor convertor executor
109 * @return list of multipart messages unified to parent interface
111 private static ListenableFuture<RpcResult<List<OfHeader>>> requestAndProcessMultipart(final MultipartType type,
112 final DeviceContext deviceContext,
113 final boolean skipTableFeatures,
114 final MultipartWriterProvider multipartWriterProvider,
115 @Nullable final ConvertorExecutor convertorExecutor) {
116 final ListenableFuture<RpcResult<List<OfHeader>>> rpcResultListenableFuture =
117 MultipartType.OFPMPTABLEFEATURES.equals(type) && skipTableFeatures
118 ? RpcResultBuilder.<List<OfHeader>>success().buildFuture()
119 : requestMultipart(type, deviceContext);
121 createCallback(type, rpcResultListenableFuture, deviceContext, multipartWriterProvider, convertorExecutor);
122 return rpcResultListenableFuture;
126 * Inject callback ti future for specified multipart type. This callback will translate and write
127 * result of multipart messages.
129 * @param type multipart type
130 * @param future multipart collection future
131 * @param deviceContext device context
132 * @param multipartWriterProvider multipart writer provider
133 * @param convertorExecutor convertor executor
135 private static void createCallback(final MultipartType type,
136 final ListenableFuture<RpcResult<List<OfHeader>>> future,
137 final DeviceContext deviceContext,
138 @Nullable final MultipartWriterProvider multipartWriterProvider,
139 @Nullable final ConvertorExecutor convertorExecutor) {
140 Futures.addCallback(future, new FutureCallback<RpcResult<List<OfHeader>>>() {
142 public void onSuccess(final RpcResult<List<OfHeader>> result) {
143 if (Objects.nonNull(result.getResult())) {
144 LOG.info("Static node {} info: {} collected", deviceContext.getDeviceInfo(), type);
145 translateAndWriteResult(
149 multipartWriterProvider,
152 result.getErrors().forEach(rpcError -> {
153 LOG.warn("Failed to retrieve static node {} info: {}", type, rpcError.getMessage());
155 if (LOG.isTraceEnabled() && Objects.nonNull(rpcError.getCause())) {
156 LOG.trace("Detailed error:", rpcError.getCause());
160 // If table features are disabled or returned nothing, at least make empty tables
161 if (MultipartType.OFPMPTABLEFEATURES.equals(type)) {
162 DeviceInitializationUtil.makeEmptyTables(
164 deviceContext.getDeviceInfo(),
165 deviceContext.getPrimaryConnectionContext().getFeatures().getTables());
171 public void onFailure(@Nonnull final Throwable throwable) {
172 LOG.warn("Request of type {} for static info of node {} failed.",
173 type, deviceContext.getDeviceInfo());
175 }, MoreExecutors.directExecutor());
179 * Translate and write multipart messages from OpenflowJava.
181 * @param type multipart type
182 * @param result multipart messages
183 * @param deviceContext device context
184 * @param multipartWriterProvider multipart writer provider
185 * @param convertorExecutor convertor executor
187 @SuppressWarnings("checkstyle:IllegalCatch")
188 private static void translateAndWriteResult(final MultipartType type,
189 final List<OfHeader> result,
190 final DeviceContext deviceContext,
191 @Nullable final MultipartWriterProvider multipartWriterProvider,
192 @Nullable final ConvertorExecutor convertorExecutor) {
193 if (Objects.nonNull(result)) {
195 result.forEach(reply -> {
196 // First, translate collected data to proper openflowplugin representation
197 MultipartReplyTranslatorUtil
200 deviceContext.getDeviceInfo(),
202 deviceContext.oook())
203 .ifPresent(translatedReply -> {
204 // If we collected meter features, check if we have support for meters
205 // and pass this information to device context
206 if (MultipartType.OFPMPMETERFEATURES.equals(type)
207 && translatedReply instanceof MeterFeatures) {
208 final MeterFeatures meterFeatures = (MeterFeatures) translatedReply;
210 if (meterFeatures.getMaxMeter().getValue() > 0) {
211 deviceContext.getDeviceState().setMeterAvailable(true);
215 // Now. try to write translated collected features
216 Optional.ofNullable(multipartWriterProvider)
217 .flatMap(provider -> provider.lookup(type))
218 .ifPresent(writer -> writer.write(translatedReply, false));
221 } catch (final Exception e) {
222 LOG.warn("Failed to write node {} to DS ", deviceContext.getDeviceInfo(), e);
225 LOG.warn("Failed to write node {} to DS because we failed to gather device info.",
226 deviceContext.getDeviceInfo());
231 * Send request to device and unify different possible reply types from OpenflowJava to common parent interface.
233 * @param multipartType multipart type
234 * @param deviceContext device context
235 * @return unified replies
237 private static ListenableFuture<RpcResult<List<OfHeader>>> requestMultipart(final MultipartType multipartType,
238 final DeviceContext deviceContext) {
239 if (deviceContext.canUseSingleLayerSerialization()) {
240 final SingleLayerMultipartCollectorService service =
241 new SingleLayerMultipartCollectorService(deviceContext, deviceContext);
243 return Futures.transform(service.handleServiceCall(multipartType),
244 new Function<RpcResult<List<MultipartReply>>, RpcResult<List<OfHeader>>>() {
247 public RpcResult<List<OfHeader>> apply(final RpcResult<List<MultipartReply>> input) {
248 if (Objects.isNull(input.getResult()) && input.isSuccessful()) {
249 final List<OfHeader> temp = null;
250 return RpcResultBuilder.success(temp).build();
253 return input.isSuccessful()
254 ? RpcResultBuilder.success(input
257 .map(OfHeader.class::cast)
258 .collect(Collectors.toList()))
260 : RpcResultBuilder.<List<OfHeader>>failed()
261 .withRpcErrors(input.getErrors())
267 final MultiLayerMultipartCollectorService service =
268 new MultiLayerMultipartCollectorService(deviceContext, deviceContext);
270 return Futures.transform(service.handleServiceCall(multipartType), new Function<RpcResult<List<org
271 .opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply>>,
272 RpcResult<List<OfHeader>>>() {
275 public RpcResult<List<OfHeader>> apply(final RpcResult<List<org.opendaylight.yang.gen.v1.urn.opendaylight
276 .openflow.protocol.rev130731.MultipartReply>> input) {
277 if (Objects.isNull(input.getResult()) && input.isSuccessful()) {
278 final List<OfHeader> temp = null;
279 return RpcResultBuilder.success(temp).build();
282 return input.isSuccessful()
283 ? RpcResultBuilder.success(input
286 .map(OfHeader.class::cast)
287 .collect(Collectors.toList()))
289 : RpcResultBuilder.<List<OfHeader>>failed()
290 .withRpcErrors(input.getErrors())