CORD-484 Support management network for a VM

Change-Id: I866ae3ae7d839cd29f1732605170f3dfbab402bb
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordService.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordService.java
index f877630..97651b7 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordService.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordService.java
@@ -38,7 +38,8 @@
         PRIVATE_DIRECT,
         PRIVATE_INDIRECT,
         PUBLIC_DIRECT,
-        PUBLIC_INDIRECT
+        PUBLIC_INDIRECT,
+        MANAGEMENT
     }
 
     private final CordServiceId id;
@@ -165,7 +166,7 @@
      * It assumes that network name contains network type.
      *
      * @param netName network name
-     * @return network type, or null if it doesn't match any type
+     * @return network type, or PRIVATE if it doesn't match any type
      */
     private ServiceType getServiceType(String netName) {
         checkNotNull(netName);
@@ -179,10 +180,10 @@
             return PUBLIC_DIRECT;
         } else if (name.contains(PUBLIC_INDIRECT.toString())) {
             return PUBLIC_INDIRECT;
-        } else if (name.contains(PRIVATE.toString())) {
-            return PRIVATE;
+        } else if (name.contains(MANAGEMENT.toString())) {
+            return MANAGEMENT;
         } else {
-            return null;
+            return PRIVATE;
         }
     }
 }
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
index 19c9e7d..1dc094b 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -403,7 +403,13 @@
                  host.ipAddresses().stream().findFirst().get());
 
         CordService service = getCordService(vNet);
-        if (service != null) {
+        if (service == null) {
+            return;
+        }
+
+        if (service.serviceType().equals(CordService.ServiceType.MANAGEMENT)) {
+            ruleInstaller.populateManagementNetworkRules(host, service);
+        } else {
             // TODO check if the service needs an update on its group buckets after done CORD-433
             ruleInstaller.updateServiceGroup(service);
             arpProxy.addServiceIp(service.serviceIp());
@@ -440,7 +446,13 @@
         ruleInstaller.removeBasicConnectionRules(host);
 
         CordService service = getCordService(vNet);
-        if (service != null) {
+        if (service == null) {
+            return;
+        }
+
+        if (service.serviceType().equals(CordService.ServiceType.MANAGEMENT)) {
+            ruleInstaller.removeManagementNetworkRules(host, service);
+        } else {
             // TODO check if the service needs an update on its group buckets after done CORD-433
             ruleInstaller.updateServiceGroup(service);
 
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
index ad9dee8..c3a2a5b 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
@@ -103,10 +103,11 @@
     private static final int TABLE_DST_IP = 4;
     private static final int TABLE_TUNNEL_IN = 5;
 
+    private static final int MANAGEMENT_PRIORITY = 55000;
+    private static final int HIGH_PRIORITY = 50000;
     private static final int DEFAULT_PRIORITY = 5000;
-    private static final int LOWER_PRIORITY = 4000;
+    private static final int LOW_PRIORITY = 4000;
     private static final int LOWEST_PRIORITY = 0;
-    private static final int HIGHER_PRIORITY = 50000;
 
     private static final int VXLAN_UDP_PORT = 4789;
 
@@ -387,6 +388,126 @@
     }
 
     /**
+     * Populates flow rules for management network access.
+     *
+     * @param host host which has management network interface
+     * @param mService management network service
+     */
+    public void populateManagementNetworkRules(Host host, CordService mService) {
+        checkNotNull(mService);
+
+        DeviceId deviceId = host.location().deviceId();
+        IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP)
+                .matchArpTpa(mService.serviceIp().getIp4Address())
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(PortNumber.LOCAL)
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(MANAGEMENT_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_FIRST)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+
+        selector = DefaultTrafficSelector.builder()
+                .matchInPort(PortNumber.LOCAL)
+                .matchEthType(Ethernet.TYPE_ARP)
+                .matchArpTpa(hostIp.getIp4Address())
+                .build();
+
+        treatment = DefaultTrafficTreatment.builder()
+                .setOutput(host.location().port())
+                .build();
+
+        flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(MANAGEMENT_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_FIRST)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+
+        selector = DefaultTrafficSelector.builder()
+                .matchInPort(PortNumber.LOCAL)
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(mService.serviceIpRange())
+                .build();
+
+        treatment = DefaultTrafficTreatment.builder()
+                .transition(TABLE_DST_IP)
+                .build();
+
+        flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(MANAGEMENT_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_FIRST)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(mService.serviceIp().toIpPrefix())
+                .build();
+
+        treatment = DefaultTrafficTreatment.builder()
+                .setOutput(PortNumber.LOCAL)
+                .build();
+
+        flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(MANAGEMENT_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_ACCESS_TYPE)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+    }
+
+    /**
+     * Removes management network access rules.
+     *
+     * @param host host to be removed
+     * @param mService service for management network
+     */
+    public void removeManagementNetworkRules(Host host, CordService mService) {
+        checkNotNull(mService);
+
+        for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
+            if (flowRule.deviceId().equals(host.location().deviceId())) {
+                PortNumber port = getOutputFromTreatment(flowRule);
+                if (port != null && port.equals(host.location().port())) {
+                    processFlowRule(false, flowRule);
+                }
+            }
+
+            // TODO remove the other rules if mgmt network is not in use
+        }
+    }
+
+    /**
      * Populates default rules on the first table.
      * The rules are for shuttling vxlan-encapped packets and supporting physical
      * network connectivity.
@@ -409,7 +530,7 @@
                 .fromApp(appId)
                 .withSelector(selector)
                 .withTreatment(treatment)
-                .withPriority(HIGHER_PRIORITY)
+                .withPriority(HIGH_PRIORITY)
                 .forDevice(deviceId)
                 .forTable(TABLE_FIRST)
                 .makePermanent()
@@ -433,7 +554,7 @@
                 .fromApp(appId)
                 .withSelector(selector)
                 .withTreatment(treatment)
-                .withPriority(HIGHER_PRIORITY)
+                .withPriority(HIGH_PRIORITY)
                 .forDevice(deviceId)
                 .forTable(TABLE_FIRST)
                 .makePermanent()
@@ -456,7 +577,7 @@
                 .fromApp(appId)
                 .withSelector(selector)
                 .withTreatment(treatment)
-                .withPriority(HIGHER_PRIORITY)
+                .withPriority(HIGH_PRIORITY)
                 .forDevice(deviceId)
                 .forTable(TABLE_FIRST)
                 .makePermanent()
@@ -479,7 +600,7 @@
                 .fromApp(appId)
                 .withSelector(selector)
                 .withTreatment(treatment)
-                .withPriority(HIGHER_PRIORITY)
+                .withPriority(HIGH_PRIORITY)
                 .forDevice(deviceId)
                 .forTable(TABLE_FIRST)
                 .makePermanent()
@@ -633,7 +754,7 @@
                 .fromApp(appId)
                 .withSelector(selector)
                 .withTreatment(treatment)
-                .withPriority(LOWER_PRIORITY)
+                .withPriority(LOW_PRIORITY)
                 .forDevice(deviceId)
                 .forTable(TABLE_IN_PORT)
                 .makePermanent()
@@ -665,7 +786,7 @@
                     .fromApp(appId)
                     .withSelector(selector)
                     .withTreatment(treatment)
-                    .withPriority(LOWER_PRIORITY)
+                    .withPriority(LOW_PRIORITY)
                     .forDevice(device.id())
                     .forTable(TABLE_ACCESS_TYPE)
                     .makePermanent()
@@ -1006,6 +1127,25 @@
     }
 
     /**
+     * Returns the output port number from a given flow rule.
+     *
+     * @param flowRule flow rule
+     * @return port number, or null if the rule does not have output instruction
+     */
+    private PortNumber getOutputFromTreatment(FlowRule flowRule) {
+        Instruction instruction = flowRule.treatment().allInstructions().stream()
+                .filter(inst -> inst instanceof  Instructions.OutputInstruction)
+                .findFirst()
+                .orElse(null);
+
+        if (instruction == null) {
+            return null;
+        }
+
+        return ((Instructions.OutputInstruction) instruction).port();
+    }
+
+    /**
      * Creates a new group for a given service.
      *
      * @param deviceId device id to create a group