Add ability to specify controllers on command line

Change-Id: Ic88189dbf33d834a79597f108dd283ab478c9923
diff --git a/trellis/README.md b/trellis/README.md
index d59b649..3600075 100644
--- a/trellis/README.md
+++ b/trellis/README.md
@@ -56,25 +56,6 @@
 ## ONOS - Network Config
 `onos-netcfg <onos-ip> routing/trellis/trellis.json`
 
-
-## Update Controller IP
-The location of ONOS controller needs to be updated in several places, including
-Mininet script and zebra config.
-
-In `routing/trellis/trellis.py`
-```
-net.addController(RemoteController('c0', ip='192.168.56.11'))
-net.addController(RemoteController('c1', ip='192.168.56.12'))
-net.addController(RemoteController('c2', ip='192.168.56.13'))
-```
-
-In `routing/trellis/zebradbgp1.conf`
-Note: This ONOS IP need to be reachable from Mininet emulated Quagga host.
-127.0.0.1 is invalid.
-```
-fpm connection ip 192.168.56.11 port 2620
-```
-
 ## Disable/Modify AppArmor
 The apparmor will set dhcpd in enforce mode. We will need to disable the profile.
 ```
@@ -93,7 +74,7 @@
 ## Start Mininet Emulation
 ```
 cd routing/trellis
-sudo ./trellis.py
+sudo ./trellis.py --controllers ONOS_CONTROLLER_IP1, ONOS_CONTROLLER_IP2,...,ONOS_CONTROLLER_IPN
 ```
 
 ## Verify Network Connectivity
@@ -122,13 +103,10 @@
 vagrant ssh
 ```
 
-Now. Follow above instructions to modify the IP address of `trellis.py` and `zebradbgp*.conf.
-and then start the mininet to test
+Now start mininet to test
 ```
 cd routing/trellis
-vim trellis.py
-vim zebradbgp1.conf
-sudo ./trellis.py
+sudo ./trellis.py --controllers ONOS_CONTROLLER_IP1, ONOS_CONTROLLER_IP2,...,ONOS_CONTROLLER_IPN
 ```
 
 # Troubleshooting
diff --git a/trellis/trellis.py b/trellis/trellis.py
index 2e9d8bc..7208aa4 100755
--- a/trellis/trellis.py
+++ b/trellis/trellis.py
@@ -12,6 +12,7 @@
 from routinglib import BgpRouter
 from routinglib import RoutedHost, RoutedHost6
 from trellislib import DhcpClient, Dhcp6Client, DhcpRelay, DhcpServer, Dhcp6Server
+from trellislib import get_mininet, parse_trellis_args, set_up_zebra_config
 from functools import partial
 
 class Trellis( Topo ):
@@ -109,12 +110,10 @@
 if __name__ == "__main__":
     setLogLevel('debug')
     topo = Trellis()
-
     switch = partial(OVSSwitch, protocols='OpenFlow13')
-    net = Mininet(topo=topo, controller=None, switch=switch)
-    net.addController(RemoteController('c0', ip='192.168.56.11'))
-    net.addController(RemoteController('c1', ip='192.168.56.12'))
-    net.addController(RemoteController('c2', ip='192.168.56.13'))
+    arguments = parse_trellis_args()
+    set_up_zebra_config(arguments.controllers)
+    net = get_mininet(arguments, topo, switch)
 
     net.start()
     CLI(net)
diff --git a/trellis/trellis_dualhome.py b/trellis/trellis_dualhome.py
index 4d2b744..56c4599 100755
--- a/trellis/trellis_dualhome.py
+++ b/trellis/trellis_dualhome.py
@@ -14,6 +14,7 @@
 from routinglib import RoutedHost, RoutedHost6
 from trellislib import DhcpClient, Dhcp6Client, DhcpRelay, DhcpServer, Dhcp6Server
 from trellislib import DualHomedDhcpClient
+from trellislib import get_mininet, parse_trellis_args, set_up_zebra_config
 from functools import partial
 
 class Trellis( Topo ):
@@ -177,12 +178,10 @@
 if __name__ == "__main__":
     setLogLevel('debug')
     topo = Trellis()
-
     switch = partial(OVSSwitch, protocols='OpenFlow13')
-    net = Mininet(topo=topo, controller=None, switch=switch)
-    #net.addController(RemoteController('c0', ip='192.168.56.11'))
-    #net.addController(RemoteController('c1', ip='192.168.56.12'))
-    #net.addController(RemoteController('c2', ip='192.168.56.13'))
+    arguments = parse_trellis_args()
+    set_up_zebra_config(arguments.controllers)
+    net = get_mininet(arguments, topo, switch)
 
     net.start()
     CLI(net)
diff --git a/trellis/trellis_duallink.py b/trellis/trellis_duallink.py
index df0243c..da6b399 100755
--- a/trellis/trellis_duallink.py
+++ b/trellis/trellis_duallink.py
@@ -12,6 +12,7 @@
 from routinglib import BgpRouter
 from routinglib import RoutedHost, RoutedHost6
 from trellislib import DhcpClient, Dhcp6Client, DhcpRelay, DhcpServer, Dhcp6Server
+from trellislib import get_mininet, parse_trellis_args, set_up_zebra_config
 from functools import partial
 
 class Trellis( Topo ):
@@ -116,12 +117,10 @@
 if __name__ == "__main__":
     setLogLevel('debug')
     topo = Trellis()
-
     switch = partial(OVSSwitch, protocols='OpenFlow13')
-    net = Mininet(topo=topo, controller=None, switch=switch)
-    net.addController(RemoteController('c0', ip='192.168.56.11'))
-    net.addController(RemoteController('c1', ip='192.168.56.12'))
-    net.addController(RemoteController('c2', ip='192.168.56.13'))
+    arguments = parse_trellis_args()
+    set_up_zebra_config(arguments.controllers)
+    net = get_mininet(arguments, topo, switch)
 
     net.start()
     CLI(net)
diff --git a/trellis/trellis_hag.py b/trellis/trellis_hag.py
index b981f00..d78bd9d 100755
--- a/trellis/trellis_hag.py
+++ b/trellis/trellis_hag.py
@@ -3,7 +3,6 @@
 import sys
 sys.path.append('..')
 from mininet.topo import Topo
-from mininet.net import Mininet
 from mininet.cli import CLI
 from mininet.log import setLogLevel
 from mininet.node import RemoteController, OVSBridge, Host, OVSSwitch
@@ -14,10 +13,12 @@
 from routinglib import RoutedHost, RoutedHost6
 from trellislib import DhcpClient, Dhcp6Client, DhcpRelay, DhcpServer, Dhcp6Server
 from trellislib import DualHomedDhcpClient
+from trellislib import get_mininet, parse_trellis_args, set_up_zebra_config
 from functools import partial
 
+
 class Trellis( Topo ):
-    "Trellis basic topology"
+    """Trellis HAG topology"""
 
     def __init__( self, *args, **kwargs ):
         Topo.__init__( self, *args, **kwargs )
@@ -210,28 +211,27 @@
         rpd6 = self.addHost('rpd6', cls=DhcpClient, mac='00:dd:00:00:00:02')
         self.addLink(rpd5, s207)
         self.addLink(rpd6, s208)
-        
+
         # IPv6 Hosts - RPDs
         rpd5v6 = self.addHost('rpd5v6', cls=Dhcp6Client, mac='00:ee:00:00:00:01')
         rpd6v6 = self.addHost('rpd6v6', cls=Dhcp6Client, mac='00:ee:00:00:00:02')
         self.addLink(rpd5v6, s207)
         self.addLink(rpd6v6, s208)
-        
 
 
-        
+
+
 
 topos = { 'trellis' : Trellis }
 
 if __name__ == "__main__":
     setLogLevel('debug')
-    topo = Trellis()
 
+    topo = Trellis()
     switch = partial(OVSSwitch, protocols='OpenFlow13')
-    net = Mininet(topo=topo, controller=None, switch=switch)
-    #net.addController(RemoteController('c0', ip='10.192.19.161'))
-    #net.addController(RemoteController('c1', ip='10.192.19.162'))
-    #net.addController(RemoteController('c2', ip='10.192.19.163'))
+    arguments = parse_trellis_args()
+    set_up_zebra_config(arguments.controllers)
+    net = get_mininet(arguments, topo, switch)
 
     net.start()
     CLI(net)
diff --git a/trellis/trellis_remote_dhcp.py b/trellis/trellis_remote_dhcp.py
index 1942dd2..fbc1c7f 100755
--- a/trellis/trellis_remote_dhcp.py
+++ b/trellis/trellis_remote_dhcp.py
@@ -12,6 +12,7 @@
 from routinglib import BgpRouter
 from routinglib import RoutedHost
 from trellislib import DhcpClient, DhcpServer, DhcpRelay
+from trellislib import get_mininet, parse_trellis_args, set_up_zebra_config
 from functools import partial
 
 class Trellis( Topo ):
@@ -117,12 +118,10 @@
 if __name__ == "__main__":
     setLogLevel('debug')
     topo = Trellis()
-
     switch = partial(OVSSwitch, protocols='OpenFlow13')
-    net = Mininet(topo=topo, controller=None, switch=switch)
-    net.addController(RemoteController('c0', ip='192.168.56.11'))
-    net.addController(RemoteController('c1', ip='192.168.56.12'))
-    net.addController(RemoteController('c2', ip='192.168.56.13'))
+    arguments = parse_trellis_args()
+    set_up_zebra_config(arguments.controllers)
+    net = get_mininet(arguments, topo, switch)
 
     net.start()
     CLI(net)
diff --git a/trellis/trellis_vlan.py b/trellis/trellis_vlan.py
index 0000f62..89b3a3d 100755
--- a/trellis/trellis_vlan.py
+++ b/trellis/trellis_vlan.py
@@ -13,6 +13,7 @@
 from routinglib import RoutedHost
 from trellislib import DhcpClient, DhcpServer
 from trellislib import TaggedDhcpClient, TaggedDhcpServer
+from trellislib import get_mininet, parse_trellis_args, set_up_zebra_config
 from functools import partial
 
 class Trellis( Topo ):
@@ -105,12 +106,10 @@
 if __name__ == "__main__":
     setLogLevel('debug')
     topo = Trellis()
-
     switch = partial(OVSSwitch, protocols='OpenFlow13')
-    net = Mininet(topo=topo, controller=None, switch=switch)
-    net.addController(RemoteController('c0', ip='192.168.56.11'))
-    net.addController(RemoteController('c1', ip='192.168.56.12'))
-    net.addController(RemoteController('c2', ip='192.168.56.13'))
+    arguments = parse_trellis_args()
+    set_up_zebra_config(arguments.controllers)
+    net = get_mininet(arguments, topo, switch)
 
     net.start()
     CLI(net)
diff --git a/trellis/trellislib.py b/trellis/trellislib.py
index 8cf3c6c..23bc5c3 100644
--- a/trellis/trellislib.py
+++ b/trellis/trellislib.py
@@ -6,8 +6,10 @@
 
 import sys
 sys.path.append('..')
-from mininet.node import Host
+from mininet.node import Host, RemoteController
 from routinglib import RoutedHost, RoutedHost6, Router
+import argparse
+from mininet.net import Mininet
 
 class TaggedRoutedHost(RoutedHost):
     """Host that can be configured with multiple IP addresses."""
@@ -204,3 +206,52 @@
     low2 = mac_value & 0xffff
 
     return 'fe80::{:04x}:{:02x}ff:fe{:02x}:{:04x}'.format(high2, high1, low1, low2)
+
+# Parses Trellis parameters
+def parse_trellis_args():
+    parser = argparse.ArgumentParser(description="Trellis Arguments")
+    parser.add_argument("-c", "--controllers", help = "Comma Separated List of ONOS controllers",
+                        required = True, default = "")
+    return parser.parse_args()
+
+# Gets a mininet instance
+def get_mininet(arguments, topo, switch):
+    net = Mininet(topo=topo, controller=None, switch=switch)
+
+    if arguments.controllers:
+        controllers = arguments.controllers.split(',')
+        controller_number = 0
+        for controller in controllers:
+            net.addController(RemoteController('c' + str(controller_number), ip=controller))
+            controller_number += 1
+    return net
+
+# Generates the Zebra config files
+def set_up_zebra_config(controllers_string):
+    zebra_config = "log file /var/log/quagga/zebradbgp{}.log\n" \
+                   "hostname zebra-bgp{}\n" \
+                   "password quagga\n" \
+                    "!\n" \
+                    "! Default route via virtual management switch\n" \
+                    "!\n" \
+                    "ip route 0.0.0.0/0 172.16.0.1\n" \
+                    "!\n" \
+                    "fpm connection ip {} port 2620\n"
+    controllers = controllers_string.split(',')
+
+    controller1 = controllers[0]
+    if (len(controllers) > 1):
+        controller2 = controllers[1]
+    else:
+        controller2 = controller1
+
+
+    zebra1 = zebra_config.format("1", "1", controller1)
+    zebra2 = zebra_config.format("2", "2", controller2)
+
+    with open("zebradbgp1.conf", "w") as config_file_1:
+        config_file_1.write(zebra1)
+
+    with open("zebradbgp2.conf", "w") as config_file_2:
+        config_file_2.write(zebra2)
+
diff --git a/trellis/zebradbgp1.conf b/trellis/zebradbgp1.conf
deleted file mode 100644
index 51991a4..0000000
--- a/trellis/zebradbgp1.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-log file /var/log/quagga/zebradbgp1.log
-hostname zebra-bgp1
-password quagga
-!
-! Default route via virtual management switch
-!
-ip route 0.0.0.0/0 172.16.0.1
-!
-fpm connection ip 192.168.56.11 port 2620
diff --git a/trellis/zebradbgp2.conf b/trellis/zebradbgp2.conf
deleted file mode 100644
index dce218d..0000000
--- a/trellis/zebradbgp2.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-log file /var/log/quagga/zebradbgp2.log
-hostname zebra-bgp2
-password quagga
-!
-! Default route via virtual management switch
-!
-ip route 0.0.0.0/0 172.16.0.1
-!
-fpm connection ip 192.168.56.11 port 2620