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
8 package org.opendaylight.openflowplugin.impl.device.initialization;
10 import com.google.common.base.Function;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.concurrent.Future;
20 import java.util.stream.Collectors;
21 import javax.annotation.Nonnull;
22 import javax.annotation.Nullable;
23 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
24 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
25 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
26 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
27 import org.opendaylight.openflowplugin.impl.common.MultipartReplyTranslatorUtil;
28 import org.opendaylight.openflowplugin.impl.datastore.MultipartWriterProvider;
29 import org.opendaylight.openflowplugin.impl.services.multilayer.MultiLayerMultipartCollectorService;
30 import org.opendaylight.openflowplugin.impl.services.singlelayer.SingleLayerMultipartCollectorService;
31 import org.opendaylight.openflowplugin.impl.util.DeviceInitializationUtil;
32 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
33 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterFeatures;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.Capabilities;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
38 import org.opendaylight.yangtools.yang.common.RpcResult;
39 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 public class OF13DeviceInitializer extends AbstractDeviceInitializer {
45 private static final Logger LOG = LoggerFactory.getLogger(OF13DeviceInitializer.class);
48 protected Future<Void> initializeNodeInformation(@Nonnull final DeviceContext deviceContext,
49 final boolean switchFeaturesMandatory,
50 final boolean skipTableFeatures,
51 @Nullable final MultipartWriterProvider multipartWriterProvider,
52 @Nullable final ConvertorExecutor convertorExecutor) {
53 final ConnectionContext connectionContext =
54 Preconditions.checkNotNull(deviceContext.getPrimaryConnectionContext());
55 final DeviceState deviceState = Preconditions.checkNotNull(deviceContext.getDeviceState());
56 final DeviceInfo deviceInfo = Preconditions.checkNotNull(deviceContext.getDeviceInfo());
57 final Capabilities capabilities = connectionContext.getFeatures().getCapabilities();
58 LOG.debug("Setting capabilities for device {}", deviceInfo);
59 DeviceStateUtil.setDeviceStateBasedOnV13Capabilities(deviceState, capabilities);
61 // First process description reply, write data to DS and write consequent data if successful
62 return Futures.transformAsync(
63 requestMultipart(MultipartType.OFPMPDESC, deviceContext),
65 translateAndWriteResult(
66 MultipartType.OFPMPDESC,
69 multipartWriterProvider,
72 final List<ListenableFuture<RpcResult<List<OfHeader>>>> futures = new ArrayList<>();
73 futures.add(requestAndProcessMultipart(MultipartType.OFPMPMETERFEATURES, deviceContext,
74 skipTableFeatures, multipartWriterProvider, convertorExecutor));
75 futures.add(requestAndProcessMultipart(MultipartType.OFPMPGROUPFEATURES, deviceContext,
76 skipTableFeatures, multipartWriterProvider, convertorExecutor));
77 futures.add(requestAndProcessMultipart(MultipartType.OFPMPTABLEFEATURES, deviceContext,
78 skipTableFeatures, multipartWriterProvider, convertorExecutor));
79 futures.add(requestAndProcessMultipart(MultipartType.OFPMPPORTDESC, deviceContext, skipTableFeatures,
80 multipartWriterProvider, convertorExecutor));
82 return Futures.transform(
83 switchFeaturesMandatory ? Futures.allAsList(futures) : Futures.successfulAsList(futures),
84 new Function<List<RpcResult<List<OfHeader>>>, Void>() {
86 public Void apply(final List<RpcResult<List<OfHeader>>> input) {
87 LOG.info("Static node {} successfully finished collecting",
88 deviceContext.getDeviceInfo());
91 }, MoreExecutors.directExecutor());
92 }, MoreExecutors.directExecutor());
97 * Request multipart of specified type and then run some processing on it.
99 * @param type multipart type
100 * @param deviceContext device context
101 * @param skipTableFeatures skip collecting of table features
102 * @param multipartWriterProvider multipart writer provider
103 * @param convertorExecutor convertor executor
104 * @return list of multipart messages unified to parent interface
106 private static ListenableFuture<RpcResult<List<OfHeader>>> requestAndProcessMultipart(final MultipartType type,
107 final DeviceContext deviceContext,
108 final boolean skipTableFeatures,
109 final MultipartWriterProvider multipartWriterProvider,
110 @Nullable final ConvertorExecutor convertorExecutor) {
111 final ListenableFuture<RpcResult<List<OfHeader>>> rpcResultListenableFuture =
112 MultipartType.OFPMPTABLEFEATURES.equals(type) && skipTableFeatures
113 ? RpcResultBuilder.<List<OfHeader>>success().buildFuture()
114 : requestMultipart(type, deviceContext);
116 createCallback(type, rpcResultListenableFuture, deviceContext, multipartWriterProvider, convertorExecutor);
117 return rpcResultListenableFuture;
121 * Inject callback ti future for specified multipart type. This callback will translate and write
122 * result of multipart messages.
124 * @param type multipart type
125 * @param future multipart collection future
126 * @param deviceContext device context
127 * @param multipartWriterProvider multipart writer provider
128 * @param convertorExecutor convertor executor
130 private static void createCallback(final MultipartType type,
131 final ListenableFuture<RpcResult<List<OfHeader>>> future,
132 final DeviceContext deviceContext,
133 @Nullable final MultipartWriterProvider multipartWriterProvider,
134 @Nullable final ConvertorExecutor convertorExecutor) {
135 Futures.addCallback(future, new FutureCallback<RpcResult<List<OfHeader>>>() {
137 public void onSuccess(final RpcResult<List<OfHeader>> result) {
138 if (result.getResult() != null) {
139 LOG.info("Static node {} info: {} collected", deviceContext.getDeviceInfo(), type);
140 translateAndWriteResult(
144 multipartWriterProvider,
147 result.getErrors().forEach(rpcError -> {
148 LOG.warn("Failed to retrieve static node {} info: {}", type, rpcError.getMessage());
150 if (LOG.isTraceEnabled() && rpcError.getCause() != null) {
151 LOG.trace("Detailed error:", rpcError.getCause());
155 // If table features are disabled or returned nothing, at least make empty tables
156 if (MultipartType.OFPMPTABLEFEATURES.equals(type)) {
157 DeviceInitializationUtil.makeEmptyTables(
159 deviceContext.getDeviceInfo(),
160 deviceContext.getPrimaryConnectionContext().getFeatures().getTables());
166 public void onFailure(final Throwable throwable) {
167 LOG.warn("Request of type {} for static info of node {} failed.",
168 type, deviceContext.getDeviceInfo());
170 }, MoreExecutors.directExecutor());
174 * Translate and write multipart messages from OpenflowJava.
176 * @param type multipart type
177 * @param result multipart messages
178 * @param deviceContext device context
179 * @param multipartWriterProvider multipart writer provider
180 * @param convertorExecutor convertor executor
182 @SuppressWarnings("checkstyle:IllegalCatch")
183 private static void translateAndWriteResult(final MultipartType type,
184 final List<OfHeader> result,
185 final DeviceContext deviceContext,
186 @Nullable final MultipartWriterProvider multipartWriterProvider,
187 @Nullable final ConvertorExecutor convertorExecutor) {
188 if (result != null) {
190 result.forEach(reply -> {
191 // First, translate collected data to proper openflowplugin representation
192 MultipartReplyTranslatorUtil
195 deviceContext.getDeviceInfo(),
197 deviceContext.oook())
198 .ifPresent(translatedReply -> {
199 // If we collected meter features, check if we have support for meters
200 // and pass this information to device context
201 if (MultipartType.OFPMPMETERFEATURES.equals(type)
202 && translatedReply instanceof MeterFeatures) {
203 final MeterFeatures meterFeatures = (MeterFeatures) translatedReply;
205 if (meterFeatures.getMaxMeter().getValue() > 0) {
206 deviceContext.getDeviceState().setMeterAvailable(true);
210 // Now. try to write translated collected features
211 Optional.ofNullable(multipartWriterProvider)
212 .flatMap(provider -> provider.lookup(type))
213 .ifPresent(writer -> writer.write(translatedReply, false));
216 } catch (final Exception e) {
217 LOG.warn("Failed to write node {} to DS ", deviceContext.getDeviceInfo(), e);
220 LOG.warn("Failed to write node {} to DS because we failed to gather device info.",
221 deviceContext.getDeviceInfo());
226 * Send request to device and unify different possible reply types from OpenflowJava to common parent interface.
228 * @param multipartType multipart type
229 * @param deviceContext device context
230 * @return unified replies
232 private static ListenableFuture<RpcResult<List<OfHeader>>> requestMultipart(final MultipartType multipartType,
233 final DeviceContext deviceContext) {
234 if (deviceContext.canUseSingleLayerSerialization()) {
235 final SingleLayerMultipartCollectorService service =
236 new SingleLayerMultipartCollectorService(deviceContext, deviceContext);
238 return Futures.transform(service.handleServiceCall(multipartType),
240 if (input.getResult() == null && input.isSuccessful()) {
241 return RpcResultBuilder.<List<OfHeader>>success(null).build();
244 return input.isSuccessful()
245 ? RpcResultBuilder.success(input
248 .map(OfHeader.class::cast)
249 .collect(Collectors.toList()))
251 : RpcResultBuilder.<List<OfHeader>>failed()
252 .withRpcErrors(input.getErrors())
254 }, MoreExecutors.directExecutor());
257 final MultiLayerMultipartCollectorService service =
258 new MultiLayerMultipartCollectorService(deviceContext, deviceContext);
260 return Futures.transform(service.handleServiceCall(multipartType), input -> {
261 if (input.getResult() == null && input.isSuccessful()) {
262 return RpcResultBuilder.<List<OfHeader>>success(null).build();
265 return input.isSuccessful() ? RpcResultBuilder
266 .success(input.getResult().stream().map(OfHeader.class::cast).collect(Collectors.toList())).build()
267 : RpcResultBuilder.<List<OfHeader>>failed().withRpcErrors(input.getErrors()).build();
268 }, MoreExecutors.directExecutor());