import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceReplyProcessor;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.MessageHandler;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.MultiMsgCollector;
+import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.group.DeviceGroupRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.meter.DeviceMeterRegistry;
* @param upperBound max amount of outstanding packetIns
*/
void updatePacketInRateLimit(long upperBound);
+
+ /**
+ * @return registry point for item life cycle sources of device
+ */
+ ItemLifeCycleRegistry getItemLifeCycleSourceRegistry();
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.api.openflow.registry;
+
+import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleSource;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ * Registration point for any kind of lifecycle sources per device.
+ */
+public interface ItemLifeCycleRegistry {
+
+ /**
+ * register given life cycle source to known sources of device
+ *
+ * @param lifeCycleSource life cycle changes provider
+ * @return closeable registration
+ */
+ Registration registerLifeCycleSource(ItemLifeCycleSource lifeCycleSource);
+
+ /**
+ * close all existing registrations
+ */
+ void clear();
+
+ /**
+ * @return registered sources
+ */
+ Iterable<ItemLifeCycleSource> getLifeCycleSources();
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.api.openflow.rpc;
+
+import javax.annotation.Nullable;
+import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
+
+/**
+ * A source of switch item lifecycle enables for injecting of
+ * a {@link org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener}
+ * in order to act upon item lifecycle changes.
+ */
+public interface ItemLifeCycleSource {
+
+ /**
+ * @param itemLifecycleListener acts upon lifecycle changes
+ */
+ void setItemLifecycleListener(@Nullable ItemLifecycleListener itemLifecycleListener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.api.openflow.rpc.listener;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+
+/**
+ * Flow/group/meter lifecycle listener - aimed on rpc result approved by barrier message.
+ */
+public interface ItemLifecycleListener {
+
+ /**
+ * react upon item added event
+ *
+ * @param itemPath keyed path in DS
+ * @param itemBody item body
+ */
+
+ <I extends Identifiable<K> & DataObject, K extends Identifier<I>> void onAdded(KeyedInstanceIdentifier<I, K> itemPath, I itemBody);
+
+ /**
+ * react upon item removed event
+ *
+ * @param itemPath keyed path in DS
+ */
+ <I extends Identifiable<K> & DataObject, K extends Identifier<I>> void onRemoved(KeyedInstanceIdentifier<I, K> itemPath);
+}
import com.google.common.util.concurrent.ListenableFuture;
import io.netty.util.Timeout;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
/**
* Created by Martin Bobak <mbobak@cisco.com> on 27.2.2015.
* @return handle to currently scheduled statistics polling
*/
Optional<Timeout> getPollTimeout();
+
+ /**
+ * @return dedicated item life cycle change listener (per device)
+ */
+ ItemLifecycleListener getItemLifeCycleListener();
}
import org.opendaylight.openflowplugin.api.openflow.device.handlers.MultiMsgCollector;
import org.opendaylight.openflowplugin.api.openflow.md.core.SwitchConnectionDistinguisher;
import org.opendaylight.openflowplugin.api.openflow.md.core.TranslatorKey;
+import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.group.DeviceGroupRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.meter.DeviceMeterRegistry;
private final MessageTranslator<PacketInMessage, PacketReceived> packetInTranslator;
private final TranslatorLibrary translatorLibrary;
private Map<Long, NodeConnectorRef> nodeConnectorCache;
+ private ItemLifeCycleRegistry itemLifeCycleSourceRegistry;
@VisibleForTesting
packetInTranslator = translatorLibrary.lookupTranslator(
new TranslatorKey(deviceState.getVersion(), PacketIn.class.getName()));
nodeConnectorCache = new ConcurrentHashMap<>();
+
+ itemLifeCycleSourceRegistry = new ItemLifeCycleRegistryImpl();
}
/**
deviceFlowRegistry.close();
deviceMeterRegistry.close();
+ itemLifeCycleSourceRegistry.clear();
+
for (final DeviceContextClosedHandler deviceContextClosedHandler : closeHandlers) {
deviceContextClosedHandler.onDeviceContextClosed(this);
public void updatePacketInRateLimit(long upperBound) {
packetInLimiter.changeWaterMarks((int) (LOW_WATERMARK_FACTOR * upperBound), (int) (HIGH_WATERMARK_FACTOR * upperBound));
}
+
+ @Override
+ public ItemLifeCycleRegistry getItemLifeCycleSourceRegistry() {
+ return itemLifeCycleSourceRegistry;
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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;
+
+import java.util.Collections;
+import java.util.Set;
+import org.apache.mina.util.ConcurrentHashSet;
+import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegistry;
+import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleSource;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ * default implementation
+ */
+public class ItemLifeCycleRegistryImpl implements ItemLifeCycleRegistry {
+
+ private final Set<ItemLifeCycleSource> registry;
+
+ public ItemLifeCycleRegistryImpl() {
+ registry = new ConcurrentHashSet<>();
+ }
+
+
+ @Override
+
+ public Registration registerLifeCycleSource(final ItemLifeCycleSource lifeCycleSource) {
+ registry.add(lifeCycleSource);
+ return new Registration() {
+ @Override
+ public void close() throws Exception {
+ registry.remove(lifeCycleSource);
+ }
+ };
+ }
+
+ @Override
+ public void clear() {
+ registry.clear();
+ }
+
+ @Override
+ public Iterable<ItemLifeCycleSource> getLifeCycleSources() {
+ return Collections.unmodifiableCollection(registry);
+ }
+}
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
+import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleSource;
import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
routedRpcReg.registerPath(NodeContext.class, deviceContext.getDeviceState().getNodeInstanceIdentifier());
rpcRegistrations.add(routedRpcReg);
LOG.debug("Registration of service {} for device {}.", serviceClass, deviceContext.getDeviceState().getNodeInstanceIdentifier());
+
+ if (serviceInstance instanceof ItemLifeCycleSource) {
+ // TODO: collect registration for selective unregistering in case of tearing down only one rpc
+ deviceContext.getItemLifeCycleSourceRegistry().registerLifeCycleSource((ItemLifeCycleSource) serviceInstance);
+ }
}
/**
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.rpc.listener;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+
+/**
+ * General implementation of {@link ItemLifecycleListener} - keeping of DS/operational reflection up-to-date
+ */
+public class ItemLifecycleListenerImpl implements ItemLifecycleListener {
+
+ private final DeviceContext deviceContext;
+
+ public ItemLifecycleListenerImpl(DeviceContext deviceContext) {
+ this.deviceContext = deviceContext;
+ }
+
+ @Override
+ public <I extends Identifiable<K> & DataObject, K extends Identifier<I>> void onAdded(KeyedInstanceIdentifier<I, K> itemPath, I itemBody) {
+ deviceContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, itemPath, itemBody);
+ deviceContext.submitTransaction();
+ }
+
+ @Override
+ public <I extends Identifiable<K> & DataObject, K extends Identifier<I>> void onRemoved(KeyedInstanceIdentifier<I, K> itemPath) {
+ deviceContext.addDeleteToTxChain(LogicalDatastoreType.OPERATIONAL, itemPath);
+ deviceContext.submitTransaction();
+ }
+}
*/
package org.opendaylight.openflowplugin.impl.services;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
-
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Future;
+import javax.annotation.Nullable;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowDescriptor;
import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowRegistryKey;
+import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleSource;
+import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
import org.opendaylight.openflowplugin.impl.registry.flow.FlowDescriptorFactory;
import org.opendaylight.openflowplugin.impl.registry.flow.FlowRegistryKeyFactory;
import org.opendaylight.openflowplugin.impl.util.FlowUtil;
import org.opendaylight.openflowplugin.openflow.md.util.FlowCreatorUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowModInputBuilder;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class SalFlowServiceImpl implements SalFlowService {
+public class SalFlowServiceImpl implements SalFlowService, ItemLifeCycleSource {
private static final Logger LOG = LoggerFactory.getLogger(SalFlowServiceImpl.class);
private final FlowService<UpdateFlowOutput> flowUpdate;
private final FlowService<AddFlowOutput> flowAdd;
private final FlowService<RemoveFlowOutput> flowRemove;
+ private final DeviceContext deviceContext;
+ private ItemLifecycleListener itemLifecycleListener;
public SalFlowServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
+ this.deviceContext = deviceContext;
flowRemove = new FlowService(requestContextStack, deviceContext, RemoveFlowOutput.class);
flowAdd = new FlowService<>(requestContextStack, deviceContext, AddFlowOutput.class);
flowUpdate = new FlowService<>(requestContextStack, deviceContext, UpdateFlowOutput.class);
}
+ @Override
+ public void setItemLifecycleListener(@Nullable ItemLifecycleListener itemLifecycleListener) {
+ this.itemLifecycleListener = itemLifecycleListener;
+ }
+
@Override
public Future<RpcResult<AddFlowOutput>> addFlow(final AddFlowInput input) {
final FlowId flowId;
flowId = FlowUtil.createAlienFlowId(input.getTableId());
}
- final DeviceContext deviceContext = flowAdd.getDeviceContext();
final FlowRegistryKey flowRegistryKey = FlowRegistryKeyFactory.create(input);
final FlowDescriptor flowDescriptor = FlowDescriptorFactory.create(input.getTableId(), flowId);
deviceContext.getDeviceFlowRegistry().store(flowRegistryKey, flowDescriptor);
public void onSuccess(final RpcResult<AddFlowOutput> rpcResult) {
if (rpcResult.isSuccessful()) {
LOG.debug("flow add finished without error, id={}", flowId.getValue());
+ if (itemLifecycleListener != null) {
+ KeyedInstanceIdentifier<Flow, FlowKey> flowPath = createFlowPath(flowDescriptor,
+ deviceContext.getDeviceState().getNodeInstanceIdentifier());
+ final FlowBuilder flowBuilder = new FlowBuilder(input).setId(flowDescriptor.getFlowId());
+ itemLifecycleListener.onAdded(flowPath, flowBuilder.build());
+ }
} else {
LOG.debug("flow add failed with error, id={}", flowId.getValue());
}
public void onSuccess(final RpcResult<RemoveFlowOutput> result) {
if (result.isSuccessful()) {
FlowRegistryKey flowRegistryKey = FlowRegistryKeyFactory.create(input);
- flowRemove.getDeviceContext().getDeviceFlowRegistry().markToBeremoved(flowRegistryKey);
+ deviceContext.getDeviceFlowRegistry().markToBeremoved(flowRegistryKey);
+ if (itemLifecycleListener != null) {
+ final FlowDescriptor flowDescriptor = deviceContext.getDeviceFlowRegistry().retrieveIdForFlow(flowRegistryKey);
+ if (flowDescriptor != null) {
+ KeyedInstanceIdentifier<Flow, FlowKey> flowPath = createFlowPath(flowDescriptor,
+ deviceContext.getDeviceState().getNodeInstanceIdentifier());
+ itemLifecycleListener.onRemoved(flowPath);
+ }
+ }
} else {
if (LOG.isTraceEnabled()) {
StringBuilder errors = new StringBuilder();
FlowRegistryKey updatedflowRegistryKey = FlowRegistryKeyFactory.create(updated);
final FlowRef flowRef = input.getFlowRef();
- final DeviceFlowRegistry deviceFlowRegistry = flowUpdate.getDeviceContext().getDeviceFlowRegistry();
+ final DeviceFlowRegistry deviceFlowRegistry = deviceContext.getDeviceFlowRegistry();
deviceFlowRegistry.markToBeremoved(flowRegistryKey);
+
+ if (itemLifecycleListener != null) {
+ final FlowDescriptor flowDescriptor = deviceContext.getDeviceFlowRegistry().retrieveIdForFlow(flowRegistryKey);
+ if (flowDescriptor != null) {
+ KeyedInstanceIdentifier<Flow, FlowKey> flowPath = createFlowPath(flowDescriptor,
+ deviceContext.getDeviceState().getNodeInstanceIdentifier());
+ itemLifecycleListener.onRemoved(flowPath);
+ }
+ }
//if provided, store flow id to flow registry
if (flowRef != null) {
final FlowId flowId = flowRef.getValue().firstKeyOf(Flow.class, FlowKey.class).getId();
final FlowDescriptor flowDescriptor = FlowDescriptorFactory.create(updated.getTableId(), flowId);
deviceFlowRegistry.store(updatedflowRegistryKey, flowDescriptor);
+
+ if (itemLifecycleListener != null) {
+ KeyedInstanceIdentifier<Flow, FlowKey> flowPath = createFlowPath(flowDescriptor,
+ deviceContext.getDeviceState().getNodeInstanceIdentifier());
+ final FlowBuilder flowBuilder = new FlowBuilder(input.getUpdatedFlow()).setId(flowDescriptor.getFlowId());
+ itemLifecycleListener.onAdded(flowPath, flowBuilder.build());
+ }
}
}
});
return future;
}
+
+ @VisibleForTesting
+ static KeyedInstanceIdentifier<Flow, FlowKey> createFlowPath(FlowDescriptor flowDescriptor,
+ KeyedInstanceIdentifier<Node, NodeKey> nodePath) {
+ return nodePath.augmentation(FlowCapableNode.class)
+ .child(Table.class, flowDescriptor.getTableKey())
+ .child(Flow.class, new FlowKey(flowDescriptor.getFlowId()));
+ }
}
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
+import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
import org.opendaylight.openflowplugin.impl.rpc.AbstractRequestContext;
+import org.opendaylight.openflowplugin.impl.rpc.listener.ItemLifecycleListenerImpl;
import org.opendaylight.openflowplugin.impl.services.RequestContextUtil;
import org.opendaylight.openflowplugin.impl.statistics.services.dedicated.StatisticsGatheringOnTheFlyService;
import org.opendaylight.openflowplugin.impl.statistics.services.dedicated.StatisticsGatheringService;
private static final Logger LOG = LoggerFactory.getLogger(StatisticsContextImpl.class);
private static final String CONNECTION_CLOSED = "Connection closed.";
+
+ private final ItemLifecycleListener itemLifeCycleListener;
private final Collection<RequestContext<?>> requestContexts = new HashSet<>();
private final DeviceContext deviceContext;
private final DeviceState devState;
statListForCollecting.add(MultipartType.OFPMPQUEUE);
}
collectingStatType = ImmutableList.<MultipartType>copyOf(statListForCollecting);
+ itemLifeCycleListener = new ItemLifecycleListenerImpl(deviceContext);
}
@Override
statisticsGatheringOnTheFlyService) {
this.statisticsGatheringOnTheFlyService = statisticsGatheringOnTheFlyService;
}
+
+ @Override
+ public ItemLifecycleListener getItemLifeCycleListener() {
+ return itemLifeCycleListener;
+ }
}
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
+import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleSource;
import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.sm.control.rev150812.ChangeStatisticsWorkModeInput;
switch (targetWorkMode) {
case COLLECTALL:
scheduleNextPolling(deviceContext, statisticsContext, new TimeCounter());
+ for (ItemLifeCycleSource lifeCycleSource : deviceContext.getItemLifeCycleSourceRegistry().getLifeCycleSources()) {
+ lifeCycleSource.setItemLifecycleListener(null);
+ }
break;
case FULLYDISABLED:
final Optional<Timeout> pollTimeout = statisticsContext.getPollTimeout();
if (pollTimeout.isPresent()) {
pollTimeout.get().cancel();
}
+ for (ItemLifeCycleSource lifeCycleSource : deviceContext.getItemLifeCycleSourceRegistry().getLifeCycleSources()) {
+ lifeCycleSource.setItemLifecycleListener(statisticsContext.getItemLifeCycleListener());
+ }
break;
default:
LOG.warn("statistics work mode not supported: {}", targetWorkMode);
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
+import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegistry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
private BindingAwareBroker.RoutedRpcRegistration<RpcService> routedRpcRegistration;
@Mock
private DeviceState deviceState;
+ @Mock
+ private ItemLifeCycleRegistry itemLifeCycleRegistry;
+
private KeyedInstanceIdentifier<Node, NodeKey> nodePath;
@Before
Mockito.when(connectionContext.getFeatures()).thenReturn(features);
Mockito.when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
Mockito.when(deviceContext.getDeviceState()).thenReturn(deviceState);
+ Mockito.when(deviceContext.getItemLifeCycleSourceRegistry()).thenReturn(itemLifeCycleRegistry);
Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodePath);
}
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Matchers;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
import org.opendaylight.openflowplugin.api.OFConstants;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
import org.opendaylight.openflowplugin.api.openflow.device.Xid;
+import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
+import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowDescriptor;
+import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowRegistryKey;
+import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
-import org.opendaylight.openflowplugin.impl.registry.flow.DeviceFlowRegistryImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
private static final BigInteger DUMMY_DATAPATH_ID = new BigInteger("444");
private static final Short DUMMY_VERSION = OFConstants.OFP_VERSION_1_3;
+ private static final String DUMMY_NODE_ID = "dummyNodeID";
+ private static final String DUMMY_FLOW_ID = "dummyFlowID";
+ private static final Short DUMMY_TABLE_ID = (short) 0;
+
+ private static final KeyedInstanceIdentifier<Node, NodeKey> NODE_II
+ = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(DUMMY_NODE_ID)));
+
+ private static final KeyedInstanceIdentifier<Table, TableKey> TABLE_II
+ = NODE_II.augmentation(FlowCapableNode.class).child(Table.class, new TableKey(DUMMY_TABLE_ID));
@Mock
private RequestContextStack mockedRequestContextStack;
private Match match;
private SalFlowServiceImpl salFlowService;
+ @Mock
+ DeviceState mockedDeviceState;
+ @Mock
+ private DeviceFlowRegistry deviceFlowRegistry;
@Before
public void initialization() {
when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedPrimConnectionContext);
when(mockedDeviceContext.getMessageSpy()).thenReturn(mockedMessagSpy);
- when(mockedDeviceContext.getDeviceFlowRegistry()).thenReturn(new DeviceFlowRegistryImpl());
+ when(mockedDeviceContext.getDeviceFlowRegistry()).thenReturn(deviceFlowRegistry);
when(mockedRequestContextStack.createRequestContext()).thenReturn(requestContext);
when(requestContext.getXid()).thenReturn(new Xid(84L));
when(requestContext.getFuture()).thenReturn(RpcResultBuilder.success().buildFuture());
salFlowService = new SalFlowServiceImpl(mockedRequestContextStack, mockedDeviceContext);
+
+
+ when(mockedDeviceState.getNodeInstanceIdentifier()).thenReturn(NODE_II);
+ when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
}
@Test
public void testAddFlow() throws Exception {
- final AddFlowInput mockedAddFlowInput = createFlowMock(AddFlowInput.class);
+ addFlow(null);
+ }
+
+ @Test
+ public void testAddFlowWithItemLifecycle() throws Exception {
+ addFlow(mock(ItemLifecycleListener.class));
+ }
+
+ private void addFlow(final ItemLifecycleListener itemLifecycleListener) throws ExecutionException, InterruptedException {
+ AddFlowInput mockedAddFlowInput = createFlowMock(AddFlowInput.class);
+ salFlowService.setItemLifecycleListener(itemLifecycleListener);
verifyOutput(salFlowService.addFlow(mockedAddFlowInput));
+ if (itemLifecycleListener != null) {
+ Mockito.verify(itemLifecycleListener).onAdded(Matchers.<KeyedInstanceIdentifier<Flow, FlowKey>>any(), Matchers.<Flow>any());
+ }
}
@Test
public void testRemoveFlow() throws Exception {
- final RemoveFlowInput mockedRemoveFlowInput = createFlowMock(RemoveFlowInput.class);
+ removeFlow(null);
+ }
+
+ @Test
+ public void testRemoveFlowWithItemLifecycle() throws Exception {
+ removeFlow(mock(ItemLifecycleListener.class));
+ }
+
+ private void removeFlow(final ItemLifecycleListener itemLifecycleListener) throws Exception {
+ RemoveFlowInput mockedRemoveFlowInput = createFlowMock(RemoveFlowInput.class);
+
+ if (itemLifecycleListener != null) {
+ salFlowService.setItemLifecycleListener(itemLifecycleListener);
+ mockingFlowRegistryLookup();
+
+ }
verifyOutput(salFlowService.removeFlow(mockedRemoveFlowInput));
+ if (itemLifecycleListener != null) {
+ Mockito.verify(itemLifecycleListener).onRemoved(Matchers.<KeyedInstanceIdentifier<Flow, FlowKey>>any());
+ }
+
}
@Test
public void testUpdateFlow() throws Exception {
- final UpdateFlowInput mockedUpdateFlowInput = mock(UpdateFlowInput.class);
+ updateFlow(null);
+ }
+
+ @Test
+ public void testUpdateFlowWithItemLifecycle() throws Exception {
+ updateFlow(mock(ItemLifecycleListener.class));
+ }
+
+ private void updateFlow(final ItemLifecycleListener itemLifecycleListener) throws Exception {
+ UpdateFlowInput mockedUpdateFlowInput = mock(UpdateFlowInput.class);
- final UpdatedFlow mockedUpdateFlow = createFlowMock(UpdatedFlow.class);
+ UpdatedFlow mockedUpdateFlow = createFlowMock(UpdatedFlow.class);
when(mockedUpdateFlowInput.getUpdatedFlow()).thenReturn(mockedUpdateFlow);
- final OriginalFlow mockedOriginalFlow = createFlowMock(OriginalFlow.class);
+ FlowRef mockedFlowRef = mock(FlowRef.class);
+ Mockito.doReturn(TABLE_II.child(Flow.class, new FlowKey(new FlowId(DUMMY_FLOW_ID)))).when(mockedFlowRef).getValue();
+ when(mockedUpdateFlowInput.getFlowRef()).thenReturn(mockedFlowRef);
+
+ OriginalFlow mockedOriginalFlow = createFlowMock(OriginalFlow.class);
when(mockedUpdateFlowInput.getOriginalFlow()).thenReturn(mockedOriginalFlow);
+ if (itemLifecycleListener != null) {
+ salFlowService.setItemLifecycleListener(itemLifecycleListener);
+ mockingFlowRegistryLookup();
+ }
+
verifyOutput(salFlowService.updateFlow(mockedUpdateFlowInput));
+
+ if (itemLifecycleListener != null) {
+ Mockito.verify(itemLifecycleListener).onAdded(Matchers.<KeyedInstanceIdentifier<Flow, FlowKey>>any(), Matchers.<Flow>any());
+ Mockito.verify(itemLifecycleListener).onRemoved(Matchers.<KeyedInstanceIdentifier<Flow, FlowKey>>any());
+ }
+
+ }
+
+ private void mockingFlowRegistryLookup() {
+ FlowDescriptor mockedFlowDescriptor = mock(FlowDescriptor.class);
+ when(mockedFlowDescriptor.getFlowId()).thenReturn(new FlowId(DUMMY_FLOW_ID));
+ when(mockedFlowDescriptor.getTableKey()).thenReturn(new TableKey(DUMMY_TABLE_ID));
+
+ when(deviceFlowRegistry.retrieveIdForFlow(Matchers.any(FlowRegistryKey.class))).thenReturn(mockedFlowDescriptor);
}
private <T extends DataObject> void verifyOutput(Future<RpcResult<T>> rpcResultFuture) throws ExecutionException, InterruptedException {
assertTrue(addFlowOutputRpcResult.isSuccessful());
}
- private <T extends Flow> T createFlowMock(Class<T> flowClazz) {
+ private <T extends org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow> T createFlowMock(Class<T> flowClazz) {
T mockedFlow = mock(flowClazz);
when(mockedFlow.getMatch()).thenReturn(match);
return mockedFlow;
timeCounter = new TimeCounter();
}
+ /**
+ * tm = time mark
+ * - tm1 at time 2 ms
+ * - tm2 at time 4 ms
+ * - tm3 at time 9 ms
+ *
+ * awaited average time:
+ * - tm1 = 2/1 = 2 ms
+ * - tm2 = 4/2 = 2 ms
+ * - tm3 = 9/3 = 3 ms
+ *
+ * But this times are only theoretical if whole test is executed without latency and atomically. Therefore awaited
+ * average times can't be compared to exact values of awaited average time (therefore == was replaced with >=)
+ * @throws Exception
+ */
@Test
public void testGetAverageTimeBetweenMarks() throws Exception {
Assert.assertEquals(0, timeCounter.getAverageTimeBetweenMarks());
zzz(2L);
timeCounter.addTimeMark();
- Assert.assertEquals(2, timeCounter.getAverageTimeBetweenMarks());
+ Assert.assertTrue(timeCounter.getAverageTimeBetweenMarks() >= 2);
zzz(2L);
timeCounter.addTimeMark();
- Assert.assertEquals(2, timeCounter.getAverageTimeBetweenMarks());
+ Assert.assertTrue(timeCounter.getAverageTimeBetweenMarks() >= 2);
zzz(5L);
timeCounter.addTimeMark();
- Assert.assertEquals(3, timeCounter.getAverageTimeBetweenMarks());
+ Assert.assertTrue(timeCounter.getAverageTimeBetweenMarks() >= 3);
}
private void zzz(long length) {