Add configuration to pick type of VC

Change-Id: Id0fb3dadeffc19de4bf96a79de7044e34240e830
diff --git a/sdx-l2/src/main/java/org/onosproject/sdxl2/SdxL2ArpNdpHandler.java b/sdx-l2/src/main/java/org/onosproject/sdxl2/SdxL2ArpNdpHandler.java
index 48e78b9..6100763 100644
--- a/sdx-l2/src/main/java/org/onosproject/sdxl2/SdxL2ArpNdpHandler.java
+++ b/sdx-l2/src/main/java/org/onosproject/sdxl2/SdxL2ArpNdpHandler.java
@@ -45,7 +45,6 @@
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketService;
-import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
 import java.util.Iterator;
@@ -55,26 +54,29 @@
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import static org.onosproject.security.AppGuard.checkPermission;
 import static org.onosproject.security.AppPermission.Type.PACKET_WRITE;
-import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Implementation of ARP and NDP handler based on ProxyArpManager.
  */
 public class SdxL2ArpNdpHandler {
 
-    private final Logger log = getLogger(SdxL2ArpNdpHandler.class);
-
     private static final String MSG_NULL = "ARP or NDP message cannot be null.";
 
-    protected IntentService intentService;
+    private IntentService intentService;
 
-    protected ApplicationId applicationId;
+    private ApplicationId applicationId;
 
-    protected PacketService packetService;
+    private PacketService packetService;
 
-    public static String vcType;
+    private static String vcType;
 
-
+    /**
+     * Creates an ARP/NDP packet handler.
+     *
+     * @param intentService IntentService object
+     * @param packetService PacketService object
+     * @param applicationId ApplicationId object
+     */
     public SdxL2ArpNdpHandler(IntentService intentService,
                               PacketService packetService, ApplicationId applicationId) {
         this.intentService = intentService;
@@ -390,7 +392,6 @@
         }
 
         /**
-         * /**
          * Builds TrafficSelector for MAC
          * based tunnels.
          *
@@ -407,7 +408,23 @@
             }
             return selectorBuilder.build();
         }
-
     }
 
+    /**
+     * Retrieves the SDX-L2 VC type.
+     *
+     * @return VC type
+     */
+    public static String getVcType() {
+        return vcType;
+    }
+
+    /**
+     * Sets up the SDX-L2 VC type.
+     *
+     * @param vcType VC type
+     */
+    public static void setVcType(String vcType) {
+        SdxL2ArpNdpHandler.vcType = vcType;
+    }
 }
diff --git a/sdx-l2/src/main/java/org/onosproject/sdxl2/SdxL2Manager.java b/sdx-l2/src/main/java/org/onosproject/sdxl2/SdxL2Manager.java
index edd7901..69b3510 100644
--- a/sdx-l2/src/main/java/org/onosproject/sdxl2/SdxL2Manager.java
+++ b/sdx-l2/src/main/java/org/onosproject/sdxl2/SdxL2Manager.java
@@ -19,12 +19,15 @@
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.Property;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.ICMP6;
 import org.onlab.packet.IPv6;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
@@ -32,7 +35,9 @@
 import org.onosproject.net.edge.EdgePortService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.PacketContext;
@@ -43,12 +48,15 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Iterator;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onlab.packet.Ethernet.TYPE_ARP;
 import static org.onlab.packet.Ethernet.TYPE_IPV6;
 import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
@@ -65,13 +73,17 @@
 public class SdxL2Manager implements SdxL2Service {
 
     private static final String SDXL2_APP = "org.onosproject.sdxl2";
+    private static final String ERROR_ADD_VC_CPS = "Unable to find %s and %s in sdxl2=%s";
     private static final String ERROR_ADD_VC_VLANS =
             "Cannot create VC when CPs have different number of VLANs";
     private static final String ERROR_ADD_VC_VLANS_CLI =
             "\u001B[0;31mError executing command: " + ERROR_ADD_VC_VLANS + "\u001B[0;49m";
     private static final String VC_0 = "MAC";
+    @Property(name = "VirtualCircuitType", value = VC_0, label = "Tunnel mechanism for Virtual Circuits")
+    private String vcType = VC_0;
+    private String previousvcType = vcType;
+
     private static Logger log = LoggerFactory.getLogger(SdxL2Manager.class);
-    private static final String ERROR_ADD_VC_CPS = "Unable to find %s and %s in sdxl2=%s";
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected SdxL2Store sdxL2Store;
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -81,7 +93,10 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EdgePortService edgePortService;
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
+
     protected SdxL2Processor processor = new SdxL2Processor();
     protected ApplicationId appId;
     protected SdxL2MonitoringService monitoringManager;
@@ -97,8 +112,11 @@
     protected void activate(ComponentContext context) {
         appId = coreService.registerApplication(SDXL2_APP);
         monitoringManager = new SdxL2MonitoringManager(appId, intentService, edgePortService);
-        SdxL2ArpNdpHandler.vcType = VC_0;
-        vcManager = new SdxL2MacVCManager(appId, sdxL2Store, intentService);
+        SdxL2ArpNdpHandler.setVcType(VC_0);
+        vcManager = buildVCManager();
+        cfgService.registerProperties(getClass());
+        readComponentConfiguration(context);
+        changeVCTunnel();
         handleArpNdp();
         log.info("Started");
     }
@@ -108,11 +126,120 @@
      */
     @Deactivate
     protected void deactivate() {
-        this.cleanSdxL2();
+        this.cleanup();
         unhandleArpNdp();
         log.info("Stopped");
     }
 
+    @Modified
+    public void modified(ComponentContext context) {
+        readComponentConfiguration(context);
+        changeVCTunnel();
+    }
+
+    /**
+     * Determines if the currently used VC is active or not.
+     *
+     * @return Boolean value indicating whether VC is active (true) or not (false).
+     */
+    private boolean isCircuitActive() {
+        Iterator<Intent> intents = intentService.getIntents().iterator();
+        Intent intent;
+        while (intents.hasNext()) {
+            intent = intents.next();
+            if (intent.appId().equals(this.appId) &&
+                    intentService.getIntentState(intent.key()) == IntentState.INSTALLED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Checks if property name is defined and set to True.
+     *
+     * @param properties properties to be looked up
+     * @param propertyName the name of the property to look up
+     * @return value when the propertyName is defined or return null
+     */
+    private static String isTunnelEnabled(Dictionary<?, ?> properties,
+                                          String propertyName) {
+        String value = null;
+        try {
+            String s = (String) properties.get(propertyName);
+            value = isNullOrEmpty(s) ? null : s.trim();
+            if (value != null &&
+                    !value.equals("MAC") &&
+                    !value.equals("VLAN") &&
+                    !value.equals("MPLS")) {
+                value = null;
+            }
+        } catch (ClassCastException e) {
+            // No propertyName defined.
+        }
+        return value;
+    }
+
+    /**
+     * Extracts properties from the component configuration context.
+     *
+     * @param context the component context
+     */
+    private void readComponentConfiguration(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        String tunnel;
+        tunnel = isTunnelEnabled(properties, "VirtualCircuitType");
+        if (tunnel == null) {
+            log.info("Tunnel mechanism for VC is not configured, " +
+                             "using current value {}", vcType);
+        } else {
+            vcType = tunnel;
+            log.info("Configured. Tunnel mechanism is {}", vcType);
+        }
+    }
+
+    /**
+     * Changes the tunnel mechanism for the VCs.
+     */
+    private void changeVCTunnel() {
+        if (previousvcType.equals(vcType)) {
+            log.info("Tunnel mechanism has not been changed");
+            return;
+        }
+        if (isCircuitActive()) {
+            log.info("Change of tunnels not allowed - there are active circuits");
+            vcType = previousvcType;
+            return;
+        }
+        previousvcType = vcType;
+        SdxL2ArpNdpHandler.setVcType(vcType);
+        vcManager = buildVCManager();
+    }
+
+    /**
+     * Builds a VC depending on the internal variable "vcType", previously set-up.
+     * Default circuit is through VLANs
+     *
+     * @return VirtualCircuit object of the selected type
+     */
+    private SdxL2VCService buildVCManager() {
+        SdxL2VCService manager;
+        switch (vcType) {
+            case "MAC":
+                manager = new SdxL2MacVCManager(appId, sdxL2Store, intentService);
+                break;
+            case "MPLS":
+                manager = new SdxL2MplsVCManager(appId, sdxL2Store, intentService);
+                break;
+            default:
+            case "VLAN":
+                manager = new SdxL2VlanVCManager(appId, sdxL2Store, intentService);
+                break;
+        }
+        return manager;
+    }
+
     @Override
     public void createSdxL2(String sdxl2) {
 
@@ -290,7 +417,16 @@
      */
     @Override
     public void cleanSdxL2() {
+        Set<String> sdxl2s = this.getSdxL2s();
+        sdxl2s.forEach(sdxl2 -> this.deleteSdxL2(sdxl2));
+    }
+
+    /**
+     * Stops monitoring and unregister configuration service.
+     */
+    public void cleanup() {
         this.monitoringManager.cleanup();
+        this.cfgService.unregisterProperties(getClass(), false);
     }
 
     /**
@@ -298,7 +434,7 @@
      * and registers the SDX-L2 PacketProcessor.
      */
     private void handleArpNdp() {
-        SdxL2ArpNdpHandler.vcType = VC_0;
+        SdxL2ArpNdpHandler.setVcType(VC_0);
         arpndpHandler = new SdxL2ArpNdpHandler(intentService, packetService, appId);
         packetService.addProcessor(processor, PacketProcessor.director(1));