Fixed main issues of SwitchManager 30/19630/18
authorMartin Sunal <msunal@cisco.com>
Tue, 5 May 2015 13:43:57 +0000 (15:43 +0200)
committerMartin Sunal <msunal@cisco.com>
Sun, 10 May 2015 09:47:24 +0000 (11:47 +0200)
SwitchManager is feeded from listeners
    FlowCapableNodeListener - FlowCapableNode from OPER DS on BASE scope of OF augmentation
    OfOverlayNodeListener - OfOverlayNodeConfig from CONF DS on BASE scope of OF overlay augmentation
    FlowCapableNodeConnectorListener - FlowCapableNodeConnector from OPER DS on BASE scope of OF augmentation

SwitchState is composed based on listeners' data:
    FlowCapableNode, OfOverlayNodeConfig, and FlowCapableNodeConnector-s
SwitchManager keeps track of SwitcheState-s in Map.

SwitchState can have 3 types of status:
  DISCONNECTED - SwitchState does not contain FlowCapableNode.
  PREPARING - SwitchState contains FlowCapableNode but information about tunnel(tunnelPort, tunnelIp) is missing
  READY - SwitchState contains FlowCapableNode, tunnelPort and tunnelIp

Even status of SwitchState is DISCONNECTED the SwitchManager still keeps track
until SwitchState contains configuration. This is because FlowCapableNode can disappear from OPER DS but
additional OfOverlayNodeConfig data are still in CONF DS.
Without this tracking we would loose OfOverlayNodeConfig data when SwitchState had been removed.

Change-Id: I7781fdfb0e306568587a1c69c2ab9f90e205180d
Signed-off-by: Martin Sunal <msunal@cisco.com>
12 files changed:
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/EndpointManager.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRenderer.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OfContext.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/PolicyManager.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/SwitchManager.java [deleted file]
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/FlowCapableNodeConnectorListener.java [new file with mode: 0644]
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/FlowCapableNodeListener.java [new file with mode: 0644]
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/OfOverlayNodeListener.java [new file with mode: 0644]
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/SwitchListener.java [moved from renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/SwitchListener.java with 93% similarity]
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/SwitchManager.java [new file with mode: 0644]
renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/OfTableTest.java
renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/MockSwitchManager.java [moved from renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/MockSwitchManager.java with 77% similarity]

index 0d718c4711ad8bbaaa9b4efb2961761ce80c93b3..9cb8ffc7360eb53e69c0133be4a9643c140e8d61 100644 (file)
@@ -31,6 +31,7 @@ import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
 import org.opendaylight.groupbasedpolicy.endpoint.EndpointRpcRegistry;
 import org.opendaylight.groupbasedpolicy.endpoint.EpKey;
 import org.opendaylight.groupbasedpolicy.endpoint.EpRendererAugmentation;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager;
 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
 import org.opendaylight.groupbasedpolicy.util.SetUtils;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
@@ -88,10 +89,6 @@ public class EndpointManager implements AutoCloseable, DataChangeListener
             LoggerFactory.getLogger(EndpointManager.class);
     private final static InstanceIdentifier<Nodes> nodesIid = InstanceIdentifier
             .builder(Nodes.class).build();
-    private final static InstanceIdentifier<Node> nodeIid = InstanceIdentifier
-            .builder(Nodes.class).child(Node.class).build();
-    private ListenerRegistration<DataChangeListener> nodesReg;
-
     private static final InstanceIdentifier<Endpoint> endpointsIid =
             InstanceIdentifier.builder(Endpoints.class)
                     .child(Endpoint.class).build();
@@ -128,10 +125,6 @@ public class EndpointManager implements AutoCloseable, DataChangeListener
                             endpointsIid,
                             this,
                             DataChangeScope.ONE);
-            nodesReg = dataProvider.registerDataChangeListener(
-                    LogicalDatastoreType.OPERATIONAL, nodeIid,
-                    new NodesListener(), DataChangeScope.SUBTREE);
-
         } else
             listenerReg = null;
 
@@ -411,113 +404,6 @@ public class EndpointManager implements AutoCloseable, DataChangeListener
         }
     }
 
-    // TODO: alagalah Investigate using the internal project listener structure
-    // for this. ie Endpoint should listen to
-    // SwitchManager updates and update the EP maps accordingly (update
-    // Endpoint). Removal should include the overloaded
-    // method updateEndpoint(Node node)
-    private class NodesListener implements DataChangeListener {
-        @Override
-        public void onDataChanged(
-                AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
-            for (DataObject dao : change.getCreatedData().values()) {
-                if (!(dao instanceof Node))
-                    continue;
-                Node node = (Node) dao;
-                if (node.getNodeConnector() != null) {
-                    updateEndpoint(node);
-                }
-            }
-            for (DataObject dao : change.getUpdatedData().values()) {
-                if (!(dao instanceof Node))
-                    continue;
-                Node node = (Node) dao;
-                if (node.getNodeConnector() != null) {
-                    updateEndpoint(node);
-                }
-            }
-        }
-    }
-
-    // TODO Li alagalah move this near to other updateEndpoint()
-    private void updateEndpoint(Node node) {
-        final InstanceIdentifier<Endpoints> endpointsIid = InstanceIdentifier.builder(Endpoints.class).build();
-
-        Optional<Endpoints> epResult;
-        EpKey epKey = null;
-        for (NodeConnector nc : node.getNodeConnector()) {
-            FlowCapableNodeConnector fcnc = nc
-                    .getAugmentation(FlowCapableNodeConnector.class);
-            try {
-                epResult = dataProvider.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL, endpointsIid)
-                        .get();
-                if (epResult.isPresent()) {
-                    Endpoints endpoints = epResult.get();
-                    if (endpoints.getEndpoint() != null) {
-                        Boolean isEmpty = true;
-                        for (Endpoint ep : endpoints.getEndpoint()) {
-                            // 2. Search for portname
-                            OfOverlayContext currentAugmentation = ep.getAugmentation(OfOverlayContext.class);
-                            if (currentAugmentation.getPortName() != null && fcnc.getName() != null
-                                    && currentAugmentation.getPortName().getValue().equals(fcnc.getName())) {
-                                NodeId nodeId;
-                                NodeConnectorId nodeConnectorId;
-                                Name name;
-                                try {
-                                    nodeId = currentAugmentation.getNodeId();
-                                    nodeConnectorId = currentAugmentation.getNodeConnectorId();
-                                    name = currentAugmentation.getPortName();
-                                } catch (Exception e) {
-                                    nodeId = null;
-                                    nodeConnectorId = null;
-                                    name = null;
-                                }
-                                Boolean process = false;
-                                if (nodeId == null && nodeConnectorId == null) {
-                                    LOG.debug("ep NodeID and NC ID Both null");
-                                    process = true;
-                                }
-                                if (nodeId != null && nodeConnectorId != null) {
-                                    if (!(nodeConnectorId.getValue().equals(nc.getId().getValue()))) {
-                                        LOG.debug("ep NodeID and NC ID Both NOT null but epNCID !=nodeNCID");
-                                        process = true;
-                                    }
-                                }
-                                if (process) {
-                                    WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
-                                    // 3. Update endpoint
-                                    EndpointBuilder epBuilder = new EndpointBuilder(ep);
-                                    OfOverlayContextBuilder ofOverlayAugmentation = new OfOverlayContextBuilder();
-                                    ofOverlayAugmentation.setNodeId(node.getId());
-                                    ofOverlayAugmentation.setNodeConnectorId(nc.getId());
-                                    ofOverlayAugmentation.setPortName(name);
-                                    epBuilder.addAugmentation(OfOverlayContext.class, ofOverlayAugmentation.build());
-                                    epBuilder.setL3Address(ep.getL3Address());
-                                    InstanceIdentifier<Endpoint> iidEp = InstanceIdentifier.builder(Endpoints.class)
-                                            .child(Endpoint.class, ep.getKey()).build();
-                                    tx.put(LogicalDatastoreType.OPERATIONAL, iidEp, epBuilder.build());
-                                    tx.submit().get();
-                                    epKey = new EpKey(ep.getKey().getL2Context(), ep.getKey().getMacAddress());
-                                    notifyEndpointUpdated(epKey);
-                                    LOG.debug("Values:");
-                                    LOG.debug("node: Node ID:" + node.getId().getValue());
-                                    LOG.debug("node: NodeConnectorID: " + nc.getId().getValue());
-                                    if (nodeId != null && nodeConnectorId != null) {
-                                        LOG.debug("ep: nodeID:" + nodeId.getValue());
-                                        LOG.debug("ep: nodeConnectorID:" + nodeConnectorId.getValue());
-                                    }
-                                    isEmpty = false;
-                                }
-                            }
-                        }
-                    }
-                }
-            } catch (InterruptedException | ExecutionException e) {
-                LOG.error("Exception in UpdateEndpoint", e);
-            }
-        }
-    }
-
     // **************
     // Implementation
     // **************
index 263778a1dc6ea33c361de1ebf46f6d9cec93b35c..22763f06aa6bb2170d2f3c79b31aa6d950d02ef6 100644 (file)
@@ -17,6 +17,7 @@ import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataCh
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager;
 import org.opendaylight.groupbasedpolicy.resolver.PolicyResolver;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -62,7 +63,7 @@ public class OFOverlayRenderer implements AutoCloseable, DataChangeListener {
         //TODO: Consider moving to groupbasedpolicy-ofoverlay-config so as to be user configurable in distribution.
         executor = Executors.newScheduledThreadPool(numCPU * 2);
 
-        switchManager = new SwitchManager(dataProvider, executor);
+        switchManager = new SwitchManager(dataProvider);
         endpointManager = new EndpointManager(dataProvider, rpcRegistry,
                                               executor, switchManager);
         policyResolver = new PolicyResolver(dataProvider, executor);
@@ -120,7 +121,7 @@ public class OFOverlayRenderer implements AutoCloseable, DataChangeListener {
             public void onSuccess(final Optional<OfOverlayConfig> result) {
                 if (!result.isPresent()) return;
                 if (result.get() instanceof OfOverlayConfig) {
-                    config = (OfOverlayConfig)result.get();
+                    config = result.get();
                     applyConfig();
                 }
             }
index 78f1c17ce80da2c04e1d1c233d6d81d101a32d4e..a86f050f9c93439cba1b4864b819a2f655179b1e 100644 (file)
@@ -12,6 +12,7 @@ import java.util.concurrent.ScheduledExecutorService;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager;
 import org.opendaylight.groupbasedpolicy.resolver.PolicyResolver;
 
 public class OfContext {
index ee64e488970ea7bed08c88105d6f5765a0f5767d..beedc2a8667b46c0b5e0f9e2226c896de7ce21c0 100644 (file)
@@ -36,6 +36,8 @@ import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OfTable;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PortSecurity;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.SourceMapper;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchListener;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures;
 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
@@ -142,32 +144,7 @@ public class PolicyManager
 
     @Override
     public void switchReady(final NodeId nodeId) {
-        //TODO Apr15 alagalah : OVSDB CRUD tunnels may go here.
-//        WriteTransaction t = dataBroker.newWriteOnlyTransaction();
-//
-//        NodeBuilder nb = new NodeBuilder()
-//            .setId(nodeId)
-//            .addAugmentation(FlowCapableNode.class,
-//                             new FlowCapableNodeBuilder()
-//                                .build());
-//        t.merge(LogicalDatastoreType.CONFIGURATION,
-//                FlowUtils.createNodePath(nodeId),
-//                nb.build(), true);
-//        ListenableFuture<Void> result = t.submit();
-//        Futures.addCallback(result,
-//                            new FutureCallback<Void>() {
-//            @Override
-//            public void onSuccess(Void result) {
-//                dirty.get().addNode(nodeId);
-//                scheduleUpdate();
-//            }
-//
-//            @Override
-//            public void onFailure(Throwable t) {
-//                LOG.error("Could not add switch {}", nodeId, t);
-//            }
-//        });
-
+        scheduleUpdate();
     }
 
     @Override
@@ -276,7 +253,7 @@ public class PolicyManager
                    t.read(LogicalDatastoreType.CONFIGURATION, entry.getKey()).get();
 
             if (r.isPresent()) {
-                Table curTable = (Table)r.get();
+                Table curTable = r.get();
                 curr = new HashSet<Flow>(curTable.getFlow());
             }
             Sets.SetView<Flow> deletions = Sets.difference(curr, update);
@@ -354,8 +331,6 @@ public class PolicyManager
         @Override
         public Void call() throws Exception {
             for (NodeId node : switchManager.getReadySwitches()) {
-                if (!switchManager.isSwitchReady(node))
-                    return null;
                 PolicyInfo info = policyResolver.getCurrentPolicy();
                 if (info == null)
                     return null;
diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/SwitchManager.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/SwitchManager.java
deleted file mode 100644 (file)
index 940f393..0000000
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * 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.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.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
-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;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * Manage connected switches and ensure their configuration is set up
- * correctly
- * @author readams
- */
-public class SwitchManager implements AutoCloseable {
-    private static final Logger LOG = 
-            LoggerFactory.getLogger(SwitchManager.class);
-
-    private final DataBroker dataProvider;
-
-    private final static InstanceIdentifier<Nodes> nodesIid =
-            InstanceIdentifier.builder(Nodes.class).build();
-    private final static InstanceIdentifier<Node> nodeIid =
-            InstanceIdentifier.builder(Nodes.class)
-                .child(Node.class).build();
-    private ListenerRegistration<DataChangeListener> nodesReg;
-    private ListenerRegistration<DataChangeListener> nodesConfigReg;
-
-    protected ConcurrentHashMap<NodeId, SwitchState> switches = 
-            new ConcurrentHashMap<>();
-    protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
-
-    public SwitchManager(DataBroker dataProvider,
-                         ScheduledExecutorService executor) {
-        super();
-        this.dataProvider = dataProvider;
-        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");
-    }
-
-    // *************
-    // SwitchManager
-    // *************
-
-    /**
-     * Get the collection of switches that are in the "ready" state.  Note
-     * that the collection may be concurrently modified
-     * @return A {@link Collection} containing the switches that are ready.
-     */
-    public Collection<NodeId> getReadySwitches() {
-        Collection<SwitchState> ready =
-                Collections2.filter(switches.values(),
-                            new Predicate<SwitchState>() {
-                    @Override
-                    public boolean apply(SwitchState input) {
-                        return SwitchStatus.READY.equals(input.status);
-                    }
-                });
-        return Collections2.transform(ready,
-                                      new Function<SwitchState, NodeId>() {
-            @Override
-            public NodeId apply(SwitchState input) {
-                return input.nodeId;
-            }
-        });
-    }
-
-    /**
-     * Check whether the specified switch is in the ready state
-     * @param nodeId the node
-     * @return <code>true</code> if the switch is in the ready state
-     */
-    public boolean isSwitchReady(NodeId nodeId) {
-        SwitchState state = switches.get(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;
-    }
-    
-    //TODO OVSDB CRUD belongs in here. (see Trello)
-    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
-     * @param listener the {@link SwitchListener} to add
-     */
-    public void registerListener(SwitchListener listener) {
-        listeners.add(listener);
-    }
-
-    /**
-     * Set the encapsulation format the specified value
-     * @param format The new format
-     */
-    public void setEncapsulationFormat(EncapsulationFormat format) {
-        // No-op for now
-    }
-
-    // *************
-    // AutoCloseable
-    // *************
-
-    @Override
-    public void close() throws Exception {
-        nodesReg.close();
-        nodesConfigReg.close();
-    }
-
-    // ******************
-    // DataChangeListener
-    // ******************
-
-    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.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 = old;
-        }
-        return state;
-    }
-    
-    private void updateSwitch(DataObject dao) {
-        if (!(dao instanceof Node)) return;
-        // Switches are registered as Nodes in the inventory; OpenFlow switches
-        // are of type FlowCapableNode
-        Node node = (Node)dao;
-        FlowCapableNode fcn = node.getAugmentation(FlowCapableNode.class);
-        if (fcn == null) return;
-
-        LOG.debug("{} update", 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);
-        }
-    }
-
-    // XXX there's a race condition here if a switch exists at startup and is
-    // removed very quickly.
-    private final FutureCallback<Optional<Nodes>> readSwitchesCallback =
-            new FutureCallback<Optional<Nodes>>() {
-        @Override
-        public void onSuccess(Optional<Nodes> result) {
-            if (result.isPresent() && result.get() instanceof Nodes) {
-                Nodes nodes = (Nodes)result.get();
-                for (Node node : nodes.getNode()) {
-                    updateSwitch(node);
-                }
-            }
-        }
-
-        @Override
-        public void onFailure(Throwable t) {
-            LOG.error("Count not read switch information", t);
-        }
-    };
-
-    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.
-     *
-     * <p>This is safe only if there can only be one notification at a time,
-     * as there are race conditions in the face of concurrent data change
-     * notifications
-     */
-    private void readSwitches() {
-        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);
-        }
-    }
-
-    /**
-     * Set the ready state of the node to PREPARING and begin the initialization
-     * process
-     */
-    private void switchConnected(NodeId nodeId) {
-        SwitchState state = switches.get(nodeId);
-        if (state != null) {
-            // XXX - TODO - For now we just go straight to ready state.
-            // need to configure tunnels and tables as needed
-            switchReady(nodeId);
-            LOG.info("New switch {} connected", nodeId);
-        }
-    }
-
-    /**
-     * Set the ready state of the node to READY and notify listeners
-     */
-    private void switchReady(NodeId nodeId) {
-        SwitchState state = switches.get(nodeId);
-        if (state != null) {
-            state.status = SwitchStatus.READY;
-            for (SwitchListener listener : listeners) {
-                listener.switchReady(nodeId);
-            }
-        }
-    }
-
-    /**
-     * Remove the switch from the switches we're keeping track of and
-     * notify listeners
-     */
-    private void removeSwitch(NodeId nodeId) {
-        switches.remove(nodeId);
-        for (SwitchListener listener : listeners) {
-            listener.switchRemoved(nodeId);
-        }
-        LOG.info("Switch {} removed", nodeId);
-    }
-
-    protected enum SwitchStatus {
-        /**
-         * The switch is not currently connected
-         */
-        DISCONNECTED,
-        /**
-         * The switch is connected but not yet configured
-         */
-        PREPARING,
-        /**
-         * The switch is ready to for policy rules to be installed
-         */
-        READY
-    }
-
-    /**
-     * Internal representation of the state of a connected switch
-     */
-    protected static class SwitchState {
-        NodeId nodeId;
-
-        Node switchNode;
-        OfOverlayNodeConfig nodeConfig;
-        
-        NodeConnectorId tunnelPort;
-        Set<NodeConnectorId> externalPorts = Collections.emptySet();
-
-        SwitchStatus status = SwitchStatus.DISCONNECTED;
-        
-        public SwitchState(NodeId switchNode) {
-            super();
-            nodeId = switchNode;
-        }
-
-        /**
-         * Constructor used for tests
-         */
-        public SwitchState(NodeId node,
-                           NodeConnectorId tunnelPort,
-                           Set<NodeConnectorId> externalPorts,
-                           OfOverlayNodeConfig nodeConfig) {
-            this.nodeId = node;
-            this.tunnelPort = tunnelPort;
-            this.externalPorts = externalPorts;
-            this.nodeConfig = nodeConfig;
-        }
-        
-        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<>();
-            if (ports != null) {
-                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 && nodeConfig.getExternalInterfaces() != 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();
-        }
-    }
-
-}
diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/FlowCapableNodeConnectorListener.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/FlowCapableNodeConnectorListener.java
new file mode 100644 (file)
index 0000000..f74dabf
--- /dev/null
@@ -0,0 +1,195 @@
+package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+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.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
+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.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextBuilder;
+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.node.NodeConnectorKey;
+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.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+
+public class FlowCapableNodeConnectorListener implements DataChangeListener, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FlowCapableNodeConnectorListener.class);
+
+    private final static InstanceIdentifier<FlowCapableNodeConnector> fcNodeConnectorIid = InstanceIdentifier.builder(
+            Nodes.class)
+        .child(Node.class)
+        .child(NodeConnector.class)
+        .augmentation(FlowCapableNodeConnector.class)
+        .build();
+    private final static InstanceIdentifier<Endpoints> endpointsIid = InstanceIdentifier.builder(Endpoints.class)
+        .build();
+    private final DataBroker dataProvider;
+    private final SwitchManager switchManager;
+    private final ListenerRegistration<DataChangeListener> listenerRegistration;
+
+    public FlowCapableNodeConnectorListener(DataBroker dataProvider, SwitchManager switchManager) {
+        this.dataProvider = checkNotNull(dataProvider);
+        this.switchManager = checkNotNull(switchManager);
+        listenerRegistration = dataProvider.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+                fcNodeConnectorIid, this, DataChangeScope.BASE);
+    }
+
+    @Override
+    public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
+        Map<Name, Endpoint> epWithOfOverlayAugByPortName = readEpsWithOfOverlayAugByPortName(rwTx);
+        boolean isDataPutToTx = false;
+        for (Entry<InstanceIdentifier<?>, DataObject> fcncEntry : change.getCreatedData().entrySet()) {
+            if (FlowCapableNodeConnector.class.equals(fcncEntry.getKey().getTargetType())) {
+                InstanceIdentifier<NodeConnector> ncIid = fcncEntry.getKey().firstIdentifierOf(NodeConnector.class);
+                FlowCapableNodeConnector fcnc = (FlowCapableNodeConnector) fcncEntry.getValue();
+                LOG.trace(
+                        "FlowCapableNodeConnector created: NodeId: {} NodeConnectorId: {} FlowCapableNodeConnector: {}",
+                        ncIid.firstKeyOf(Node.class, NodeKey.class).getId().getValue(),
+                        ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
+                switchManager.updateSwitchNodeConnectorConfig(ncIid, fcnc);
+                Name portName = getPortName(fcnc);
+                boolean updated = updateEpWithNodeConnectorInfo(epWithOfOverlayAugByPortName.get(portName), ncIid, rwTx);
+                if (updated == true) {
+                    isDataPutToTx = true;
+                }
+            }
+        }
+        for (Entry<InstanceIdentifier<?>, DataObject> fcncEntry : change.getUpdatedData().entrySet()) {
+            if (FlowCapableNodeConnector.class.equals(fcncEntry.getKey().getTargetType())) {
+                InstanceIdentifier<NodeConnector> ncIid = fcncEntry.getKey().firstIdentifierOf(NodeConnector.class);
+                FlowCapableNodeConnector fcnc = (FlowCapableNodeConnector) fcncEntry.getValue();
+                LOG.trace(
+                        "FlowCapableNodeConnector updated: NodeId: {} NodeConnectorId: {} FlowCapableNodeConnector: {}",
+                        ncIid.firstKeyOf(Node.class, NodeKey.class).getId().getValue(),
+                        ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
+                switchManager.updateSwitchNodeConnectorConfig(ncIid, fcnc);
+                Name portName = getPortName(fcnc);
+                boolean updated = updateEpWithNodeConnectorInfo(epWithOfOverlayAugByPortName.get(portName), ncIid, rwTx);
+                if (updated == true) {
+                    isDataPutToTx = true;
+                }
+                FlowCapableNodeConnector originalFcnc = (FlowCapableNodeConnector) change.getOriginalData().get(
+                        fcncEntry.getKey());
+                Name portNameFromOriginalFcnc = getPortName(originalFcnc);
+                // portname already existed and then was changed
+                if (portNameFromOriginalFcnc != null && !Objects.equal(portNameFromOriginalFcnc, portName)) {
+                    updated = updateEpWithNodeConnectorInfo(epWithOfOverlayAugByPortName.get(portNameFromOriginalFcnc),
+                            null, rwTx);
+                    if (updated == true) {
+                        isDataPutToTx = true;
+                    }
+                }
+            }
+        }
+        for (InstanceIdentifier<?> fcncIid : change.getRemovedPaths()) {
+            if (FlowCapableNodeConnector.class.equals(fcncIid.getTargetType())) {
+                InstanceIdentifier<NodeConnector> ncIid = fcncIid.firstIdentifierOf(NodeConnector.class);
+                FlowCapableNodeConnector originalFcnc = (FlowCapableNodeConnector) change.getOriginalData()
+                    .get(fcncIid);
+                LOG.trace("FlowCapableNodeConnector removed: NodeId: {} NodeConnectorId: {}",
+                        ncIid.firstKeyOf(Node.class, NodeKey.class).getId().getValue(),
+                        ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue());
+                switchManager.updateSwitchNodeConnectorConfig(ncIid, null);
+                Name portNameFromOriginalFcnc = getPortName(originalFcnc);
+                boolean updated = updateEpWithNodeConnectorInfo(
+                        epWithOfOverlayAugByPortName.get(portNameFromOriginalFcnc), null, rwTx);
+                if (updated == true) {
+                    isDataPutToTx = true;
+                }
+            }
+        }
+        if (isDataPutToTx) {
+            rwTx.submit();
+        } else {
+            rwTx.cancel();
+        }
+    }
+
+    private Map<Name, Endpoint> readEpsWithOfOverlayAugByPortName(ReadTransaction rTx) {
+        Optional<Endpoints> potentialEps = Futures.getUnchecked(rTx.read(LogicalDatastoreType.OPERATIONAL, endpointsIid));
+        if (!potentialEps.isPresent() || potentialEps.get().getEndpoint() == null) {
+            return Collections.emptyMap();
+        }
+        Map<Name, Endpoint> epsByPortName = new HashMap<>();
+        for (Endpoint ep : potentialEps.get().getEndpoint()) {
+            OfOverlayContext ofOverlayEp = ep.getAugmentation(OfOverlayContext.class);
+            if (ofOverlayEp != null && ofOverlayEp.getPortName() != null) {
+                epsByPortName.put(ofOverlayEp.getPortName(), ep);
+            }
+        }
+        return epsByPortName;
+    }
+
+    private Name getPortName(FlowCapableNodeConnector fcnc) {
+        if (fcnc == null || fcnc.getName() == null) {
+            return null;
+        }
+        return new Name(fcnc.getName());
+    }
+
+    /**
+     * @return {@code true} if data was put to the transaction; {@code false} otherwise
+     */
+    private boolean updateEpWithNodeConnectorInfo(Endpoint epWithOfOverlayAug, InstanceIdentifier<NodeConnector> ncIid,
+            WriteTransaction tx) {
+        if (epWithOfOverlayAug == null) {
+            return false;
+        }
+        OfOverlayContext oldOfOverlayAug = epWithOfOverlayAug.getAugmentation(OfOverlayContext.class);
+        OfOverlayContextBuilder newOfOverlayAug = new OfOverlayContextBuilder(oldOfOverlayAug);
+        if (ncIid == null && oldOfOverlayAug.getNodeConnectorId() == null) {
+            return false;
+        }
+        if (ncIid != null) {
+            NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
+            if (ncId.equals(oldOfOverlayAug.getNodeConnectorId())) {
+                return false;
+            }
+            NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
+            newOfOverlayAug.setNodeId(nodeId);
+            newOfOverlayAug.setNodeConnectorId(ncId);
+        }
+        InstanceIdentifier<OfOverlayContext> epOfOverlayAugIid = InstanceIdentifier.builder(Endpoints.class)
+            .child(Endpoint.class, epWithOfOverlayAug.getKey())
+            .augmentation(OfOverlayContext.class)
+            .build();
+        tx.put(LogicalDatastoreType.OPERATIONAL, epOfOverlayAugIid, newOfOverlayAug.build());
+        return true;
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            listenerRegistration.close();
+        }
+    }
+
+}
diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/FlowCapableNodeListener.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/FlowCapableNodeListener.java
new file mode 100644 (file)
index 0000000..c4f5a17
--- /dev/null
@@ -0,0 +1,74 @@
+package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+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.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+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.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FlowCapableNodeListener implements DataChangeListener, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FlowCapableNodeListener.class);
+
+    private final static InstanceIdentifier<FlowCapableNode> fcNodeIid = InstanceIdentifier.builder(Nodes.class)
+        .child(Node.class)
+        .augmentation(FlowCapableNode.class)
+        .build();
+    private final SwitchManager switchManager;
+    private final ListenerRegistration<DataChangeListener> listenerRegistration;
+
+    public FlowCapableNodeListener(DataBroker dataProvider, SwitchManager switchManager) {
+        this.switchManager = checkNotNull(switchManager);
+        listenerRegistration = checkNotNull(dataProvider).registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+                fcNodeIid, this, DataChangeScope.BASE);
+    }
+
+    @Override
+    public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        for (Entry<InstanceIdentifier<?>, DataObject> fcNodeEntry : change.getCreatedData().entrySet()) {
+            if (FlowCapableNode.class.equals(fcNodeEntry.getKey().getTargetType())) {
+                NodeId nodeId = fcNodeEntry.getKey().firstKeyOf(Node.class, NodeKey.class).getId();
+                FlowCapableNode fcNode = (FlowCapableNode) fcNodeEntry.getValue();
+                LOG.trace("FlowCapableNode created. NodeId: {} FlowCapableNode: {}", nodeId.getValue(), fcNode);
+                switchManager.updateSwitch(nodeId, fcNode);
+            }
+        }
+        for (Entry<InstanceIdentifier<?>, DataObject> fcNodeEntry : change.getUpdatedData().entrySet()) {
+            if (FlowCapableNode.class.equals(fcNodeEntry.getKey().getTargetType())) {
+                NodeId nodeId = fcNodeEntry.getKey().firstKeyOf(Node.class, NodeKey.class).getId();
+                FlowCapableNode fcNode = (FlowCapableNode) fcNodeEntry.getValue();
+                LOG.trace("FlowCapableNode updated. NodeId: {} FlowCapableNode: {}", nodeId.getValue(), fcNode);
+                switchManager.updateSwitch(nodeId, fcNode);
+            }
+        }
+        for (InstanceIdentifier<?> removedFcNodeIid : change.getRemovedPaths()) {
+            if (FlowCapableNode.class.equals(removedFcNodeIid.getTargetType())) {
+                NodeId nodeId = removedFcNodeIid.firstKeyOf(Node.class, NodeKey.class).getId();
+                LOG.trace("FlowCapableNode removed. NodeId: {}", nodeId.getValue());
+                switchManager.updateSwitch(nodeId, null);
+            }
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            listenerRegistration.close();
+        }
+    }
+
+}
diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/OfOverlayNodeListener.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/OfOverlayNodeListener.java
new file mode 100644 (file)
index 0000000..bfb143c
--- /dev/null
@@ -0,0 +1,77 @@
+package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+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.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig;
+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.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OfOverlayNodeListener implements DataChangeListener, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OfOverlayNodeListener.class);
+
+    private final static InstanceIdentifier<OfOverlayNodeConfig> ofOverlayNodeIid = InstanceIdentifier.builder(
+            Nodes.class)
+        .child(Node.class)
+        .augmentation(OfOverlayNodeConfig.class)
+        .build();
+    private final SwitchManager switchManager;
+    private final ListenerRegistration<DataChangeListener> listenerRegistration;
+
+    public OfOverlayNodeListener(DataBroker dataProvider, SwitchManager switchManager) {
+        this.switchManager = checkNotNull(switchManager);
+        listenerRegistration = checkNotNull(dataProvider).registerDataChangeListener(
+                LogicalDatastoreType.CONFIGURATION, ofOverlayNodeIid, this, DataChangeScope.BASE);
+    }
+
+    @Override
+    public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        for (Entry<InstanceIdentifier<?>, DataObject> nodeConfigEntry : change.getCreatedData().entrySet()) {
+            if (OfOverlayNodeConfig.class.equals(nodeConfigEntry.getKey().getTargetType())) {
+                NodeId nodeId = nodeConfigEntry.getKey().firstKeyOf(Node.class, NodeKey.class).getId();
+                OfOverlayNodeConfig nodeConfig = (OfOverlayNodeConfig) nodeConfigEntry.getValue();
+                LOG.trace("OfOverlayNodeConfig created. NodeId: {} OfOverlayNodeConfig: {}", nodeId.getValue(),
+                        nodeConfig);
+                switchManager.updateSwitchConfig(nodeId, nodeConfig);
+            }
+        }
+        for (Entry<InstanceIdentifier<?>, DataObject> nodeConfigEntry : change.getUpdatedData().entrySet()) {
+            if (OfOverlayNodeConfig.class.equals(nodeConfigEntry.getKey().getTargetType())) {
+                NodeId nodeId = nodeConfigEntry.getKey().firstKeyOf(Node.class, NodeKey.class).getId();
+                OfOverlayNodeConfig nodeConfig = (OfOverlayNodeConfig) nodeConfigEntry.getValue();
+                LOG.trace("OfOverlayNodeConfig updated. NodeId: {} OfOverlayNodeConfig: {}", nodeId.getValue(),
+                        nodeConfig);
+                switchManager.updateSwitchConfig(nodeId, nodeConfig);
+            }
+        }
+        for (InstanceIdentifier<?> removedNodeConfigIid : change.getRemovedPaths()) {
+            if (OfOverlayNodeConfig.class.equals(removedNodeConfigIid.getTargetType())) {
+                NodeId nodeId = removedNodeConfigIid.firstKeyOf(Node.class, NodeKey.class).getId();
+                LOG.trace("OfOverlayNodeConfig removed. NodeId: {}", nodeId.getValue());
+                switchManager.updateSwitchConfig(nodeId, null);
+            }
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            listenerRegistration.close();
+        }
+    }
+
+}
similarity index 93%
rename from renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/SwitchListener.java
rename to renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/SwitchListener.java
index a5db1f4b4985689582ab7a2ecf3c82ef46143cd8..1ed01bdef0b980e69c7bba8020fbaf9046029657 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.groupbasedpolicy.renderer.ofoverlay;
+package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node;
 
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 
diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/SwitchManager.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/SwitchManager.java
new file mode 100644 (file)
index 0000000..0f11cab
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * 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.node;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+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.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+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.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+
+/**
+ * Manage connected switches and ensure their configuration is set up
+ * correctly
+ */
+public class SwitchManager implements AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SwitchManager.class);
+
+    protected Map<NodeId, SwitchState> switches = new HashMap<>();
+    protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
+
+    private final FlowCapableNodeListener nodeListener;
+    private final OfOverlayNodeListener ofOverlayNodeListener;
+    private final FlowCapableNodeConnectorListener nodeConnectorListener;
+
+    public SwitchManager(DataBroker dataProvider) {
+        if (dataProvider == null) {
+            LOG.warn("No data provider for {}. Listeners {}, {}, {} are not registered.",
+                    SwitchManager.class.getSimpleName(), FlowCapableNodeListener.class.getSimpleName(),
+                    OfOverlayNodeListener.class.getSimpleName(), FlowCapableNodeConnectorListener.class.getSimpleName());
+            nodeListener = null;
+            ofOverlayNodeListener = null;
+            nodeConnectorListener = null;
+        } else {
+            nodeListener = new FlowCapableNodeListener(dataProvider, this);
+            ofOverlayNodeListener = new OfOverlayNodeListener(dataProvider, this);
+            nodeConnectorListener = new FlowCapableNodeConnectorListener(dataProvider, this);
+        }
+        LOG.debug("Initialized OFOverlay switch manager");
+    }
+
+    /**
+     * Get the collection of switches that are in the "ready" state. Note
+     * that the collection is immutable.
+     *
+     * @return A {@link Collection} containing the switches that are ready.
+     */
+    public synchronized Collection<NodeId> getReadySwitches() {
+        ImmutableList<NodeId> readySwitches = FluentIterable.from(switches.values())
+            .filter(new Predicate<SwitchState>() {
+
+                @Override
+                public boolean apply(SwitchState input) {
+                    return input.status == SwitchStatus.READY;
+                }
+            })
+            .transform(new Function<SwitchState, NodeId>() {
+
+                @Override
+                public NodeId apply(SwitchState input) {
+                    return input.nodeId;
+                }
+            })
+            .toList();
+        LOG.trace("Get ready switches: {}", readySwitches);
+        return readySwitches;
+    }
+
+    public synchronized Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
+        SwitchState state = switches.get(nodeId);
+        if (state == null)
+            return Collections.emptySet();
+        return state.externalPorts;
+    }
+
+    public synchronized NodeConnectorId getTunnelPort(NodeId nodeId) {
+        SwitchState state = switches.get(nodeId);
+        if (state == null)
+            return null;
+        return state.tunnelPort;
+    }
+
+    public synchronized 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
+     *
+     * @param listener the {@link SwitchListener} to add
+     */
+    public void registerListener(SwitchListener listener) {
+        listeners.add(listener);
+    }
+
+    /**
+     * Set the encapsulation format the specified value
+     *
+     * @param format The new format
+     */
+    public void setEncapsulationFormat(EncapsulationFormat format) {
+        // No-op for now
+    }
+
+    synchronized void updateSwitch(NodeId nodeId, @Nullable FlowCapableNode fcNode) {
+        SwitchState state = getSwitchState(checkNotNull(nodeId));
+        SwitchStatus oldStatus = state.status;
+        state.setFlowCapableNode(fcNode);
+        handleSwitchState(state, oldStatus);
+    }
+
+    synchronized void updateSwitchNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid,
+            @Nullable FlowCapableNodeConnector fcnc) {
+        NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
+        SwitchState state = getSwitchState(nodeId);
+        SwitchStatus oldStatus = state.status;
+        state.setNodeConnectorConfig(ncIid, fcnc);
+        handleSwitchState(state, oldStatus);
+    }
+
+    synchronized void updateSwitchConfig(NodeId nodeId, @Nullable OfOverlayNodeConfig config) {
+        SwitchState state = getSwitchState(checkNotNull(nodeId));
+        SwitchStatus oldStatus = state.status;
+        state.setConfig(config);
+        handleSwitchState(state, oldStatus);
+    }
+
+    private SwitchState getSwitchState(NodeId id) {
+        SwitchState state = switches.get(id);
+        if (state == null) {
+            state = new SwitchState(id);
+            switches.put(id, state);
+            LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
+        }
+        return state;
+    }
+
+    private void handleSwitchState(SwitchState state, SwitchStatus oldStatus) {
+        if (oldStatus == SwitchStatus.READY && state.status != SwitchStatus.READY) {
+            LOG.info("Switch {} removed", state.nodeId.getValue());
+            notifySwitchRemoved(state.nodeId);
+        } else if (oldStatus != SwitchStatus.READY && state.status == SwitchStatus.READY) {
+            LOG.info("Switch {} ready", state.nodeId.getValue());
+            notifySwitchReady(state.nodeId);
+        } else if (oldStatus == SwitchStatus.READY && state.status == SwitchStatus.READY) {
+            // TODO Be msunal we could improve this by ignoring of updates where uninteresting fields are changed
+            LOG.debug("Switch {} updated", state.nodeId.getValue());
+            notifySwitchUpdated(state.nodeId);
+        }
+        if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
+            switches.remove(state.nodeId);
+            LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
+        }
+    }
+
+    private void notifySwitchRemoved(NodeId nodeId) {
+        for (SwitchListener listener : listeners) {
+            listener.switchRemoved(nodeId);
+        }
+    }
+
+    private void notifySwitchReady(NodeId nodeId) {
+        for (SwitchListener listener : listeners) {
+            listener.switchReady(nodeId);
+        }
+    }
+
+    private void notifySwitchUpdated(NodeId nodeId) {
+        for (SwitchListener listener : listeners) {
+            listener.switchUpdated(nodeId);
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        nodeListener.close();
+        ofOverlayNodeListener.close();
+        nodeConnectorListener.close();
+    }
+
+    /**
+     * Internal representation of the state of a connected switch
+     */
+    protected static final class SwitchState {
+
+        private NodeId nodeId;
+        private FlowCapableNode fcNode;
+        private OfOverlayNodeConfig nodeConfig;
+        private Map<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIid = Maps.newHashMap();
+
+        NodeConnectorId tunnelPort;
+        Set<NodeConnectorId> externalPorts = new HashSet<>();
+
+        SwitchStatus status;
+
+        public SwitchState(NodeId switchNode) {
+            super();
+            nodeId = switchNode;
+        }
+
+        /**
+         * Constructor used for tests
+         */
+        public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
+                OfOverlayNodeConfig nodeConfig) {
+            this.nodeId = node;
+            this.tunnelPort = tunnelPort;
+            this.externalPorts = externalPorts;
+            this.nodeConfig = nodeConfig;
+        }
+
+        private void update() {
+            HashSet<NodeConnectorId> externalPorts = new HashSet<>();
+            NodeConnectorId tunnelPort = null;
+            for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
+                FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
+                if (fcnc.getName() == null) {
+                    continue;
+                }
+                InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
+                NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
+                if (fcnc.getName().matches(".*(vxlan|tun).*")) {
+                    tunnelPort = ncId;
+                }
+                if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
+                    for (String pattern : nodeConfig.getExternalInterfaces()) {
+                        if (fcnc.getName().matches(pattern)) {
+                            externalPorts.add(ncId);
+                            break;
+                        }
+                    }
+                }
+            }
+            this.tunnelPort = tunnelPort;
+            this.externalPorts = Collections.unmodifiableSet(externalPorts);
+        }
+
+        private void updateStatus() {
+            boolean tunnelPortWithIpExists = tunnelPortWithIpExists();
+            if (fcNode != null) {
+                if (tunnelPortWithIpExists) {
+                    setStatus(SwitchStatus.READY);
+                } else {
+                    setStatus(SwitchStatus.PREPARING);
+                }
+            } else {
+                setStatus(SwitchStatus.DISCONNECTED);
+            }
+        }
+
+        private void setStatus(SwitchStatus newStatus) {
+            if (Objects.equal(status, newStatus)) {
+                return;
+            }
+            LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
+            this.status = newStatus;
+        }
+
+        private boolean tunnelPortWithIpExists() {
+            boolean tunnelPortWithIpExists = false;
+            if (tunnelPort != null && nodeConfig != null && nodeConfig.getTunnelIp() != null) {
+                tunnelPortWithIpExists = true;
+            }
+            LOG.trace("Status of tunnel on switch {} - tunnelPort: {} tunnelIp: {}", nodeId.getValue(),
+                    tunnelPort == null ? null : tunnelPort.getValue(),
+                    nodeConfig == null ? null : nodeConfig.getTunnelIp());
+            return tunnelPortWithIpExists;
+        }
+
+        public boolean isConfigurationEmpty() {
+            if (fcNode != null)
+                return false;
+            if (nodeConfig != null)
+                return false;
+            if (!fcncByNcIid.isEmpty())
+                return false;
+            return true;
+        }
+
+        public void setFlowCapableNode(FlowCapableNode fcNode) {
+            this.fcNode = fcNode;
+            LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
+            updateStatus();
+        }
+
+        public void setConfig(OfOverlayNodeConfig config) {
+            this.nodeConfig = config;
+            LOG.trace("Switch {} set {}", nodeId.getValue(), config);
+            update();
+            updateStatus();
+        }
+
+        public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
+            if (fcnc == null) {
+                fcncByNcIid.remove(ncIid);
+            } else {
+                fcncByNcIid.put(ncIid, fcnc);
+            }
+            LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
+                    ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
+            update();
+            updateStatus();
+        }
+
+    }
+
+    protected enum SwitchStatus {
+        /**
+         * The switch is not currently connected
+         */
+        DISCONNECTED,
+        /**
+         * The switch is connected but not yet configured
+         */
+        PREPARING,
+        /**
+         * The switch is ready to for policy rules to be installed
+         */
+        READY
+    }
+
+}
index 7e19881d1341ce50602337c7450406c2174c2c9e..4256f1f0b5de92ae890fbe99619fcc01e06ab611 100755 (executable)
@@ -14,8 +14,8 @@ import java.util.Map;
 \r
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockEndpointManager;\r
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockPolicyManager;\r
-import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockSwitchManager;\r
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;\r
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.MockSwitchManager;\r
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.AllowAction;\r
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Classifier;\r
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.L4Classifier;\r
similarity index 77%
rename from renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/MockSwitchManager.java
rename to renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/node/MockSwitchManager.java
index 1ab9e8d1f72d0b19e148a261a3d13a9d95250a8c..6dbc6458c543c31e34caed8274b3ac14459acc64 100644 (file)
@@ -6,10 +6,12 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.groupbasedpolicy.renderer.ofoverlay;
+package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node;
 
 import java.util.Set;
 
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchListener;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager;
 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;
@@ -20,14 +22,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 public class MockSwitchManager extends SwitchManager {
 
     public MockSwitchManager() {
-        super(null, null);
+        super(null);
     }
 
-    public void addSwitch(NodeId node, 
+    public void addSwitch(NodeId node,
                           NodeConnectorId tunnelPort,
                           Set<NodeConnectorId> externalPorts,
                           OfOverlayNodeConfig nodeConfig) {
-        SwitchState state = new SwitchState(node, tunnelPort, 
+        SwitchState state = new SwitchState(node, tunnelPort,
                                             externalPorts, nodeConfig);
         state.status = SwitchStatus.READY;
         switches.put(node, state);