/*
* Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.openflowplugin.impl.device.initialization;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.impl.common.MultipartReplyTranslatorUtil;
import org.opendaylight.openflowplugin.impl.datastore.MultipartWriterProvider;
import org.opendaylight.openflowplugin.impl.services.multilayer.MultiLayerMultipartCollectorService;
import org.opendaylight.openflowplugin.impl.services.singlelayer.SingleLayerMultipartCollectorService;
import org.opendaylight.openflowplugin.impl.util.DeviceInitializationUtil;
import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterFeatures;
import org.opendaylight.yang.gen.v1.urn.opendaylight.multipart.types.rev170112.MultipartReply;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.Capabilities;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OF13DeviceInitializer extends AbstractDeviceInitializer {
private static final Logger LOG = LoggerFactory.getLogger(OF13DeviceInitializer.class);
@Override
protected Future<Void> initializeNodeInformation(@Nonnull final DeviceContext deviceContext,
final boolean switchFeaturesMandatory,
@Nullable final MultipartWriterProvider multipartWriterProvider,
@Nullable final ConvertorExecutor convertorExecutor) {
final ConnectionContext connectionContext = Preconditions.checkNotNull(deviceContext.getPrimaryConnectionContext());
final DeviceState deviceState = Preconditions.checkNotNull(deviceContext.getDeviceState());
final DeviceInfo deviceInfo = Preconditions.checkNotNull(deviceContext.getDeviceInfo());
final Capabilities capabilities = connectionContext.getFeatures().getCapabilities();
LOG.debug("Setting capabilities for device {}", deviceInfo.getLOGValue());
DeviceStateUtil.setDeviceStateBasedOnV13Capabilities(deviceState, capabilities);
// First process description reply, write data to DS and write consequent data if successful
return Futures.transform(
requestMultipart(MultipartType.OFPMPDESC, deviceContext),
(AsyncFunction<RpcResult<List<OfHeader>>, Void>) input -> {
translateAndWriteResult(
MultipartType.OFPMPDESC,
input.getResult(),
deviceContext,
multipartWriterProvider,
convertorExecutor);
final List<ListenableFuture<RpcResult<List<OfHeader>>>> futures = new ArrayList<>();
futures.add(requestAndProcessMultipart(MultipartType.OFPMPMETERFEATURES, deviceContext, multipartWriterProvider, convertorExecutor));
futures.add(requestAndProcessMultipart(MultipartType.OFPMPGROUPFEATURES, deviceContext, multipartWriterProvider, convertorExecutor));
futures.add(requestAndProcessMultipart(MultipartType.OFPMPTABLEFEATURES, deviceContext, multipartWriterProvider, convertorExecutor));
futures.add(requestAndProcessMultipart(MultipartType.OFPMPPORTDESC, deviceContext, multipartWriterProvider, convertorExecutor));
return Futures.transform(
(switchFeaturesMandatory ? Futures.allAsList(futures) : Futures.successfulAsList(futures)),
new Function<List<RpcResult<List<OfHeader>>>, Void>() {
@Nullable
@Override
public Void apply(@Nullable final List<RpcResult<List<OfHeader>>> input) {
LOG.info("Static node {} successfully finished collecting", deviceContext.getDeviceInfo().getLOGValue());
return null;
}
});
});
}
/**
* Request multipart of specified type and then run some processing on it
* @param type multipart type
* @param deviceContext device context
* @param multipartWriterProvider multipart writer provider
* @param convertorExecutor convertor executor
* @return list of multipart messages unified to parent interface
*/
private static ListenableFuture<RpcResult<List<OfHeader>>> requestAndProcessMultipart(final MultipartType type,
final DeviceContext deviceContext,
final MultipartWriterProvider multipartWriterProvider,
@Nullable final ConvertorExecutor convertorExecutor) {
final ListenableFuture<RpcResult<List<OfHeader>>> rpcResultListenableFuture =
MultipartType.OFPMPTABLEFEATURES.equals(type) && deviceContext.isSkipTableFeatures()
? RpcResultBuilder.<List<OfHeader>>success().buildFuture()
: requestMultipart(type, deviceContext);
createCallback(type, rpcResultListenableFuture, deviceContext, multipartWriterProvider, convertorExecutor);
return rpcResultListenableFuture;
}
/**
* Inject callback ti future for specified multipart type. This callback will translate and write
* result of multipart messages
* @param type multipart type
* @param future multipart collection future
* @param deviceContext device context
* @param multipartWriterProvider multipart writer provider
* @param convertorExecutor convertor executor
*/
private static void createCallback(final MultipartType type,
final ListenableFuture<RpcResult<List<OfHeader>>> future,
final DeviceContext deviceContext,
@Nullable final MultipartWriterProvider multipartWriterProvider,
@Nullable final ConvertorExecutor convertorExecutor) {
Futures.addCallback(future, new FutureCallback<RpcResult<List<OfHeader>>>() {
@Override
public void onSuccess(final RpcResult<List<OfHeader>> result) {
if (Objects.nonNull(result.getResult())) {
LOG.info("Static node {} info: {} collected", deviceContext.getDeviceInfo().getLOGValue(), type);
translateAndWriteResult(
type,
result.getResult(),
deviceContext,
multipartWriterProvider,
convertorExecutor);
} else {
result.getErrors().forEach(rpcError -> {
LOG.warn("Failed to retrieve static node {} info: {}", type, rpcError.getMessage());
if (LOG.isTraceEnabled() && Objects.nonNull(rpcError.getCause())) {
LOG.trace("Detailed error:", rpcError.getCause());
}
});
// If table features are disabled or returned nothing, at least make empty tables
if (MultipartType.OFPMPTABLEFEATURES.equals(type)) {
DeviceInitializationUtil.makeEmptyTables(
deviceContext,
deviceContext.getDeviceInfo(),
deviceContext.getPrimaryConnectionContext().getFeatures().getTables());
}
}
}
@Override
public void onFailure(@Nonnull final Throwable t) {
LOG.warn("Request of type {} for static info of node {} failed.", type, deviceContext.getDeviceInfo().getLOGValue());
}
});
}
/**
* Translate and write multipart messages from OpenflowJava
* @param type multipart type
* @param result multipart messages
* @param deviceContext device context
* @param multipartWriterProvider multipart writer provider
* @param convertorExecutor convertor executor
*/
private static void translateAndWriteResult(final MultipartType type,
final List<OfHeader> result,
final DeviceContext deviceContext,
@Nullable final MultipartWriterProvider multipartWriterProvider,
@Nullable final ConvertorExecutor convertorExecutor) {
if (Objects.nonNull(result)) {
try {
result.forEach(reply -> {
// First, translate collected data to proper openflowplugin representation
MultipartReplyTranslatorUtil
.translate(
reply,
deviceContext.getDeviceInfo(),
convertorExecutor,
deviceContext.oook())
.ifPresent(translatedReply -> {
// If we collected meter features, check if we have support for meters
// and pass this information to device context
if (MultipartType.OFPMPMETERFEATURES.equals(type) &&
translatedReply instanceof MeterFeatures) {
final MeterFeatures meterFeatures = (MeterFeatures) translatedReply;
if (meterFeatures.getMaxMeter().getValue() > 0) {
deviceContext.getDeviceState().setMeterAvailable(true);
}
}
// Now. try to write translated collected features
Optional.ofNullable(multipartWriterProvider)
.flatMap(provider -> provider.lookup(type))
.ifPresent(writer -> writer.write(translatedReply, false));
});
});
} catch (final Exception e) {
LOG.warn("Failed to write node {} to DS ", deviceContext.getDeviceInfo().getLOGValue(), e);
}
} else {
LOG.warn("Failed to write node {} to DS because we failed to gather device info.",
deviceContext.getDeviceInfo().getLOGValue());
}
}
/**
* Send request to device and unify different possible reply types from OpenflowJava to common parent interface
* @param multipartType multipart type
* @param deviceContext device context
* @return unified replies
*/
private static ListenableFuture<RpcResult<List<OfHeader>>> requestMultipart(final MultipartType multipartType,
final DeviceContext deviceContext) {
if (deviceContext.canUseSingleLayerSerialization()) {
final SingleLayerMultipartCollectorService service =
new SingleLayerMultipartCollectorService(deviceContext, deviceContext);
return Futures.transform(service.handleServiceCall(multipartType), new Function<RpcResult<List<MultipartReply>>, RpcResult<List<OfHeader>>>() {
@Nullable
@Override
public RpcResult<List<OfHeader>> apply(final RpcResult<List<MultipartReply>> input) {
if (Objects.isNull(input.getResult()) && input.isSuccessful()) {
final List<OfHeader> temp = null;
return RpcResultBuilder.success(temp).build();
}
return input.isSuccessful()
? RpcResultBuilder.success(input
.getResult()
.stream()
.map(reply -> (OfHeader) reply)
.collect(Collectors.toList()))
.build()
: RpcResultBuilder.<List<OfHeader>>failed()
.withRpcErrors(input.getErrors())
.build();
}
});
}
final MultiLayerMultipartCollectorService service =
new MultiLayerMultipartCollectorService(deviceContext, deviceContext);
return Futures.transform(service.handleServiceCall(multipartType), new Function<RpcResult<List<org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply>>, RpcResult<List<OfHeader>>>() {
@Nullable
@Override
public RpcResult<List<OfHeader>> apply(final RpcResult<List<org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply>> input) {
if (Objects.isNull(input.getResult()) && input.isSuccessful()) {
final List<OfHeader> temp = null;
return RpcResultBuilder.success(temp).build();
}
return input.isSuccessful()
? RpcResultBuilder.success(input
.getResult()
.stream()
.map(reply -> (OfHeader) reply)
.collect(Collectors.toList()))
.build()
: RpcResultBuilder.<List<OfHeader>>failed()
.withRpcErrors(input.getErrors())
.build();
}
});
}
}