Bug-7718 Operational neutron port status 54/51554/6
authorJosh <jhershbe@redhat.com>
Wed, 8 Feb 2017 11:27:26 +0000 (13:27 +0200)
committerSam Hague <shague@redhat.com>
Wed, 26 Apr 2017 22:44:33 +0000 (22:44 +0000)
networking-odl requires feedback as to when a neutron
port can be marked as "ACTIVE". This commit places an
estimation of that information in the operational data
store. For ports that connect to OVS we wait for them
to connect and for basic L2 flows to be configured.
Note that we do not validate the flows in operational
nor do we check all flows. Ports that are implemented
purely as flows are marked ACTIVE immediately. The plan
is for networking-odl to receive notifications of these
status changes via a websocket.

It was decided at the time being
to keep the port status out of the ODL neutron project
since (a) functionally, this issue is mainly an issue for
netvirt and (b) to do it right in netvirt would require
some re-architecting.

Change-Id: Id719e904b277fe4dbb9c3d118d24c3bedf110a33
Signed-off-by: Josh <jhershbe@redhat.com>
vpnservice/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/netvirt/elan/internal/ElanInterfaceManager.java
vpnservice/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/netvirt/neutronvpn/api/utils/NeutronUtils.java
vpnservice/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronPortChangeListener.java

index 4c4537ce2051f90c379c9b5b9d63d7a7840a1c35..2e9bf03f4a018cff9efc75d9aa52a1198bd25081 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. 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,
@@ -65,6 +65,7 @@ import org.opendaylight.netvirt.elan.utils.ElanForwardingEntriesHandler;
 import org.opendaylight.netvirt.elan.utils.ElanUtils;
 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
@@ -690,8 +691,8 @@ public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanIn
         List<StaticMacEntries> staticMacEntriesList = elanInterface.getStaticMacEntries();
         List<PhysAddress> staticMacAddresses = Lists.newArrayList();
 
+        boolean isInterfaceOperational = isOperational(interfaceInfo);
         if (ElanUtils.isNotEmpty(staticMacEntriesList)) {
-            boolean isInterfaceOperational = isOperational(interfaceInfo);
             for (StaticMacEntries staticMacEntry : staticMacEntriesList) {
                 InstanceIdentifier<MacEntry> macId = getMacEntryOperationalDataPath(elanInstanceName,
                         staticMacEntry.getMacAddress());
@@ -729,6 +730,10 @@ public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanIn
         }
         futures.add(ElanUtils.waitForTransactionToComplete(tx));
         futures.add(ElanUtils.waitForTransactionToComplete(writeFlowGroupTx));
+        if (isInterfaceOperational) {
+            //At this point, the interface is operational and D/SMAC flows have been configured, mark the port active
+            NeutronUtils.updatePortStatus(elanInterface.getName(), NeutronUtils.PORT_STATUS_ACTIVE, broker);
+        }
     }
 
     protected void removeInterfaceStaticMacEntries(String elanInstanceName, String interfaceName,
index 6eb144542e530810daaff0eea579da7f9c5744e1..2185ecb6933c98b3bca59e6d71537472650d594e 100644 (file)
 
 package org.opendaylight.netvirt.neutronvpn.api.utils;
 
-import static org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants.VNIC_TYPE_NORMAL;
-
 import java.util.List;
 import java.util.Objects;
 
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeBase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.NetworkProviderExtension;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.neutron.networks.network.Segments;
-
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class NeutronUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(NeutronUtils.class);
+
+    public static final String VNIC_TYPE_NORMAL = "normal";
+    public static final String PORT_STATUS_ACTIVE = "ACTIVE";
+    public static final String PORT_STATUS_BUILD = "BUILD";
+    public static final String PORT_STATUS_DOWN = "DOWN";
+    public static final String PORT_STATUS_ERROR = "ERROR";
+    public static final String PORT_STATUS_NOTAPPLICABLE = "N/A";
+
+    /**
+     * Create a Neutron Port status entry in the operational data store.
+     * @param uuid The uuid of the Neutron port
+     * @param portStatus value to set the status (see constants above)
+     * @param dataBroker DataBroker instance
+     * @return true if transaction submitted successfully
+     */
+    public static boolean createPortStatus(String uuid, String portStatus, DataBroker dataBroker) {
+        return writePortStatus(uuid, portStatus, dataBroker, true);
+    }
+
+    /**
+     * Update a Neutron Port status entry in the operational data store.
+     * @param uuid The uuid of the Neutron port
+     * @param portStatus value to set the status (see constants above)
+     * @param dataBroker DataBroker instance
+     * @return true if transaction submitted successfully
+     */
+    public static boolean updatePortStatus(String uuid, String portStatus, DataBroker dataBroker) {
+        return writePortStatus(uuid, portStatus, dataBroker, false);
+    }
+
+    private static boolean writePortStatus(String uuid, String portStatus, DataBroker dataBroker, boolean create) {
+        Uuid uuidObj = new Uuid(uuid);
+        PortBuilder portBuilder = new PortBuilder();
+        portBuilder.setUuid(uuidObj);
+        portBuilder.setStatus(portStatus);
+
+        InstanceIdentifier iid = InstanceIdentifier.create(Neutron.class).child(Ports.class).child(
+                                                                            Port.class, new PortKey(uuidObj));
+        SingleTransactionDataBroker tx = new SingleTransactionDataBroker(dataBroker);
+        try {
+            if (create) {
+                tx.syncWrite(LogicalDatastoreType.OPERATIONAL, iid, portBuilder.build());
+            } else {
+                tx.syncUpdate(LogicalDatastoreType.OPERATIONAL, iid, portBuilder.build());
+            }
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("writePortStatus: failed neutron port status write. isCreate ? " + create, e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+    * Delete a Neutron Port status entry from the operational data store.
+    * @param uuid The uuid of the Neutron port
+    * @param dataBroker DataBroker instance
+    * @return true if transaction submitted successfully
+    */
+    public static boolean deletePortStatus(String uuid, DataBroker dataBroker) {
+        Uuid uuidObj = new Uuid(uuid);
+
+        InstanceIdentifier iid = InstanceIdentifier.create(Neutron.class).child(Ports.class).child(
+                Port.class, new PortKey(uuidObj));
+        SingleTransactionDataBroker tx = new SingleTransactionDataBroker(dataBroker);
+        try {
+            tx.syncDelete(LogicalDatastoreType.OPERATIONAL, iid);
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("deletePortStatus: failed neutron port status delete", e);
+            return false;
+        }
+
+        return true;
+    }
 
     public static boolean isPortVnicTypeNormal(Port port) {
         PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
index 81e1c14297600a5d143ca7c786c3fb46832aa7a5..e095485bdbbcdd7d7bf9cfb662acda1732799ecb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright © 2015 - 2017 Ericsson India Global Services Pvt Ltd. 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,
@@ -115,21 +115,27 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
         }
         NeutronvpnUtils.addToPortCache(input);
 
+        String portStatus = NeutronUtils.PORT_STATUS_DOWN;
         if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
             if (input.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {
                 handleRouterInterfaceAdded(input);
+                NeutronUtils.createPortStatus(input.getUuid().getValue(), NeutronUtils.PORT_STATUS_ACTIVE, dataBroker);
                 return;
             }
             if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())) {
                 handleRouterGatewayUpdated(input);
+                portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
             } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
                 handleFloatingIpPortUpdated(null, input);
+                portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
             }
 
             if (input.getFixedIps() != null && !input.getFixedIps().isEmpty()) {
                 handleNeutronPortCreated(input);
             }
         }
+
+        NeutronUtils.createPortStatus(input.getUuid().getValue(), portStatus, dataBroker);
     }
 
     @Override
@@ -144,6 +150,7 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
             return;
         }
         NeutronvpnUtils.removeFromPortCache(input);
+        NeutronUtils.deletePortStatus(input.getUuid().getValue(), dataBroker);
 
         if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
             if (input.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {