Use component config for the default private gateway MAC

Change-Id: Ie0d17d6c78f4c13093a7b7ec6032f9f6e3e7c3e6
diff --git a/src/main/java/org/opencord/cordvtn/api/Constants.java b/src/main/java/org/opencord/cordvtn/api/Constants.java
index d6d2831..6730cf3 100644
--- a/src/main/java/org/opencord/cordvtn/api/Constants.java
+++ b/src/main/java/org/opencord/cordvtn/api/Constants.java
@@ -15,8 +15,6 @@
  */
 package org.opencord.cordvtn.api;
 
-import org.onlab.packet.Ip4Address;
-
 /**
  * Provides constants used in CORD VTN services.
  */
@@ -37,6 +35,5 @@
     public static final String DEFAULT_OF_PROTOCOL = "tcp";
     public static final int DEFAULT_OF_PORT = 6653;
     public static final int DEFAULT_OVSDB_PORT = 6640;
-    public static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
-    public static final int DHCP_INFINITE_LEASE = -1;
+    public static final String DEFAULT_GATEWAY_MAC_STR = "00:00:00:00:00:01";
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java b/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
index aa90093..bb4ce5e 100644
--- a/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
+++ b/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
@@ -54,6 +54,7 @@
 
     protected final Logger log = getLogger(getClass());
 
+    @Deprecated
     private static final String PRIVATE_GATEWAY_MAC = "privateGatewayMac";
     private static final String PUBLIC_GATEWAYS = "publicGateways";
     private static final String GATEWAY_IP = "gatewayIp";
@@ -106,7 +107,6 @@
         }
 
         // check all mandatory fields are present and valid
-        result = result && isMacAddress(PRIVATE_GATEWAY_MAC, MANDATORY);
         result = result && isIpPrefix(LOCAL_MANAGEMENT_IP, MANDATORY);
 
         for (JsonNode node : object.get(CORDVTN_NODES)) {
@@ -242,16 +242,6 @@
     }
 
     /**
-     * Returns private network gateway MAC address.
-     *
-     * @return mac address
-     */
-    public MacAddress privateGatewayMac() {
-        JsonNode jsonNode = object.get(PRIVATE_GATEWAY_MAC);
-        return MacAddress.valueOf(jsonNode.asText());
-    }
-
-    /**
      * Returns public network gateway IP and MAC address pairs.
      *
      * @return map of ip and mac address
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
index e8b4775..4dc69a2 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
@@ -15,10 +15,13 @@
  */
 package org.opencord.cordvtn.impl;
 
+import com.google.common.base.Strings;
 import com.google.common.collect.Maps;
 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.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.ARP;
@@ -27,6 +30,8 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
@@ -52,15 +57,18 @@
 import org.opencord.cordvtn.api.core.ServiceNetworkListener;
 import org.opencord.cordvtn.api.core.ServiceNetworkService;
 import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
+import java.util.Dictionary;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.opencord.cordvtn.api.Constants.DEFAULT_GATEWAY_MAC_STR;
 import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -71,6 +79,8 @@
 public class CordVtnArpProxy {
     protected final Logger log = getLogger(getClass());
 
+    private static final String PRIVATE_GATEWAY_MAC = "privateGatewayMac";
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
@@ -81,7 +91,10 @@
     protected HostService hostService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigService configService;
+    protected NetworkConfigService netConfigService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService compConfigService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnNodeManager nodeManager;
@@ -89,10 +102,14 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ServiceNetworkService snetService;
 
+    @Property(name = PRIVATE_GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
+            label = "Fake MAC address for virtual network gateway")
+    private String privateGatewayMacStr = DEFAULT_GATEWAY_MAC_STR;
+    private MacAddress privateGatewayMac = MacAddress.valueOf(privateGatewayMacStr);
+
     private final PacketProcessor packetProcessor = new InternalPacketProcessor();
     private final Map<IpAddress, MacAddress> gateways = Maps.newConcurrentMap();
 
-    private MacAddress privateGatewayMac = MacAddress.NONE;
     private NetworkConfigListener configListener = new InternalConfigListener();
     private ServiceNetworkListener snetListener = new InternalServiceNetworkListener();
     private ApplicationId appId;
@@ -100,24 +117,42 @@
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
-        configService.addListener(configListener);
-        readConfiguration();
+        compConfigService.registerProperties(getClass());
+
+        netConfigService.addListener(configListener);
+        readPublicGateways();
+        snetService.addListener(snetListener);
+        readPrivateGateways();
 
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
         requestPacket();
 
-        snetService.addListener(snetListener);
-        snetService.serviceNetworks().stream()
-                .filter(net -> net.type() == PRIVATE || net.type() == VSG)
-                .filter(net -> net.serviceIp() != null)
-                .forEach(net -> addGateway(net.serviceIp(), privateGatewayMac));
+        log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
-        snetService.removeListener(snetListener);
         packetService.removeProcessor(packetProcessor);
-        configService.removeListener(configListener);
+        snetService.removeListener(snetListener);
+        netConfigService.removeListener(configListener);
+        compConfigService.unregisterProperties(getClass(), false);
+
+        log.info("Stopped");
+    }
+
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        String updatedMac;
+
+        updatedMac = Tools.get(properties, PRIVATE_GATEWAY_MAC);
+        if (!Strings.isNullOrEmpty(updatedMac) &&
+                !updatedMac.equals(privateGatewayMacStr)) {
+            privateGatewayMacStr = updatedMac;
+            privateGatewayMac = MacAddress.valueOf(privateGatewayMacStr);
+        }
+
+        log.info("Modified");
     }
 
     /**
@@ -150,6 +185,13 @@
                 Optional.empty());
     }
 
+    private void readPrivateGateways() {
+        snetService.serviceNetworks().stream()
+                .filter(net -> net.type() == PRIVATE || net.type() == VSG)
+                .filter(net -> net.serviceIp() != null)
+                .forEach(net -> addGateway(net.serviceIp(), privateGatewayMac));
+    }
+
     /**
      * Adds a given gateway IP and MAC address to this ARP proxy.
      *
@@ -157,9 +199,9 @@
      * @param gatewayMac gateway mac address
      */
     private void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
-        checkNotNull(gatewayIp);
+        checkNotNull(gatewayIp, "Gateway IP address cannot be null");
         checkArgument(gatewayMac != null && gatewayMac != MacAddress.NONE,
-                      "privateGatewayMac is not configured");
+                      "Gateway MAC address cannot be null or NONE");
 
         MacAddress existing = gateways.get(gatewayIp);
         if (existing != null && !existing.equals(privateGatewayMac) &&
@@ -300,7 +342,7 @@
         }
 
         Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
-        instances.stream().forEach(instance -> {
+        instances.forEach(instance -> {
             TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                     .setOutput(instance.portNumber())
                     .build();
@@ -420,17 +462,14 @@
         }
     }
 
-    private void readConfiguration() {
-        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
+    private void readPublicGateways() {
+        CordVtnConfig config = netConfigService.getConfig(appId, CordVtnConfig.class);
         if (config == null) {
             log.debug("No configuration found");
             return;
         }
-        // TODO handle the case that private gateway MAC is changed
-        privateGatewayMac = config.privateGatewayMac();
-        log.debug("Set default service IP MAC address {}", privateGatewayMac);
 
-        config.publicGateways().entrySet().stream().forEach(entry -> {
+        config.publicGateways().entrySet().forEach(entry -> {
             addGateway(entry.getKey(), entry.getValue());
         });
         // TODO send gratuitous arp in case the MAC is changed
@@ -449,7 +488,7 @@
             switch (event.type()) {
                 case CONFIG_ADDED:
                 case CONFIG_UPDATED:
-                    readConfiguration();
+                    readPublicGateways();
                     break;
                 default:
                     break;
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
index d78be9b..efb882b 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
@@ -15,11 +15,14 @@
  */
 package org.opencord.cordvtn.impl;
 
+import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.common.primitives.Bytes;
 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.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.DHCP;
@@ -32,14 +35,13 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -51,24 +53,25 @@
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.opencord.cordvtn.api.Constants;
-import org.opencord.cordvtn.api.CordVtnConfig;
 import org.opencord.cordvtn.api.core.Instance;
 import org.opencord.cordvtn.api.core.ServiceNetworkService;
 import org.opencord.cordvtn.api.net.NetworkId;
 import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.Dictionary;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static org.onlab.packet.DHCP.DHCPOptionCode.*;
 import static org.onlab.packet.DHCPPacketType.DHCPACK;
 import static org.onlab.packet.DHCPPacketType.DHCPOFFER;
+import static org.opencord.cordvtn.api.Constants.DEFAULT_GATEWAY_MAC_STR;
 import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.BIDIRECTIONAL;
 import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.MANAGEMENT_HOST;
 import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.MANAGEMENT_LOCAL;
@@ -82,11 +85,13 @@
 
     protected final Logger log = getLogger(getClass());
 
-    private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
-    private static final byte PACKET_TTL = (byte) 127;
-    // TODO add MTU, static route option codes to ONOS DHCP and remove here
+    private static final String DHCP_SERVER_MAC = "dhcpServerMac";
+
     private static final byte DHCP_OPTION_MTU = (byte) 26;
     private static final byte DHCP_OPTION_CLASSLESS_STATIC_ROUTE = (byte) 121;
+
+    private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
+    private static final byte DEFAULT_PACKET_TTL = (byte) 127;
     private static final byte[] DHCP_DATA_LEASE_INFINITE =
             ByteBuffer.allocate(4).putInt(-1).array();
     private static final byte[] DHCP_DATA_MTU_DEFAULT =
@@ -96,7 +101,7 @@
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigService configService;
+    protected ComponentConfigService configService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
@@ -107,31 +112,46 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ServiceNetworkService snetService;
 
+    @Property(name = DHCP_SERVER_MAC, value = DEFAULT_GATEWAY_MAC_STR,
+            label = "Fake MAC address for DHCP server interface")
+    private String dhcpServerMac = DEFAULT_GATEWAY_MAC_STR;
+
     private final PacketProcessor packetProcessor = new InternalPacketProcessor();
-    private final NetworkConfigListener configListener = new InternalConfigListener();
 
     private ApplicationId appId;
-    private MacAddress dhcpServerMac = MacAddress.NONE;
 
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
-        configService.addListener(configListener);
-        readConfiguration();
-
+        configService.registerProperties(getClass());
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
         requestPackets();
+
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
-        configService.removeListener(configListener);
         packetService.removeProcessor(packetProcessor);
+        configService.unregisterProperties(getClass(), false);
         cancelPackets();
+
         log.info("Stopped");
     }
 
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        String updatedMac;
+
+        updatedMac = Tools.get(properties, DHCP_SERVER_MAC);
+        if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) {
+            dhcpServerMac = updatedMac;
+        }
+
+        log.info("Modified");
+    }
+
     private void requestPackets() {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
@@ -245,9 +265,6 @@
 
         private Ethernet buildReply(Ethernet ethRequest, byte packetType,
                                     Instance reqInstance) {
-            checkArgument(!dhcpServerMac.equals(MacAddress.NONE),
-                          "DHCP server MAC is not set");
-
             ServiceNetwork snet = snetService.serviceNetwork(reqInstance.netId());
             Ip4Address serverIp = snet.serviceIp().getIp4Address();
 
@@ -260,7 +277,7 @@
             IPv4 ipv4Reply = new IPv4();
             ipv4Reply.setSourceAddress(serverIp.toInt());
             ipv4Reply.setDestinationAddress(reqInstance.ipAddress().toInt());
-            ipv4Reply.setTtl(PACKET_TTL);
+            ipv4Reply.setTtl(DEFAULT_PACKET_TTL);
 
             UDP udpRequest = (UDP) ipv4Request.getPayload();
             UDP udpReply = new UDP();
@@ -402,7 +419,7 @@
                     .filter(Objects::nonNull)
                     .collect(Collectors.toSet());
 
-            providers.stream().forEach(provider -> {
+            providers.forEach(provider -> {
                 result.add((byte) provider.subnet().prefixLength());
                 result.addAll(getSignificantOctets(provider.subnet()));
                 result.addAll(router);
@@ -413,7 +430,7 @@
                     .filter(net -> isBidirectionalProvider(net, snet.id()))
                     .collect(Collectors.toSet());
 
-            subscribers.stream().forEach(subscriber -> {
+            subscribers.forEach(subscriber -> {
                 result.add((byte) subscriber.subnet().prefixLength());
                 result.addAll(getSignificantOctets(subscriber.subnet()));
                 result.addAll(router);
@@ -425,8 +442,7 @@
         private boolean isBidirectionalProvider(ServiceNetwork snet, NetworkId targetNetId) {
             return snet.providers().entrySet().stream()
                     .filter(p -> Objects.equals(p.getKey(), targetNetId))
-                    .filter(p -> p.getValue() == BIDIRECTIONAL)
-                    .findAny().isPresent();
+                    .anyMatch(p -> p.getValue() == BIDIRECTIONAL);
         }
 
         private List<Byte> getSignificantOctets(IpPrefix ipPrefix) {
@@ -438,35 +454,4 @@
             return Bytes.asList(result);
         }
     }
-
-    private void readConfiguration() {
-        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
-        if (config == null) {
-            log.debug("No configuration found");
-            return;
-        }
-        dhcpServerMac = config.privateGatewayMac();
-        log.debug("Set default DHCP server MAC address {}", dhcpServerMac);
-    }
-
-    private class InternalConfigListener implements NetworkConfigListener {
-
-        @Override
-        public boolean isRelevant(NetworkConfigEvent event) {
-            return event.configClass().equals(CordVtnConfig.class);
-        }
-
-        @Override
-        public void event(NetworkConfigEvent event) {
-
-            switch (event.type()) {
-                case CONFIG_ADDED:
-                case CONFIG_UPDATED:
-                    readConfiguration();
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
 }