Merge pull request #10 from mpeuster/master
authorpeusterm <manuel.peuster@uni-paderborn.de>
Thu, 4 Feb 2016 15:40:32 +0000 (16:40 +0100)
committerpeusterm <manuel.peuster@uni-paderborn.de>
Thu, 4 Feb 2016 15:40:32 +0000 (16:40 +0100)
added CLI to fetch datacenter information. also started CLI documentation in the wiki and linked it from README.md

README.md
emuvim/api/zerorpcapi.py
emuvim/cli/compute.py
emuvim/cli/datacenter.py [new file with mode: 0644]
emuvim/cli/son-emu-cli
emuvim/dcemulator/net.py
emuvim/dcemulator/node.py
emuvim/example_topology.py
emuvim/test/test_emulator.py

index a0fa397..f58d007 100644 (file)
--- a/README.md
+++ b/README.md
@@ -58,6 +58,9 @@ Automatic installation is provide through Ansible playbooks.
 * `cd ~/son-emu/emuvim`
 * `sudo python test` or `sudo python test -v` for more outputs
 
+### CLI
+* [Full CLI command documentation](https://github.com/sonata-nfv/son-emu/wiki/CLI-Command-Overview)
+
 
 ### TODO
 * DCemulator
index bbb95ca..de43423 100644 (file)
@@ -56,30 +56,30 @@ class MultiDatacenterApi(object):
     def __init__(self, dcs):
         self.dcs = dcs
 
-    def compute_action_start(self, dc_name, compute_name, image, network):
+    def compute_action_start(self, dc_label, compute_name, image, 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_name).startCompute(
+            c = self.dcs.get(dc_label).startCompute(
                 compute_name, image=image, network=network)
             return str(c.name)
         except Exception as ex:
             logging.exception("RPC error.")
             return ex.message
 
-    def compute_action_stop(self, dc_name, compute_name):
+    def compute_action_stop(self, dc_label, compute_name):
         logging.debug("RPC CALL: compute stop")
         try:
-            return self.dcs.get(dc_name).stopCompute(compute_name)
+            return self.dcs.get(dc_label).stopCompute(compute_name)
         except Exception as ex:
             logging.exception("RPC error.")
             return ex.message
 
-    def compute_list(self, dc_name):
+    def compute_list(self, dc_label):
         logging.debug("RPC CALL: compute list")
         try:
-            if dc_name is None:
+            if dc_label is None:
                 # return list with all compute nodes in all DCs
                 all_containers = []
                 for dc in self.dcs.itervalues():
@@ -89,16 +89,32 @@ class MultiDatacenterApi(object):
             else:
                 # return list of compute nodes for specified DC
                 return [(c.name, c.getStatus())
-                        for c in self.dcs.get(dc_name).listCompute()]
+                        for c in self.dcs.get(dc_label).listCompute()]
         except Exception as ex:
             logging.exception("RPC error.")
             return ex.message
 
-    def compute_status(self, dc_name, compute_name):
+    def compute_status(self, dc_label, compute_name):
         logging.debug("RPC CALL: compute status")
         try:
             return self.dcs.get(
-                dc_name).containers.get(compute_name).getStatus()
+                dc_label).containers.get(compute_name).getStatus()
+        except Exception as ex:
+            logging.exception("RPC error.")
+            return ex.message
+
+    def datacenter_list(self):
+        logging.debug("RPC CALL: datacenter list")
+        try:
+            return [d.getStatus() for d in self.dcs.itervalues()]
+        except Exception as ex:
+            logging.exception("RPC error.")
+            return ex.message
+
+    def datacenter_status(self, dc_label):
+        logging.debug("RPC CALL: datacenter status")
+        try:
+                return self.dcs.get(dc_label).getStatus()
         except Exception as ex:
             logging.exception("RPC error.")
             return ex.message
index df40814..87247cb 100644 (file)
@@ -80,7 +80,8 @@ class ZeroRpcClient(object):
 parser = argparse.ArgumentParser(description='son-emu compute')
 parser.add_argument(
     "command",
-    help="Action to be executed: start|stop|list")
+    choices=['start', 'stop', 'list', 'status'],
+    help="Action to be executed.")
 parser.add_argument(
     "--datacenter", "-d", dest="datacenter",
     help="Data center to in which the compute instance should be executed")
diff --git a/emuvim/cli/datacenter.py b/emuvim/cli/datacenter.py
new file mode 100644 (file)
index 0000000..c3850fc
--- /dev/null
@@ -0,0 +1,66 @@
+"""
+son-emu datacenter CLI
+(c) 2016 by Manuel Peuster <manuel.peuster@upb.de>
+"""
+
+import argparse
+import pprint
+from tabulate import tabulate
+import zerorpc
+
+
+pp = pprint.PrettyPrinter(indent=4)
+
+
+class ZeroRpcClient(object):
+
+    def __init__(self):
+        self.c = zerorpc.Client()
+        self.c.connect("tcp://127.0.0.1:4242")  # TODO hard coded for now. we'll change this later
+        self.cmds = {}
+
+    def execute_command(self, args):
+        if getattr(self, args["command"]) is not None:
+            # call the local method with the same name as the command arg
+            getattr(self, args["command"])(args)
+        else:
+            print "Command not implemented."
+
+    def list(self, args):
+        r = self.c.datacenter_list()
+        table = []
+        for d in r:
+            # for each dc add a line to the output table
+            if len(d) > 0:
+                table.append([d.get("label"),
+                              d.get("internalname"),
+                              d.get("switch"),
+                              d.get("n_running_containers"),
+                              len(d.get("metadata"))])
+        headers = ["Label",
+                   "Internal Name",
+                   "Switch",
+                   "# Containers",
+                   "# Metadata Items"]
+        print tabulate(table, headers=headers, tablefmt="grid")
+
+    def status(self, args):
+        r = self.c.datacenter_status(
+            args.get("datacenter"))
+        pp.pprint(r)
+
+
+parser = argparse.ArgumentParser(description='son-emu datacenter')
+parser.add_argument(
+    "command",
+    choices=['list', 'status'],
+    help="Action to be executed.")
+parser.add_argument(
+    "--datacenter", "-d", dest="datacenter",
+    help="Data center to which the command should be applied.")
+
+
+def main(argv):
+    args = vars(parser.parse_args(argv))
+    c = ZeroRpcClient()
+    c.execute_command(args)
index 56fe58b..ba7a292 100755 (executable)
@@ -15,6 +15,7 @@
 import sys
 import compute
 import network
+import datacenter
 
 
 def main():
@@ -25,6 +26,8 @@ def main():
         compute.main(sys.argv[2:])
     elif sys.argv[1] == "network":
         network.main(sys.argv[2:])
+    elif sys.argv[1] == "datacenter":
+        datacenter.main(sys.argv[2:])
 
 if __name__ == '__main__':
     main()
index 2c7e106..7b238b8 100644 (file)
@@ -29,13 +29,13 @@ class DCNetwork(Dockernet):
             self, controller=Controller, switch=OVSKernelSwitch, **kwargs)
         self.addController('c0')
 
-    def addDatacenter(self, label):
+    def addDatacenter(self, label, metadata={}):
         """
         Create and add a logical cloud data center to the network.
         """
         if label in self.dcs:
             raise Exception("Data center label already exists: %s" % label)
-        dc = Datacenter(label)
+        dc = Datacenter(label, metadata=metadata)
         dc.net = self  # set reference to network
         self.dcs[label] = dc
         dc.create()  # finally create the data center in our Mininet instance
index 6d615b5..6c1ecf7 100644 (file)
@@ -66,13 +66,16 @@ class Datacenter(object):
 
     DC_COUNTER = 1
 
-    def __init__(self, label):
+    def __init__(self, label, metadata={}):
         self.net = None  # DCNetwork to which we belong
         # each node (DC) has a short internal name used by Mininet
         # this is caused by Mininets naming limitations for swtiches etc.
         self.name = "dc%d" % Datacenter.DC_COUNTER
         Datacenter.DC_COUNTER += 1
-        self.label = label  # use this for user defined names
+        # use this for user defined names that can be longer than self.name
+        self.label = label  
+        # dict to store arbitrary metadata (e.g. latitude and longitude)
+        self.metadata = metadata
         self.switch = None  # first prototype assumes one "bigswitch" per DC
         self.containers = {}  # keep track of running containers
 
@@ -140,3 +143,15 @@ class Datacenter(object):
         data center.
         """
         return list(self.containers.itervalues())
+
+    def getStatus(self):
+        """
+        Return a dict with status information about this DC.
+        """
+        return {
+            "label": self.label,
+            "internalname": self.name,
+            "switch": self.switch.name,
+            "n_running_containers": len(self.containers),
+            "metadata": self.metadata
+        }
index 3c49f99..3c53c58 100644 (file)
@@ -37,8 +37,10 @@ def create_topology1():
     """
     dc1 = net.addDatacenter("datacenter1")
     dc2 = net.addDatacenter("datacenter2")
-    dc3 = net.addDatacenter("a_very_long_data_center_name3")
-    dc4 = net.addDatacenter("datacenter4")
+    dc3 = net.addDatacenter("long_data_center_name3")
+    dc4 = net.addDatacenter(
+        "datacenter4",
+        metadata={"mydata": "we can also add arbitrary metadata to each DC"})
 
     """
     3. You can add additional SDN switches for data center
@@ -71,6 +73,8 @@ def create_topology1():
     # connect data centers to this endpoint
     zapi1.connectDatacenter(dc1)
     zapi1.connectDatacenter(dc2)
+    zapi1.connectDatacenter(dc3)
+    zapi1.connectDatacenter(dc4)
     # run API endpoint server (in another thread, don't block)
     zapi1.start()
 
index 861de1b..8b756b9 100644 (file)
@@ -55,7 +55,10 @@ class simpleTestTopology( unittest.TestCase ):
                 self.net.addLink(self.s[i], self.s[i + 1])
         # add some data centers
         for i in range(0, ndatacenter):
-            self.dc.append(self.net.addDatacenter('datacenter%d' % i))
+            self.dc.append(
+                self.net.addDatacenter(
+                    'datacenter%d' % i,
+                    metadata={"unittest_dc": i}))
         # add some hosts
         for i in range(0, nhosts):
             self.h.append(self.net.addHost('h%d' % i))