This update add two arguments to voltha when it gets started:
   1) inter-core-subnet: the subnet to connect to for inter core communication
   2) pon-subnet: the subnet to connect to for PON communication

This requires that the voltha_net is created with a specified subnet and that
subnet is specified in the docker compose file, e.g.:

docker network create --driver overlay --subnet 10.0.1.0/24 voltha_net

And in the compose.yml file add the following options:

      - --inter-core-subnet=10.0.1.0/24
      - --pon-subnet=10.0.1.0/24

This update guarantees that the voltha instance is connected to the correct
network.

Change-Id: I5d29ab54282c4ba9aff5ba165fdb37352cfaa0fd
diff --git a/common/utils/nethelpers.py b/common/utils/nethelpers.py
index a082eeb..a4610a2 100644
--- a/common/utils/nethelpers.py
+++ b/common/utils/nethelpers.py
@@ -21,8 +21,17 @@
 from netifaces import AF_INET
 
 import netifaces as ni
+import netaddr
 
-def get_my_primary_interface():
+
+def _get_all_interfaces():
+    m_interfaces = []
+    for iface in ni.interfaces():
+        m_interfaces.append((iface, ni.ifaddresses(iface)))
+    return m_interfaces
+
+
+def _get_my_primary_interface():
     gateways = ni.gateways()
     assert 'default' in gateways, \
         ("No default gateway on host/container, "
@@ -34,7 +43,33 @@
     return interface_name
 
 
-def get_my_primary_local_ipv4(ifname=None):
+def get_my_primary_local_ipv4(inter_core_subnet=None, ifname=None):
+    if not inter_core_subnet:
+        return _get_my_primary_local_ipv4(ifname)
+    # My IP should belong to the specified subnet
+    for iface in ni.interfaces():
+        m_ip = ni.ifaddresses(iface)[AF_INET][0]['addr']
+        _ip = netaddr.IPAddress(m_ip).value
+        m_network = netaddr.IPNetwork(inter_core_subnet)
+        if _ip >= m_network.first and _ip <= m_network.last:
+            return m_ip
+    return None
+
+
+def get_my_primary_interface(pon_subnet=None):
+    if not pon_subnet:
+        return _get_my_primary_interface()
+    # My interface should have an IP that belongs to the specified subnet
+    for iface in ni.interfaces():
+        m_ip = ni.ifaddresses(iface)[AF_INET][0]['addr']
+        m_ip = netaddr.IPAddress(m_ip).value
+        m_network = netaddr.IPNetwork(pon_subnet)
+        if m_ip >= m_network.first and m_ip <= m_network.last:
+            return iface
+    return None
+
+
+def _get_my_primary_local_ipv4(ifname=None):
     try:
         ifname = get_my_primary_interface() if ifname is None else ifname
         addresses = ni.ifaddresses(ifname)
@@ -43,6 +78,5 @@
     except Exception as e:
         return None
 
-
 if __name__ == '__main__':
     print get_my_primary_local_ipv4()
diff --git a/compose/docker-compose-voltha-swarm.yml b/compose/docker-compose-voltha-swarm.yml
index 39532f7..ff03c67 100644
--- a/compose/docker-compose-voltha-swarm.yml
+++ b/compose/docker-compose-voltha-swarm.yml
@@ -15,6 +15,9 @@
       - --instance-id-is-container-name
       - --interface=eth2
       - --backend=consul
+      - --inter-core-subnet=10.0.1.0/24
+      - --pon-subnet=10.0.1.0/24
+
     networks:
       - net
     ports:
@@ -26,5 +29,6 @@
 
 networks:
   net:
-    external:
-      name: voltha_net
+     external:
+       name: voltha_net
+
diff --git a/requirements.txt b/requirements.txt
index 7009e4e..bb31099 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,6 +15,7 @@
 jsonpatch>=1.14
 kafka_python>=1.3.1
 klein>=15.3.1
+netaddr>=0.7.18
 networkx>=1.11
 nose>=1.3.7
 nose-exclude>=0.5.0
diff --git a/voltha/main.py b/voltha/main.py
index 9eb12d4..c5cb300 100755
--- a/voltha/main.py
+++ b/voltha/main.py
@@ -52,11 +52,15 @@
 defs = dict(
     config=os.environ.get('CONFIG', './voltha.yml'),
     consul=os.environ.get('CONSUL', 'localhost:8500'),
-    external_host_address=os.environ.get('EXTERNAL_HOST_ADDRESS', None),
+    inter_core_subnet=os.environ.get('INTER_CORE_SUBNET', None),
+    pon_subnet=os.environ.get('PON_SUBNET', None),
+    external_host_address=os.environ.get('EXTERNAL_HOST_ADDRESS',
+                                         get_my_primary_local_ipv4()),
     fluentd=os.environ.get('FLUENTD', None),
     grpc_port=os.environ.get('GRPC_PORT', 50055),
     instance_id=os.environ.get('INSTANCE_ID', os.environ.get('HOSTNAME', '1')),
-    internal_host_address=os.environ.get('INTERNAL_HOST_ADDRESS', None),
+    internal_host_address=os.environ.get('INTERNAL_HOST_ADDRESS',
+                                         get_my_primary_local_ipv4()),
     interface=os.environ.get('INTERFACE', get_my_primary_interface()),
     rest_port=os.environ.get('REST_PORT', 8880),
     kafka=os.environ.get('KAFKA', 'localhost:9092'),
@@ -83,6 +87,23 @@
         default=defs['consul'],
         help=_help)
 
+
+    _help = ('<inter_core_subnet> is the subnet connecting all the voltha '
+             'instances in a cluster (default: %s)' % defs['inter_core_subnet'])
+    parser.add_argument('-V', '--inter-core-subnet',
+                        dest='inter_core_subnet',
+                        action='store',
+                        default=defs['inter_core_subnet'],
+                        help=_help)
+
+    _help = ('<pon subnet> is the subnet connecting the voltha instances'
+             'with the PON network (default: %s)' % defs['pon_subnet'])
+    parser.add_argument('-P', '--pon-subnet',
+                        dest='pon_subnet',
+                        action='store',
+                        default=defs['pon_subnet'],
+                        help=_help)
+
     _help = ('<hostname> or <ip> at which Voltha is reachable from outside '
              'the cluster (default: %s)' % defs['external_host_address'])
     parser.add_argument('-E', '--external-host-address',
@@ -204,14 +225,21 @@
     if args.instance_id_is_container_name:
         args.instance_id = get_my_containers_name()
 
-    m_ip = get_my_primary_local_ipv4(args.interface)
-    if not m_ip:
-        m_ip = get_my_primary_local_ipv4()
-    if not args.external_host_address:
+    """ 
+    The container external, internal IP and PON interface needs to be 
+    set based on the subnet data.  At this time the internal IP is not used. 
+    The external IP is used for inter-core communications.  If the subnets are
+    set then they take precedence over the other relevant arguments (
+    external and internal host as well as interface
+    """
+    if args.inter_core_subnet:
+        m_ip = get_my_primary_local_ipv4(inter_core_subnet=args.inter_core_subnet)
         args.external_host_address = m_ip
-    if not args.internal_host_address:
         args.internal_host_address = m_ip
 
+    if args.pon_subnet:
+        args.interface = get_my_primary_interface(args.pon_subnet)
+
     return args
 
 
@@ -291,7 +319,9 @@
         try:
             self.log.info('starting-internal-components',
                           internal_host=self.args.internal_host_address,
-                          external_host=self.args.external_host_address)
+                          external_host=self.args.external_host_address,
+                          interface=self.args.interface,
+                          consul=self.args.consul)
 
             registry.register('main', self)