<artifactId>openflowplugin</artifactId>
<version>${openflowplugin.distribution.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>openflowplugin-api</artifactId>
+ <version>${openflowplugin.distribution.version}</version>
+ </dependency>
<!-- openflowjava -->
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-flow-service</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-statistics</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-flow-management</artifactId>
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
new Function<RpcResult<TransactionStatus>,RpcResult<Void>>() {
@Override
public RpcResult<Void> apply(RpcResult<TransactionStatus> input) {
- return Rpcs.<Void>getRpcResult(input.isSuccessful(),
- input.getErrors());
+ if (input.isSuccessful())
+ return RpcResultBuilder.<Void>success().build();
+ else
+ return RpcResultBuilder.<Void>failed()
+ .withRpcErrors(input.getErrors()).build();
}
};
}
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.DestinationMapper;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable.FlowTableCtx;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils;
this, policyResolver, switchManager,
endpointManager, executor);
flowPipeline = ImmutableList.of(new PortSecurity(ctx),
- new SourceMapper(ctx));
-
+ new SourceMapper(ctx),
+ new DestinationMapper(ctx));
+
policyScope = policyResolver.registerListener(this);
if (switchManager != null)
switchManager.registerListener(this);
dirty.get().addNode(sw);
scheduleUpdate();
}
+
+ @Override
+ public void switchUpdated(NodeId sw) {
+ dirty.get().addNode(sw);
+ scheduleUpdate();
+ }
// ****************
// EndpointListener
* @param sw the ID for the switch
*/
public void switchRemoved(NodeId sw);
+
+ /**
+ * Indicated that the switch configuration, tunnel port, or external ports
+ * have changed
+ * @param sw
+ */
+ public void switchUpdated(NodeId sw);
}
package org.opendaylight.groupbasedpolicy.renderer.ofoverlay;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledExecutorService;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig.EncapsulationFormat;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
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.node.NodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
* correctly
* @author readams
*/
-public class SwitchManager implements AutoCloseable, DataChangeListener {
- private static final Logger LOG =
+public class SwitchManager implements AutoCloseable {
+ private static final Logger LOG =
LoggerFactory.getLogger(SwitchManager.class);
private final DataBroker dataProvider;
InstanceIdentifier.builder(Nodes.class)
.child(Node.class).build();
private ListenerRegistration<DataChangeListener> nodesReg;
+ private ListenerRegistration<DataChangeListener> nodesConfigReg;
- private ConcurrentHashMap<NodeId, SwitchState> switches =
+ protected ConcurrentHashMap<NodeId, SwitchState> switches =
new ConcurrentHashMap<>();
- private List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
+ protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
public SwitchManager(DataBroker dataProvider,
ScheduledExecutorService executor) {
super();
this.dataProvider = dataProvider;
- nodesReg = dataProvider
- .registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
- nodeIid, this,
- DataChangeScope.ONE);
+ if (dataProvider != null) {
+ nodesReg = dataProvider
+ .registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+ nodeIid, new NodesListener(),
+ DataChangeScope.SUBTREE);
+ nodesConfigReg = dataProvider
+ .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+ nodeIid, new NodesConfigListener(),
+ DataChangeScope.SUBTREE);
+ }
readSwitches();
LOG.debug("Initialized OFOverlay switch manager");
}
new Function<SwitchState, NodeId>() {
@Override
public NodeId apply(SwitchState input) {
- return input.switchNode.getId();
+ return input.nodeId;
}
});
}
if (state == null) return false;
return SwitchStatus.READY.equals(state.status);
}
+
+ public Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
+ SwitchState state = switches.get(nodeId);
+ if (state == null) return Collections.emptySet();
+ return state.externalPorts;
+ }
+
+ public NodeConnectorId getTunnelPort(NodeId nodeId) {
+ SwitchState state = switches.get(nodeId);
+ if (state == null) return null;
+ return state.tunnelPort;
+ }
+
+ public IpAddress getTunnelIP(NodeId nodeId) {
+ SwitchState state = switches.get(nodeId);
+ if (state == null || state.nodeConfig == null) return null;
+ return state.nodeConfig.getTunnelIp();
+ }
/**
* Add a {@link SwitchListener} to get notifications of switch events
@Override
public void close() throws Exception {
nodesReg.close();
+ nodesConfigReg.close();
}
// ******************
// DataChangeListener
// ******************
- @Override
- public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>,
- DataObject> change) {
- for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
- LOG.info("{} removed", iid);
-
- DataObject old = change.getOriginalData().get(iid);
- if (old != null && old instanceof Node) {
- removeSwitch(((Node)old).getId());
+ private class NodesListener implements DataChangeListener {
+
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>,
+ DataObject> change) {
+ for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
+ DataObject old = change.getOriginalData().get(iid);
+ if (old != null && old instanceof Node) {
+ removeSwitch(((Node)old).getId());
+ }
}
- }
- for (DataObject dao : change.getCreatedData().values()) {
- updateSwitch(dao);
+ for (DataObject dao : change.getCreatedData().values()) {
+ updateSwitch(dao);
+ }
+ for (DataObject dao : change.getUpdatedData().values()) {
+ updateSwitch(dao);
+ }
}
- for (DataObject dao : change.getUpdatedData().values()) {
- updateSwitch(dao);
+ }
+
+ private class NodesConfigListener implements DataChangeListener {
+
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>,
+ DataObject> change) {
+ readSwitches();
}
}
// Implementation
// **************
+ private SwitchState getSwitchState(NodeId id) {
+ SwitchState state = switches.get(id);
+ if (state == null) {
+ state = new SwitchState(id);
+ SwitchState old =
+ switches.putIfAbsent(id, state);
+ if (old != null)
+ state = null;
+ }
+ return state;
+ }
+
private void updateSwitch(DataObject dao) {
if (!(dao instanceof Node)) return;
// Switches are registered as Nodes in the inventory; OpenFlow switches
LOG.debug("{} update", node.getId());
- SwitchState state = switches.get(node.getId());
- if (state == null) {
- state = new SwitchState(node);
- SwitchState old =
- switches.putIfAbsent(node.getId(), state);
- if (old == null) {
- switchConnected(node.getId());
- }
+ SwitchState state = getSwitchState(node.getId());
+
+ state.setNode(node);
+
+ if (SwitchStatus.DISCONNECTED.equals(state.status))
+ switchConnected(node.getId());
+ else if (SwitchStatus.READY.equals(state.status))
+ notifySwitchUpdated(node.getId());
+ }
+
+ private void updateSwitchConfig(NodeId nodeId, OfOverlayNodeConfig config) {
+ SwitchState state = getSwitchState(nodeId);
+ state.setConfig(config);
+ notifySwitchUpdated(nodeId);
+ }
+
+ private void notifySwitchUpdated(NodeId nodeId) {
+ for (SwitchListener listener : listeners) {
+ listener.switchUpdated(nodeId);
}
}
}
};
+ private final FutureCallback<Optional<Nodes>> readSwitchConfCallback =
+ new FutureCallback<Optional<Nodes>>() {
+ @Override
+ public void onSuccess(Optional<Nodes> result) {
+ if (result.isPresent()) {
+ Nodes nodes = (Nodes)result.get();
+ for (Node node : nodes.getNode()) {
+ OfOverlayNodeConfig config =
+ node.getAugmentation(OfOverlayNodeConfig.class);
+ if (config != null)
+ updateSwitchConfig(node.getId(), config);
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ LOG.error("Count not read switch information", t);
+ }
+ };
+
/**
* Read the set of switches from the ODL inventory and update our internal
* map.
* notifications
*/
private void readSwitches() {
- ListenableFuture<Optional<Nodes>> future =
- dataProvider.newReadOnlyTransaction()
- .read(LogicalDatastoreType.OPERATIONAL,nodesIid);
- Futures.addCallback(future, readSwitchesCallback);
+ if (dataProvider != null) {
+ ListenableFuture<Optional<Nodes>> future =
+ dataProvider.newReadOnlyTransaction()
+ .read(LogicalDatastoreType.CONFIGURATION, nodesIid);
+ Futures.addCallback(future, readSwitchConfCallback);
+
+ future = dataProvider.newReadOnlyTransaction()
+ .read(LogicalDatastoreType.OPERATIONAL, nodesIid);
+ Futures.addCallback(future, readSwitchesCallback);
+ }
}
/**
LOG.info("Switch {} removed", nodeId);
}
- private enum SwitchStatus {
+ protected enum SwitchStatus {
+ /**
+ * The switch is not currently connected
+ */
+ DISCONNECTED,
/**
* The switch is connected but not yet configured
*/
/**
* Internal representation of the state of a connected switch
*/
- private static class SwitchState {
+ protected static class SwitchState {
+ NodeId nodeId;
+
Node switchNode;
- SwitchStatus status = SwitchStatus.PREPARING;
- public SwitchState(Node switchNode) {
+ OfOverlayNodeConfig nodeConfig;
+
+ NodeConnectorId tunnelPort;
+ Set<NodeConnectorId> externalPorts = Collections.emptySet();
+
+ SwitchStatus status = SwitchStatus.DISCONNECTED;
+
+ public SwitchState(NodeId switchNode) {
super();
- this.switchNode = switchNode;
+ nodeId = switchNode;
}
+ /**
+ * Constructor used for tests
+ */
+ public SwitchState(NodeId node,
+ NodeConnectorId tunnelPort,
+ Set<NodeConnectorId> externalPorts) {
+ this.nodeId = node;
+ this.tunnelPort = tunnelPort;
+ this.externalPorts = externalPorts;
+ }
+
+ private void update() {
+ if (switchNode == null) return;
+ FlowCapableNode fcn =
+ switchNode.getAugmentation(FlowCapableNode.class);
+ if (fcn == null) return;
+
+ List<NodeConnector> ports = switchNode.getNodeConnector();
+ HashSet<NodeConnectorId> externalPorts = new HashSet<>();
+ for (NodeConnector nc : ports) {
+ FlowCapableNodeConnector fcnc =
+ nc.getAugmentation(FlowCapableNodeConnector.class);
+ if (fcnc == null || fcnc.getName() == null) continue;
+
+ if (fcnc.getName().matches(".*_(vxlan|tun)\\d+")) {
+ tunnelPort = nc.getId();
+ }
+ if (nodeConfig != null) {
+ for (String pattern : nodeConfig.getExternalInterfaces()) {
+ if (fcnc.getName().matches(pattern))
+ externalPorts.add(nc.getId());
+ }
+ }
+ }
+ this.externalPorts = Collections.unmodifiableSet(externalPorts);
+ }
+
+ public void setNode(Node switchNode) {
+ this.switchNode = switchNode;
+ update();
+ }
+
+ public void setConfig(OfOverlayNodeConfig config) {
+ nodeConfig = config;
+ update();
+ }
}
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.groupbasedpolicy.renderer.ofoverlay.flow;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
+import org.opendaylight.groupbasedpolicy.resolver.EgKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+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.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Manage the table that maps the destination address to the next hop
+ * for the path as well as applies any relevant routing transformations.
+ * @author readams
+ */
+public class DestinationMapper extends FlowTable {
+ public static final short TABLE_ID = 2;
+ /**
+ * This is the MAC address of the magical router in the sky
+ */
+ public static final String ROUTER_MAC = "88:f0:31:b5:12:b5";
+
+ public DestinationMapper(FlowTableCtx ctx) {
+ super(ctx);
+ }
+
+ @Override
+ public short getTableId() {
+ return TABLE_ID;
+ }
+
+ @Override
+ public void sync(ReadWriteTransaction t,
+ InstanceIdentifier<Table> tiid,
+ Map<String, FlowCtx> flowMap,
+ NodeId nodeId, Dirty dirty)
+ throws Exception {
+ HashSet<EgKey> visitedEgs = new HashSet<>();
+ for (Endpoint e : ctx.endpointManager.getEndpointsForNode(nodeId)) {
+ if (e.getTenant() == null || e.getEndpointGroup() == null)
+ continue;
+ EgKey key = new EgKey(e.getTenant(), e.getEndpointGroup());
+ syncEPG(t, tiid, flowMap, nodeId, key, visitedEgs);
+
+ Set<EgKey> peers = ctx.policyResolver
+ .getProvidersForConsumer(e.getTenant(),
+ e.getEndpointGroup());
+ syncEgKeys(t, tiid, flowMap, nodeId, peers, visitedEgs);
+ peers = ctx.policyResolver
+ .getConsumersForProvider(e.getTenant(),
+ e.getEndpointGroup());
+ syncEgKeys(t, tiid, flowMap, nodeId, peers, visitedEgs);
+ }
+ }
+
+ private void syncEgKeys(ReadWriteTransaction t,
+ InstanceIdentifier<Table> tiid,
+ Map<String, FlowCtx> flowMap,
+ NodeId nodeId,
+ Set<EgKey> peers,
+ HashSet<EgKey> visitedEgs) throws Exception {
+ for (EgKey key : peers) {
+ syncEPG(t, tiid, flowMap, nodeId, key, visitedEgs);
+ }
+ }
+
+ // set up next-hop destinations for all the endpoints in the endpoint
+ // group on the node
+ private void syncEPG(ReadWriteTransaction t,
+ InstanceIdentifier<Table> tiid,
+ Map<String, FlowCtx> flowMap,
+ NodeId nodeId,
+ EgKey key,
+ HashSet<EgKey> visitedEgs) throws Exception {
+ if (visitedEgs.contains(key)) return;
+ visitedEgs.add(key);
+
+ Collection<Endpoint> egEps = ctx.endpointManager
+ .getEndpointsForGroup(key.getTenantId(), key.getEgId());
+ for (Endpoint e : egEps) {
+ if (e.getTenant() == null || e.getEndpointGroup() == null)
+ continue;
+ OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
+ if (ofc == null || ofc.getNodeId() == null) continue;
+
+ syncEPL2(t, tiid, flowMap, nodeId, e, ofc, key);
+ }
+ }
+
+ private void syncEPL2(ReadWriteTransaction t,
+ InstanceIdentifier<Table> tiid,
+ Map<String, FlowCtx> flowMap,
+ NodeId nodeId,
+ Endpoint e, OfOverlayContext ofc,
+ EgKey key)
+ throws Exception {
+
+ ArrayList<Instruction> instructions = new ArrayList<>();
+ int order = 0;
+
+ String nextHop;
+ if (LocationType.External.equals(ofc.getLocationType())) {
+ // XXX - TODO - perform NAT and send to the external network
+ nextHop = "external";
+ LOG.warn("External endpoints not yet supported");
+ return;
+ } else {
+ if (Objects.equals(ofc.getNodeId(), nodeId)) {
+ // this is a local endpoint
+ nextHop = ofc.getNodeConnectorId().getValue();
+
+ instructions.add(new InstructionBuilder()
+ .setOrder(order++)
+ .setInstruction(FlowUtils.outputActionIns(ofc.getNodeConnectorId()))
+ .build());
+ } else {
+ // this endpoint is on a different switch; send to the
+ // appropriate tunnel
+
+ // XXX - TODO Add action: set tunnel_id from sEPG register
+
+ IpAddress tunDst =
+ ctx.switchManager.getTunnelIP(ofc.getNodeId());
+ NodeConnectorId tunPort =
+ ctx.switchManager.getTunnelPort(nodeId);
+ if (tunDst == null) return;
+ if (tunPort == null) return;
+
+ if (tunDst.getIpv4Address() != null) {
+ nextHop = tunDst.getIpv4Address().getValue();
+
+ // XXX - TODO Add action: set tunnel dst to tunDst ipv4
+ } else if (tunDst.getIpv6Address() != null) {
+ nextHop = tunDst.getIpv6Address().getValue();
+
+ // XXX - TODO Add action: set tunnel dst to tunDst ipv6
+ } else {
+ // this shouldn't happen
+ LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
+ return;
+ }
+
+ instructions.add(new InstructionBuilder()
+ .setOrder(order++)
+ .setInstruction(FlowUtils.outputActionIns(tunPort))
+ .build());
+ }
+ }
+
+ instructions.add(new InstructionBuilder()
+ .setOrder(order++)
+ .setInstruction(FlowUtils.gotoTable((short)(getTableId()+1)))
+ .build());
+
+ FlowId flowid = new FlowId(new StringBuilder()
+ .append(e.getL2Context())
+ .append("|l2|")
+ .append(e.getMacAddress())
+ .append("|")
+ .append(nextHop)
+ .toString());
+ if (!visit(flowMap, flowid.getValue()))
+ return;
+
+ FlowBuilder flowb = base()
+ .setId(flowid)
+ .setMatch(new MatchBuilder()
+ .setEthernetMatch(FlowUtils.ethernetMatch(null,
+ e.getMacAddress(),
+ null))
+ .build())
+ .setInstructions(new InstructionsBuilder()
+ .setInstruction(instructions)
+ .build());
+ writeFlow(t, tiid, flowb.build());
+ }
+}
package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DropActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.drop.action._case.DropActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
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.FlowKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.GoToTableCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteActionsCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTableBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.write.actions._case.WriteActionsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
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;
return createFlowPath(table, new FlowKey(flowId));
}
- public static Instructions gotoTable(short tableId) {
+ public static Instructions gotoTableInstructions(short tableId) {
return new InstructionsBuilder()
.setInstruction(ImmutableList.of(new InstructionBuilder()
.setOrder(Integer.valueOf(0))
- .setInstruction(new GoToTableCaseBuilder()
- .setGoToTable(new GoToTableBuilder()
- .setTableId(tableId)
- .build())
- .build())
+ .setInstruction(gotoTable(tableId))
.build()))
.build();
}
+ public static Instruction gotoTable(short tableId) {
+ return new GoToTableCaseBuilder()
+ .setGoToTable(new GoToTableBuilder()
+ .setTableId(tableId)
+ .build())
+ .build();
+ }
+
+ public static Instruction outputActionIns(NodeConnectorId id) {
+ return new WriteActionsCaseBuilder()
+ .setWriteActions(new WriteActionsBuilder()
+ .setAction(ImmutableList.of(new ActionBuilder()
+ .setOrder(Integer.valueOf(0))
+ .setAction(outputAction(id))
+ .build()))
+ .build())
+ .build();
+ }
+
public static Instructions dropInstructions() {
return new InstructionsBuilder()
- .setInstruction(ImmutableList.of(new InstructionBuilder()
- .setOrder(Integer.valueOf(0))
- .setInstruction(new WriteActionsCaseBuilder()
- .setWriteActions(new WriteActionsBuilder()
- .setAction(ImmutableList.of(new ActionBuilder()
- .setOrder(Integer.valueOf(0))
- .setAction(new DropActionCaseBuilder()
- .setDropAction(new DropActionBuilder()
- .build())
- .build())
- .build()))
- .build())
+ .setInstruction(ImmutableList.of(new InstructionBuilder()
+ .setOrder(Integer.valueOf(0))
+ .setInstruction(new WriteActionsCaseBuilder()
+ .setWriteActions(new WriteActionsBuilder()
+ .setAction(ImmutableList.of(new ActionBuilder()
+ .setOrder(Integer.valueOf(0))
+ .setAction(dropAction())
+ .build()))
.build())
- .build()))
+ .build())
+ .build()))
+ .build();
+ }
+
+ public static Action dropAction() {
+ return new DropActionCaseBuilder()
+ .setDropAction(new DropActionBuilder()
+ .build())
+ .build();
+ }
+
+ public static Action outputAction(NodeConnectorId id) {
+ return new OutputActionCaseBuilder()
+ .setOutputAction(new OutputActionBuilder()
+ .setOutputNodeConnector(id)
+ .build())
.build();
}
package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
import java.util.Map;
+import java.util.Set;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
InstanceIdentifier<Table> tiid,
Map<String, FlowCtx> flowMap,
NodeId nodeId, Dirty dirty) {
+ // Allow traffic from tunnel and external ports
+ NodeConnectorId tunnelIf = ctx.switchManager.getTunnelPort(nodeId);
+ if (tunnelIf != null)
+ allowFromPort(t, tiid, flowMap, tunnelIf);
+ Set<NodeConnectorId> external =
+ ctx.switchManager.getExternalPorts(nodeId);
+ for (NodeConnectorId extIf: external) {
+ allowFromPort(t, tiid, flowMap, extIf);
+ }
+
// Default drop all
dropFlow(t, tiid, flowMap, 1, null);
dropFlow(t, tiid, flowMap, 110, ARP);
dropFlow(t, tiid, flowMap, 111, IPv4);
dropFlow(t, tiid, flowMap, 112, IPv6);
-
- // XXX - TODO Allow traffic from tunnel ports and external ports
for (Endpoint e : ctx.endpointManager.getEndpointsForNode(nodeId)) {
OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
}
}
+ private void allowFromPort(ReadWriteTransaction t,
+ InstanceIdentifier<Table> tiid,
+ Map<String, FlowCtx> flowMap,
+ NodeConnectorId port) {
+ FlowId flowid = new FlowId(new StringBuilder()
+ .append("allow|")
+ .append(port.getValue())
+ .toString());
+ if (visit(flowMap, flowid.getValue())) {
+ FlowBuilder flowb = base()
+ .setId(flowid)
+ .setPriority(Integer.valueOf(200))
+ .setMatch(new MatchBuilder()
+ .setInPort(port)
+ .build())
+ .setInstructions(FlowUtils.gotoTableInstructions((short)(getTableId()+1)));
+ writeFlow(t, tiid, flowb.build());
+ }
+ }
+
private void dropFlow(ReadWriteTransaction t,
InstanceIdentifier<Table> tiid,
Map<String, FlowCtx> flowMap,
null, null))
.setInPort(ofc.getNodeConnectorId())
.build())
- .setInstructions(FlowUtils.gotoTable((short)(TABLE_ID + 1)));
+ .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)));
writeFlow(t, tiid, flowb.build());
}
.setLayer3Match(m)
.setInPort(ofc.getNodeConnectorId())
.build())
- .setInstructions(FlowUtils.gotoTable((short)(TABLE_ID + 1)))
+ .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)))
.build();
writeFlow(t, tiid, flow);
.setInPort(ofc.getNodeConnectorId())
.build())
// XXX TODO set sepg, bd, fd, vrf into registers
- .setInstructions(FlowUtils.gotoTable((short)(TABLE_ID + 1)));
+ .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)));
writeFlow(t, tiid, flowb.build());
}
}
ConditionSet ep1Conds,
TenantId ep2Tenant,
EndpointGroupId ep2Group,
- ConditionSet ep2Conds) {
+ ConditionSet ep2Conds) {
EgKey k1 = new EgKey(ep1Tenant, ep1Group);
EgKey k2 = new EgKey(ep2Tenant, ep2Group);
Policy p = policy.get().get(k1, k2);
return result;
}
+ /**
+ * Get the set of providers that have contracts with the consumer
+ * @param tenant the tenant ID for the endpoint group
+ * @param eg the endpoint group ID
+ */
+ protected Set<EgKey> getProvidersForConsumer(TenantId tenant,
+ EndpointGroupId eg) {
+ EgKey k = new EgKey(tenant, eg);
+ return Collections.unmodifiableSet(policy.get().row(k).keySet());
+ }
+
+ /**
+ * Get the set of providers that apply
+ * @param tenant the tenant ID for the endpoint group
+ * @param eg the endpoint group ID
+ */
+ protected Set<EgKey> getConsumersForProvider(TenantId tenant,
+ EndpointGroupId eg) {
+ EgKey k = new EgKey(tenant, eg);
+ return Collections.unmodifiableSet(policy.get().column(k).keySet());
+ }
+
/**
* Atomically update the active policy and notify policy listeners
* of relevant changes
return policyCache.getPolicy(ep1Tenant, ep1Group, ep1Conds,
ep2Tenant, ep2Group, ep2Conds);
}
-
+ /**
+ * Get the set of providers that have contracts with the consumer
+ * @param tenant the tenant ID for the endpoint group
+ * @param eg the endpoint group ID
+ */
+ public Set<EgKey> getProvidersForConsumer(TenantId tenant,
+ EndpointGroupId eg) {
+ return policyCache.getProvidersForConsumer(tenant, eg);
+ }
+
+ /**
+ * Get the set of providers that apply
+ * @param tenant the tenant ID for the endpoint group
+ * @param eg the endpoint group ID
+ */
+ public Set<EgKey> getConsumersForProvider(TenantId tenant,
+ EndpointGroupId eg) {
+ return policyCache.getConsumersForProvider(tenant, eg);
+ }
+
/**
* Get the normalized tenant for the given ID
* @param tenant the tenant ID
TenantContext context = resolvedTenants.get(tenantId);
if (context == null) {
ListenerRegistration<DataChangeListener> registration = null;
- if (dataProvider != null) {
- registration = dataProvider
- .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
- TenantUtils.tenantIid(tenantId),
- new PolicyChangeListener(tenantId),
- DataChangeScope.SUBTREE);
- }
+ registration = dataProvider
+ .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+ TenantUtils.tenantIid(tenantId),
+ new PolicyChangeListener(tenantId),
+ DataChangeScope.SUBTREE);
context = new TenantContext(registration);
TenantContext oldContext =
ext:augment-identifier "of-overlay-context-input";
uses endpoint-location;
}
+
+ augment "/inv:nodes/inv:node" {
+ ext:augment-identifier "of-overlay-node-config";
+
+ leaf tunnel-ip {
+ description
+ "The IP address used for tunnel termination on this switch";
+ type inet:ip-address;
+ }
+ container ovsdb-config {
+ leaf ip {
+ description
+ "The IP address used to connect to OVSDB on this switch";
+ type inet:ip-address;
+ }
+ leaf port {
+ description
+ "The port number used to connect to OVSDB on this switch";
+ type inet:port-number;
+ default 6640;
+ }
+ }
+ leaf-list external-interfaces {
+ description
+ "The names of the OpenFlow interfaces that are external and
+ lead to the underlay network. Can be specified as a
+ regular expression.";
+ type string;
+ }
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.groupbasedpolicy.renderer.ofoverlay;
+
+import java.util.Set;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+
+/**
+ * Mock version of switch manager suitable for unit tests
+ */
+public class MockSwitchManager extends SwitchManager {
+
+ public MockSwitchManager() {
+ super(null, null);
+ }
+
+ public void addSwitch(NodeId node,
+ NodeConnectorId tunnelPort,
+ Set<NodeConnectorId> externalPorts) {
+ SwitchState state = new SwitchState(node, tunnelPort, externalPorts);
+ state.status = SwitchStatus.READY;
+ switches.put(node, state);
+ for (SwitchListener listener : listeners) {
+ listener.switchReady(node);
+ }
+ }
+}
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockEndpointManager;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockPolicyManager;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockSwitchManager;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable.FlowCtx;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable.FlowTableCtx;
import org.opendaylight.groupbasedpolicy.resolver.MockPolicyResolver;
public class FlowTableTest {
FlowTableCtx ctx;
FlowTable table;
+
MockEndpointManager endpointManager;
MockPolicyResolver policyResolver;
MockPolicyManager policyManager;
+ MockSwitchManager switchManager;
NodeId nodeId = new NodeId("openflow:1");
InstanceIdentifier<Table> tiid;
endpointManager = new MockEndpointManager();
policyResolver = new MockPolicyResolver();
policyManager = new MockPolicyManager(policyResolver, endpointManager);
+ switchManager = new MockSwitchManager();
+
ctx = new FlowTableCtx(null,
null,
policyManager,
policyResolver,
- null,
+ switchManager,
endpointManager,
null);
}
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3AddressBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6Match;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import static org.junit.Assert.*;
Long etherType = null;
if (f.getMatch() != null) {
etherType = f.getMatch().getEthernetMatch()
- .getEthernetType().getType().getValue().longValue();
+ .getEthernetType().getType().getValue();
}
if (f.getMatch() == null ||
PortSecurity.ARP.equals(etherType) ||
any(InstanceIdentifier.class),
any(Flow.class));
}
+
+ @Test
+ public void testNonLocalAllow() throws Exception {
+ switchManager
+ .addSwitch(new NodeId("openflow:1"),
+ new NodeConnectorId("openflow:1:1"),
+ ImmutableSet.of(new NodeConnectorId("openflow:1:2")));
+
+ ReadWriteTransaction t = dosync(null);
+ ArgumentCaptor<Flow> ac = ArgumentCaptor.forClass(Flow.class);
+ verify(t, atLeastOnce()).put(eq(LogicalDatastoreType.CONFIGURATION),
+ any(InstanceIdentifier.class), ac.capture());
+
+ int count = 0;
+ HashMap<String, FlowCtx> flowMap = new HashMap<>();
+ Set<String> ncs = ImmutableSet.of("openflow:1:1", "openflow:1:2");
+ for (Flow f : ac.getAllValues()) {
+ flowMap.put(f.getId().getValue(), new FlowCtx(f));
+ if (f.getMatch() != null && f.getMatch().getInPort() != null &&
+ ncs.contains(f.getMatch().getInPort().getValue())) {
+ assertEquals(f.getInstructions(),
+ FlowUtils.gotoTableInstructions((short)(table.getTableId()+1)));
+ count += 1;
+ }
+ }
+ assertEquals(2, count);
+
+ t = dosync(flowMap);
+ verify(t, never()).put(any(LogicalDatastoreType.class),
+ any(InstanceIdentifier.class),
+ any(Flow.class));
+ }
@Test
public void testL2() throws Exception {
.build();
endpointManager.addEndpoint(ep);
- ReadWriteTransaction t = dosync(null);
+ ReadWriteTransaction t = dosync(null);
ArgumentCaptor<Flow> ac = ArgumentCaptor.forClass(Flow.class);
verify(t, atLeastOnce()).put(eq(LogicalDatastoreType.CONFIGURATION),
any(InstanceIdentifier.class), ac.capture());
Objects.equals(ep.getAugmentation(OfOverlayContext.class).getNodeConnectorId(),
f.getMatch().getInPort())) {
count += 1;
- assertEquals(FlowUtils.gotoTable((short)(table.getTableId()+1)),
+ assertEquals(FlowUtils.gotoTableInstructions((short)(table.getTableId()+1)),
f.getInstructions());
}
}
.build();
endpointManager.addEndpoint(ep);
- ReadWriteTransaction t = dosync(null);
+ ReadWriteTransaction t = dosync(null);
ArgumentCaptor<Flow> ac = ArgumentCaptor.forClass(Flow.class);
verify(t, atLeastOnce()).put(eq(LogicalDatastoreType.CONFIGURATION),
any(InstanceIdentifier.class), ac.capture());
Objects.equals(ep.getL3Address().get(1).getIpAddress().getIpv6Address().getValue(),
((Ipv6Match)f.getMatch().getLayer3Match()).getIpv6Source().getValue())))) {
count += 1;
- assertEquals(FlowUtils.gotoTable((short)(table.getTableId()+1)),
+ assertEquals(FlowUtils.gotoTableInstructions((short)(table.getTableId()+1)),
f.getInstructions());
}
}