Merge pull request #24 from stevenvanrossem/master
authorstevenvanrossem <steven.vanrossem@intec.ugent.be>
Wed, 17 Feb 2016 14:34:12 +0000 (15:34 +0100)
committerstevenvanrossem <steven.vanrossem@intec.ugent.be>
Wed, 17 Feb 2016 14:34:12 +0000 (15:34 +0100)
add network & monitoring cli

23 files changed:
README.md [changed mode: 0644->0755]
ansible/install.yml [changed mode: 0644->0755]
emuvim/api/__init__.py [changed mode: 0644->0755]
emuvim/api/zerorpcapi.py [changed mode: 0644->0755]
emuvim/api/zerorpcapi_DCNetwork.py [new file with mode: 0644]
emuvim/cli/__init__.py [changed mode: 0644->0755]
emuvim/cli/compute.py [changed mode: 0644->0755]
emuvim/cli/datacenter.py [changed mode: 0644->0755]
emuvim/cli/monitor.py [new file with mode: 0644]
emuvim/cli/network.py [changed mode: 0644->0755]
emuvim/cli/son-emu-cli
emuvim/dcemulator/__init__.py [changed mode: 0644->0755]
emuvim/dcemulator/link.py [changed mode: 0644->0755]
emuvim/dcemulator/monitoring.py [new file with mode: 0644]
emuvim/dcemulator/net.py [changed mode: 0644->0755]
emuvim/dcemulator/node.py [changed mode: 0644->0755]
emuvim/example_topology.py [changed mode: 0644->0755]
emuvim/test/__main__.py [changed mode: 0644->0755]
emuvim/test/runner.py [changed mode: 0644->0755]
emuvim/test/test_api_zerorpc.py [changed mode: 0644->0755]
emuvim/test/test_emulator.py [changed mode: 0644->0755]
start_dcnetwork [new file with mode: 0755]
start_example_chain [new file with mode: 0755]

old mode 100644 (file)
new mode 100755 (executable)
index f58d007..ebc6f6c
--- a/README.md
+++ b/README.md
@@ -48,12 +48,16 @@ Automatic installation is provide through Ansible playbooks.
  * `sudo python example_topology.py`
 * Second terminal:
  * `cd ~/son-emu/emuvim/cli`
- * `./son-emu-cli compute start -d dc1 -n vnf1`
- * `./son-emu-cli compute start -d dc1 -n vnf2`
+ * `./son-emu-cli compute start -d datacenter1 -n vnf1`
+ * `./son-emu-cli compute start -d datacenter1 -n vnf2`
  * `./son-emu-cli compute list`
 * First terminal:
  * `dockernet> vnf1 ping -c 2 vnf2`
 
+#### Example scripts:
+ * `./start_dcnetwork` starts an example datacenter network with monitoring api endpoint
+ * `./start_example_chain` sets up an example service chain, using the example docker container from `package_samples` https://github.com/sonata-nfv/packaging_samples/tree/master/VNFs
+
 ### Run Unit Tests
 * `cd ~/son-emu/emuvim`
 * `sudo python test` or `sudo python test -v` for more outputs
old mode 100644 (file)
new mode 100755 (executable)
index 6e73908..a374e0b
@@ -23,3 +23,9 @@
 
    - name: install argparse
      pip: name=argparse
+
+   - name: install networkx
+     pip: name=networkx
+
+   - name: install ryu
+     pip: name=ryu
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index de43423..fd814b3
@@ -56,13 +56,13 @@ class MultiDatacenterApi(object):
     def __init__(self, dcs):
         self.dcs = dcs
 
-    def compute_action_start(self, dc_label, compute_name, image, network):
+    def compute_action_start(self, dc_label, compute_name, image, command, network):
         # network e.g. {"ip": "10.0.0.254/8"}
         # TODO what to return UUID / given name / internal name ?
         logging.debug("RPC CALL: compute start")
         try:
             c = self.dcs.get(dc_label).startCompute(
-                compute_name, image=image, network=network)
+                compute_name, image=image, command=command, network=network)
             return str(c.name)
         except Exception as ex:
             logging.exception("RPC error.")
diff --git a/emuvim/api/zerorpcapi_DCNetwork.py b/emuvim/api/zerorpcapi_DCNetwork.py
new file mode 100644 (file)
index 0000000..24becd3
--- /dev/null
@@ -0,0 +1,113 @@
+"""\r
+Distributed Cloud Emulator (dcemulator)\r
+(c) 2015 by Manuel Peuster <manuel.peuster@upb.de>\r
+"""\r
+\r
+import logging\r
+import threading\r
+import zerorpc\r
+import site\r
+from subprocess import Popen\r
+\r
+logging.basicConfig(level=logging.INFO)\r
+\r
+\r
+class ZeroRpcApiEndpointDCNetwork(object):\r
+    """\r
+    Simple API endpoint that offers a zerorpc-based\r
+    interface. This interface will be used by the\r
+    default command line client.\r
+    It can be used as a reference to implement\r
+    REST interfaces providing the same semantics,\r
+    like e.g. OpenStack compute API.\r
+    """\r
+\r
+    def __init__(self, listenip, port, DCNetwork=None):\r
+        if DCNetwork :\r
+            self.connectDCNetwork(DCNetwork)\r
+        self.ip = listenip\r
+        self.port = port\r
+        logging.debug("Created monitoring API endpoint %s(%s:%d)" % (\r
+            self.__class__.__name__, self.ip, self.port))\r
+\r
+        # start Ryu controller with rest-API\r
+        python_install_path = site.getsitepackages()[0]\r
+        ryu_path = python_install_path + '/ryu/app/simple_switch_13.py'\r
+        ryu_path2 =  python_install_path + '/ryu/app/ofctl_rest.py'\r
+        ryu_cmd =  'ryu-manager'\r
+        self.ryu_process = Popen([ryu_cmd, ryu_path, ryu_path2])\r
+\r
+\r
+    def connectDCNetwork(self, net):\r
+        self.net = net\r
+        logging.info("Connected DCNetwork to API endpoint %s(%s:%d)" % (\r
+            self.__class__.__name__, self.ip, self.port))\r
+\r
+    def start(self):\r
+        thread = threading.Thread(target=self._api_server_thread, args=())\r
+        thread.daemon = True\r
+        thread.start()\r
+        logging.debug("Started API endpoint %s(%s:%d)" % (\r
+            self.__class__.__name__, self.ip, self.port))\r
+\r
+    def _api_server_thread(self):\r
+        s = zerorpc.Server(DCNetworkApi(self.net))\r
+        s.bind("tcp://%s:%d" % (self.ip, self.port))\r
+        s.run()\r
+\r
+    def stop(self):\r
+        # stop ryu controller\r
+        logging.info("Stop the monitoring API endpoint")\r
+        self.ryu_process.terminate()\r
+        #self.ryu_process.kill()\r
+        return\r
+\r
+\r
+class DCNetworkApi(object):\r
+    """\r
+        Just pass through the corresponding request to the\r
+        selected data center. Do not implement provisioning\r
+        logic here because will will have multiple API\r
+        endpoint implementations at the end.\r
+    """\r
+\r
+    def __init__(self, net):\r
+        self.net = net\r
+\r
+    def network_action_start(self, vnf_src_name, vnf_dst_name):\r
+        # call DCNetwork method, not really datacenter specific API for now...\r
+        # provided dc name needs to be part of API endpoint\r
+        # no check if vnfs are really connected to this datacenter...\r
+        logging.debug("RPC CALL: network chain start")\r
+        try:\r
+            c = self.net.setChain(\r
+                vnf_src_name, vnf_dst_name)\r
+            return str(c)\r
+        except Exception as ex:\r
+            logging.exception("RPC error.")\r
+            return ex.message\r
+\r
+    def network_action_stop(self, vnf_src_name, vnf_dst_name):\r
+        # call DCNetwork method, not really datacenter specific API for now...\r
+        # provided dc name needs to be part of API endpoint\r
+        # no check if vnfs are really connected to this datacenter...\r
+        logging.debug("RPC CALL: network chain stop")\r
+        try:\r
+            c = self.net.setChain(\r
+                vnf_src_name, vnf_dst_name, cmd='del-flows')\r
+            return c\r
+        except Exception as ex:\r
+            logging.exception("RPC error.")\r
+            return ex.message\r
+\r
+    # get egress(default) or ingress rate of a vnf\r
+    def monitor_get_rate(self, vnf_name, direction):\r
+        logging.debug("RPC CALL: get rate")\r
+        try:\r
+            c = self.net.monitor_agent.get_rate(vnf_name, direction)\r
+            return c\r
+        except Exception as ex:\r
+            logging.exception("RPC error.")\r
+            return ex.message\r
+\r
+\r
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 87247cb..15fea91
@@ -34,6 +34,7 @@ class ZeroRpcClient(object):
             args.get("datacenter"),
             args.get("name"),
             args.get("image"),
+            args.get("docker_command"),
             network)
         pp.pprint(r)
 
@@ -89,8 +90,11 @@ parser.add_argument(
     "--name", "-n", dest="name",
     help="Name of compute instance e.g. 'vnf1'")
 parser.add_argument(
-    "--image", dest="image",
+    "--image","-i", dest="image",
     help="Name of container image to be used e.g. 'ubuntu'")
+parser.add_argument(
+    "--dcmd", "-c", dest="docker_command",
+    help="Startup command of the container e.g. './start.sh'")
 parser.add_argument(
     "--net", dest="network",
     help="Network properties of compute instance e.g. '10.0.0.123/8'")
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/emuvim/cli/monitor.py b/emuvim/cli/monitor.py
new file mode 100644 (file)
index 0000000..6885a3c
--- /dev/null
@@ -0,0 +1,53 @@
+"""\r
+son-emu network CLI\r
+(c) 2016 by Manuel Peuster <manuel.peuster@upb.de>\r
+"""\r
+\r
+import argparse\r
+import pprint\r
+from tabulate import tabulate\r
+import zerorpc\r
+\r
+\r
+pp = pprint.PrettyPrinter(indent=4)\r
+\r
+class ZeroRpcClient(object):\r
+\r
+    def __init__(self):\r
+        self.c = zerorpc.Client()\r
+        # TODO connect to DCNetwork API\r
+        #self.c.connect("tcp://127.0.0.1:4242")  # TODO hard coded for now. we'll change this later\r
+        self.c.connect("tcp://127.0.0.1:5151")\r
+        self.cmds = {}\r
+\r
+    def execute_command(self, args):\r
+        if getattr(self, args["command"]) is not None:\r
+            # call the local method with the same name as the command arg\r
+            getattr(self, args["command"])(args)\r
+        else:\r
+            print "Command not implemented."\r
+\r
+    def get_rate(self, args):\r
+        r = self.c.monitor_get_rate(\r
+            args.get("vnf_name"),\r
+            args.get("direction"))\r
+        pp.pprint(r)\r
+\r
+\r
+parser = argparse.ArgumentParser(description='son-emu network')\r
+parser.add_argument(\r
+    "command",\r
+    help="Action to be executed: get_rate")\r
+parser.add_argument(\r
+    "--vnf_name", "-vnf", dest="vnf_name",\r
+    help="vnf name to be monitored")\r
+parser.add_argument(\r
+    "--direction", "-d", dest="direction",\r
+    help="in (ingress rate) or out (egress rate)")\r
+\r
+def main(argv):\r
+    print "This is the son-emu monitor CLI."\r
+    print "Arguments: %s" % str(argv)\r
+    args = vars(parser.parse_args(argv))\r
+    c = ZeroRpcClient()\r
+    c.execute_command(args)\r
old mode 100644 (file)
new mode 100755 (executable)
index 080b0ac..8d4219b
@@ -1,9 +1,62 @@
-"""
-son-emu network CLI
-(c) 2016 by Manuel Peuster <manuel.peuster@upb.de>
-"""
-
-
-def main(argv):
-    print "This is the son-emu network CLI."
-    print "Arguments: %s" % str(argv)
+"""\r
+son-emu network CLI\r
+(c) 2016 by Manuel Peuster <manuel.peuster@upb.de>\r
+"""\r
+\r
+import argparse\r
+import pprint\r
+from tabulate import tabulate\r
+import zerorpc\r
+\r
+\r
+pp = pprint.PrettyPrinter(indent=4)\r
+\r
+class ZeroRpcClient(object):\r
+\r
+    def __init__(self):\r
+        self.c = zerorpc.Client()\r
+        # TODO connect to DCNetwork API\r
+        #self.c.connect("tcp://127.0.0.1:4242")  # TODO hard coded for now. we'll change this later\r
+        self.c.connect("tcp://127.0.0.1:5151")\r
+        self.cmds = {}\r
+\r
+    def execute_command(self, args):\r
+        if getattr(self, args["command"]) is not None:\r
+            # call the local method with the same name as the command arg\r
+            getattr(self, args["command"])(args)\r
+        else:\r
+            print "Command not implemented."\r
+\r
+    def add(self, args):\r
+        r = self.c.network_action_start(\r
+            #args.get("datacenter"),\r
+            args.get("source"),\r
+            args.get("destination"))\r
+        pp.pprint(r)\r
+\r
+    def remove(self, args):\r
+        r = self.c.network_action_stop(\r
+            #args.get("datacenter"),\r
+            args.get("source"),\r
+            args.get("destination"))\r
+        pp.pprint(r)\r
+\r
+\r
+parser = argparse.ArgumentParser(description='son-emu network')\r
+parser.add_argument(\r
+    "command",\r
+    help="Action to be executed: add|remove")\r
+parser.add_argument(\r
+    "--datacenter", "-d", dest="datacenter",\r
+    help="Data center to in which the network action should be initiated")\r
+parser.add_argument(\r
+    "--source", "-src", dest="source",\r
+    help="vnf name of the source of the chain")\r
+parser.add_argument(\r
+    "--destination", "-dst", dest="destination",\r
+    help="vnf name of the destination of the chain")\r
+\r
+def main(argv):\r
+    args = vars(parser.parse_args(argv))\r
+    c = ZeroRpcClient()\r
+    c.execute_command(args)\r
index ba7a292..61cbd43 100755 (executable)
@@ -16,7 +16,7 @@ import sys
 import compute
 import network
 import datacenter
-
+import monitor
 
 def main():
     if len(sys.argv) < 2:
@@ -28,6 +28,8 @@ def main():
         network.main(sys.argv[2:])
     elif sys.argv[1] == "datacenter":
         datacenter.main(sys.argv[2:])
+    elif sys.argv[1] == "monitor":
+        monitor.main(sys.argv[2:])
 
 if __name__ == '__main__':
     main()
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/emuvim/dcemulator/monitoring.py b/emuvim/dcemulator/monitoring.py
new file mode 100644 (file)
index 0000000..094c09b
--- /dev/null
@@ -0,0 +1,62 @@
+__author__ = 'Administrator'\r
+\r
+import urllib2\r
+import logging\r
+from mininet.node import  OVSSwitch\r
+import ast\r
+logging.basicConfig(level=logging.INFO)\r
+\r
+"""\r
+class to read openflow stats from the Ryu controller of the DCNEtwork\r
+"""\r
+\r
+class DCNetworkMonitor():\r
+    def __init__(self, net):\r
+        self.net = net\r
+        # link to REST_API\r
+        self.ip = '0.0.0.0'\r
+        self.port = '8080'\r
+        self.REST_api = 'http://{0}:{1}'.format(self.ip,self.port)\r
+\r
+\r
+    def get_rate(self, vnf_name, direction='tx'):\r
+        try:\r
+            vnf_switch = self.net.DCNetwork_graph.neighbors(str(vnf_name))\r
+\r
+            if len(vnf_switch) > 1:\r
+                logging.info("vnf: {0} has multiple ports".format(vnf_name))\r
+                return\r
+            elif len(vnf_switch) == 0:\r
+                logging.info("vnf: {0} is not connected".format(vnf_name))\r
+                return\r
+            else:\r
+                vnf_switch = vnf_switch[0]\r
+            next_node = self.net.getNodeByName(vnf_switch)\r
+\r
+            if not isinstance( next_node, OVSSwitch ):\r
+                logging.info("vnf: {0} is not connected to switch".format(vnf_name))\r
+                return\r
+\r
+            mon_port = self.net.DCNetwork_graph[vnf_name][vnf_switch]['dst_port']\r
+            switch_dpid = x = int(str(next_node.dpid),16)\r
+\r
+            ret = self.REST_cmd('stats/port', switch_dpid)\r
+            port_stat_dict = ast.literal_eval(ret)\r
+            for port_stat in port_stat_dict[str(switch_dpid)]:\r
+                if port_stat['port_no'] == mon_port:\r
+                    return port_stat\r
+                    break\r
+\r
+            return ret\r
+\r
+        except Exception as ex:\r
+            logging.exception("get_txrate error.")\r
+            return ex.message\r
+\r
+\r
+\r
+    def REST_cmd(self, prefix, dpid):\r
+        url = self.REST_api + '/' + str(prefix) + '/' + str(dpid)\r
+        req = urllib2.Request(url)\r
+        ret = urllib2.urlopen(req).read()\r
+        return ret
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index 7b238b8..609d46c
@@ -5,10 +5,12 @@ Distributed Cloud Emulator (dcemulator)
 import logging
 
 from mininet.net import Dockernet
-from mininet.node import Controller, OVSKernelSwitch, Switch, Docker, Host
+from mininet.node import Controller, OVSSwitch, OVSKernelSwitch, Switch, Docker, Host, RemoteController
 from mininet.cli import CLI
-from mininet.log import setLogLevel, info
+from mininet.log import setLogLevel, info, debug
 from mininet.link import TCLink, Link
+import networkx as nx
+from monitoring import DCNetworkMonitor
 
 from node import Datacenter, EmulatorCompute
 
@@ -26,9 +28,16 @@ class DCNetwork(Dockernet):
         # create a Mininet/Dockernet network
         # call original Docker.__init__ and setup default controller
         Dockernet.__init__(
-            self, controller=Controller, switch=OVSKernelSwitch, **kwargs)
+            self, controller=RemoteController, switch=OVSKernelSwitch, **kwargs)
         self.addController('c0')
 
+        # graph of the complete DC network
+        self.DCNetwork_graph=nx.DiGraph()
+
+        # monitoring agent
+        self.monitor_agent = DCNetworkMonitor(self)
+
+
     def addDatacenter(self, label, metadata={}):
         """
         Create and add a logical cloud data center to the network.
@@ -74,14 +83,38 @@ class DCNetwork(Dockernet):
             if not "ip" in params["params2"]:
                 params["params2"]["ip"] = self.getNextIp()
 
-        return Dockernet.addLink(self, node1, node2, **params)  # TODO we need TCLinks with user defined performance here
+        link = Dockernet.addLink(self, node1, node2, **params)  # TODO we need TCLinks with user defined performance here
+
+        # add edge and assigned port number to graph in both directions between node1 and node2
+        self.DCNetwork_graph.add_edge(node1.name, node2.name, \
+                                      {'src_port': node1.ports[link.intf1], 'dst_port': node2.ports[link.intf2]})
+        self.DCNetwork_graph.add_edge(node2.name, node1.name, \
+                                       {'src_port': node2.ports[link.intf2], 'dst_port': node1.ports[link.intf1]})
+
+        return link
 
     def addDocker( self, label, **params ):
         """
         Wrapper for addDocker method to use custom container class.
         """
+        self.DCNetwork_graph.add_node(label)
         return Dockernet.addDocker(self, label, cls=EmulatorCompute, **params)
 
+    def removeDocker( self, label, **params ):
+        """
+        Wrapper for removeDocker method to update graph.
+        """
+        self.DCNetwork_graph.remove_node(label)
+        return Dockernet.removeDocker(self, label, **params)
+
+    def addSwitch( self, name, add_to_graph=True, **params ):
+        """
+        Wrapper for addSwitch method to store switch also in graph.
+        """
+        if add_to_graph:
+            self.DCNetwork_graph.add_node(name)
+        return Dockernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', **params)
+
     def getAllContainers(self):
         """
         Returns a list with all containers within all data centers.
@@ -102,3 +135,46 @@ class DCNetwork(Dockernet):
 
     def CLI(self):
         CLI(self)
+
+    # to remove chain do setChain( src, dst, cmd='del-flows')
+    def setChain(self, vnf_src_name, vnf_dst_name, cmd='add-flow'):
+        # get shortest path
+        path = nx.shortest_path(self.DCNetwork_graph, vnf_src_name, vnf_dst_name)
+        logging.info("Path between {0} and {1}: {2}".format(vnf_src_name, vnf_dst_name, path))
+
+        current_hop = vnf_src_name
+        for i in range(0,len(path)):
+            next_hop = path[path.index(current_hop)+1]
+            next_node = self.getNodeByName(next_hop)
+
+            if next_hop == vnf_dst_name:
+                return "path added between {0} and {1}".format(vnf_src_name, vnf_dst_name)
+            elif not isinstance( next_node, OVSSwitch ):
+                logging.info("Next node: {0} is not a switch".format(next_hop))
+                return "Next node: {0} is not a switch".format(next_hop)
+
+
+            switch_inport = self.DCNetwork_graph[current_hop][next_hop]['dst_port']
+            next2_hop = path[path.index(current_hop)+2]
+            switch_outport = self.DCNetwork_graph[next_hop][next2_hop]['src_port']
+
+            logging.info("add flow in switch: {0} in_port: {1} out_port: {2}".format(next_node.name, switch_inport, switch_outport))
+            # set of entry via ovs-ofctl
+            # TODO use rest API of ryu to set flow entries to correct witch dpid
+            if isinstance( next_node, OVSSwitch ):
+                match = 'in_port=%s' % switch_inport
+
+                if cmd=='add-flow':
+                    action = 'action=%s' % switch_outport
+                    s = ','
+                    ofcmd = s.join([match,action])
+                elif cmd=='del-flows':
+                    ofcmd = match
+                else:
+                    ofcmd=''
+
+                next_node.dpctl(cmd, ofcmd)
+
+            current_hop = next_hop
+
+        return "destination node: {0} not reached".format(vnf_dst_name)
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index 6c1ecf7..13007eb
@@ -99,7 +99,7 @@ class Datacenter(object):
     def start(self):
         pass
 
-    def startCompute(self, name, image=None, network=None):
+    def startCompute(self, name, image=None, command=None,network=None):
         """
         Create a new container as compute resource and connect it to this
         data center.
@@ -117,7 +117,7 @@ class Datacenter(object):
         if network is None:
             network = {}  # {"ip": "10.0.0.254/8"}
         # create the container and connect it to the given network
-        d = self.net.addDocker("%s" % (name), dimage=image)
+        d = self.net.addDocker("%s" % (name), dimage=image, dcmd=command)
         self.net.addLink(d, self.switch, params1=network)
         # do bookkeeping
         self.containers[name] = d
old mode 100644 (file)
new mode 100755 (executable)
index 3c53c58..9412863
@@ -20,6 +20,7 @@ import logging
 from mininet.log import setLogLevel
 from dcemulator.net import DCNetwork
 from api.zerorpcapi import ZeroRpcApiEndpoint
+from api.zerorpcapi_DCNetwork import ZeroRpcApiEndpointDCNetwork
 
 logging.basicConfig(level=logging.INFO)
 
@@ -30,6 +31,13 @@ def create_topology1():
     """
     net = DCNetwork()
 
+    """
+    1b. add a monitoring agent to the DCNetwork
+    """
+    mon_api = ZeroRpcApiEndpointDCNetwork("0.0.0.0", 5151)
+    mon_api.connectDCNetwork(net)
+    mon_api.start()
+
     """
     2. Add (logical) data centers to the topology
        (each data center is one "bigswitch" in our simplified
@@ -102,6 +110,8 @@ def create_topology1():
     net.start()
     net.CLI()
     # when the user types exit in the CLI, we stop the emulator
+    # we need to explicitly stop the monitoring api, so the Ryu controller is also terminated
+    mon_api.stop()
     net.stop()
 
 
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/start_dcnetwork b/start_dcnetwork
new file mode 100755 (executable)
index 0000000..aca405b
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# start DC Network
+cd emuvim/
+python example_topology.py
+
+
diff --git a/start_example_chain b/start_example_chain
new file mode 100755 (executable)
index 0000000..935a766
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+
+# deploy VNFs
+cd emuvim/cli/
+./son-emu-cli compute start -d datacenter1 -n tsrc -i traffic_source -c ./start.sh
+./son-emu-cli compute start -d datacenter2 -n fw -i firewall -c ./start.sh
+./son-emu-cli compute start -d long_data_center_name3 -n tsink -i traffic_sink -c ./start.sh
+
+# setup links in the chain
+./son-emu-cli network add -src tsrc -dst fw
+./son-emu-cli network add -src fw -dst tsink
+./son-emu-cli network add -src tsink -dst tsrc
+
+
+