From cbcd4c2d984a354e88febdaa368d53a3e6881c72 Mon Sep 17 00:00:00 2001 From: peusterm Date: Mon, 28 Dec 2015 11:33:42 +0100 Subject: [PATCH] Changed emulator design and started to create the emuvim Mininet wrapper layer. --- dockernetrpc/example_client.py | 36 +++++++------- dockernetrpc/rpc.py | 46 +++++++----------- emuvim/{ => api}/__init__.py | 0 emuvim/cli/__init__.py | 0 emuvim/cli/__main__.py | 0 emuvim/dcemulator/__init__.py | 4 ++ emuvim/dcemulator/link.py | 0 emuvim/dcemulator/net.py | 88 ++++++++++++++++++++++++++++++++++ emuvim/dcemulator/node.py | 46 ++++++++++++++++++ emuvim/example_topology.py | 43 +++++++++++++++++ 10 files changed, 218 insertions(+), 45 deletions(-) rename emuvim/{ => api}/__init__.py (100%) create mode 100644 emuvim/cli/__init__.py create mode 100644 emuvim/cli/__main__.py create mode 100644 emuvim/dcemulator/__init__.py create mode 100644 emuvim/dcemulator/link.py create mode 100644 emuvim/dcemulator/net.py create mode 100644 emuvim/dcemulator/node.py create mode 100644 emuvim/example_topology.py diff --git a/dockernetrpc/example_client.py b/dockernetrpc/example_client.py index 213905c..5a5379a 100644 --- a/dockernetrpc/example_client.py +++ b/dockernetrpc/example_client.py @@ -1,38 +1,40 @@ -import Pyro4 import time +import zerorpc def main(): # create connection to remote Mininet instance - rmn = Pyro4.Proxy("PYRONAME:remote.mininet") + c = zerorpc.Client() + c.connect("tcp://127.0.0.1:4242") # do some API tests - h1 = rmn.addHost('h1') - h2 = rmn.addHost('h2') - d1 = rmn.addDocker('d1', ip='10.0.0.253', dimage="ubuntu") + h1 = c.addHost('h1') + h2 = c.addHost('h2') + d1 = c.addDocker('d1', "ubuntu", "10.0.0.253") - s1 = rmn.addSwitch("s1") + s1 = c.addSwitch("s1") - rmn.addLink(h1, s1) - rmn.addLink(h2, s1) - rmn.addLink(d1, s1) + c.addLink(h1, s1) + c.addLink(h2, s1) + c.addLink(d1, s1) - rmn.start() + c.start_net() + c.CLI() # check functionality at runtime """ - d2 = rmn.addDocker('d2', dimage="ubuntu") - h3 = rmn.addHost('h3', ip='10.0.0.200') - rmn.addLink(d2, s1, params1={"ip": "10.0.0.251/8"}) + d2 = c.addDocker('d2', dimage="ubuntu") + h3 = c.addHost('h3', ip='10.0.0.200') + c.addLink(d2, s1, params1={"ip": "10.0.0.251/8"}) time.sleep(2) - rmn.removeLink(node1="h1", node2="s1") - rmn.removeHost('h1') - #rmn.removeHost('d1') + c.removeLink(node1="h1", node2="s1") + c.removeHost('h1') + #c.removeHost('d1') """ time.sleep(2) - rmn.stop() + c.stop_net() if __name__ == '__main__': diff --git a/dockernetrpc/rpc.py b/dockernetrpc/rpc.py index c649d77..f145b53 100644 --- a/dockernetrpc/rpc.py +++ b/dockernetrpc/rpc.py @@ -6,47 +6,46 @@ from mininet.cli import CLI import mininet.log import logging import os -import Pyro4 +import zerorpc class RemoteMininetNetwork(object): def __init__(self): - mininet.log.setLogLevel( 'debug' ) + # set mininet loglevel + mininet.log.setLogLevel( 'info' ) self.net = Mininet( controller=Controller ) self.net.addController( 'c0' ) - def start(self): + def start_net(self): self.net.start() def CLI(self): CLI(self.net) - def stop(self): + def stop_net(self): try: self.net.stop() except Exception as e: print e + def addHost(self, name, ip=None): + return str(self.net.addHost(name, ip=ip)) - def addHost(self, name, cls=None, **params): - return str(self.net.addHost(name, cls=cls, **params)) + def addDocker(self, name, dimage, ip): + return str(self.net.addDocker(name, dimage=dimage, ip=ip)) - def addDocker(self, name, **params): - return str(self.net.addDocker(name, **params)) - - def addSwitch(self, name, **params): + def addSwitch(self, name): # we have to use OVSSwitch to be able to do link attachments # at runtime (switch.attach) method - return str(self.net.addSwitch(name, cls=OVSSwitch, **params)) + return str(self.net.addSwitch(name, cls=OVSSwitch)) - def addLink(self, node1, node2, port1=None, port2=None, - cls=None, **params): + def addLink(self, node1, node2, port1=None, port2=None): return str(self.net.addLink(node1, node2, - port1, port2, cls=cls, **params)) + port1, port2)) - def removeHost(self, name, **params): - return self.net.removeHost(name, **params) + def removeHost(self, name): + return self.net.removeHost(name) def removeLink(self, link=None, node1=None, node2=None): n1, n2 = self.net.get(node1), self.net.get(node2) @@ -54,15 +53,6 @@ class RemoteMininetNetwork(object): def start_server(): - daemon = Pyro4.Daemon() - # ATTENTION: - # we need a PyroNS instance to be running: pyro4-ns (in new terminal) - ns = Pyro4.locateNS() - uri = daemon.register(RemoteMininetNetwork()) - # map object URI to a nice name - ns.register("remote.mininet", uri) - - logging.info("Server URI is: %s", uri) - - # Start the server... - daemon.requestLoop() + s = zerorpc.Server(RemoteMininetNetwork()) + s.bind("tcp://0.0.0.0:4242") + s.run() diff --git a/emuvim/__init__.py b/emuvim/api/__init__.py similarity index 100% rename from emuvim/__init__.py rename to emuvim/api/__init__.py diff --git a/emuvim/cli/__init__.py b/emuvim/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/emuvim/cli/__main__.py b/emuvim/cli/__main__.py new file mode 100644 index 0000000..e69de29 diff --git a/emuvim/dcemulator/__init__.py b/emuvim/dcemulator/__init__.py new file mode 100644 index 0000000..64f6616 --- /dev/null +++ b/emuvim/dcemulator/__init__.py @@ -0,0 +1,4 @@ +""" +Distributed Cloud Emulator (dcemulator) +(c) 2015 by Manuel Peuster +""" \ No newline at end of file diff --git a/emuvim/dcemulator/link.py b/emuvim/dcemulator/link.py new file mode 100644 index 0000000..e69de29 diff --git a/emuvim/dcemulator/net.py b/emuvim/dcemulator/net.py new file mode 100644 index 0000000..8ea41a7 --- /dev/null +++ b/emuvim/dcemulator/net.py @@ -0,0 +1,88 @@ +""" +Distributed Cloud Emulator (dcemulator) +(c) 2015 by Manuel Peuster +""" +import logging + +from mininet.net import Mininet +from mininet.node import Controller, OVSKernelSwitch, Switch +from mininet.cli import CLI +from mininet.log import setLogLevel, info +from mininet.link import TCLink, Link + +from node import Datacenter + + +class DCNetwork(object): + + def __init__(self): + self.dcs = {} + self.switches = {} + self.links = [] + + # create a Mininet/Dockernet network + setLogLevel('info') # set Mininet loglevel + self.mnet = Mininet(controller=Controller, switch=OVSKernelSwitch) + self.mnet.addController('c0') + + def addDatacenter(self, name): + """ + Create and add a logical cloud data center to the network. + """ + if name in self.dcs: + raise Exception("Data center name already exists: %s" % name) + dc = Datacenter(name) + dc.net = self # set reference to network + self.dcs[name] = dc + dc.create() # finally create the data center in our Mininet instance + logging.info("added data center: %s" % name) + return dc + + def addSwitch(self, name): + """ + We can also add additional SDN switches between data centers. + """ + s = self.mnet.addSwitch(name) + self.switches[name] = s + logging.info("added switch: %s" % name) + return s + + def addLink(self, node1, node2): + assert node1 is not None + assert node2 is not None + # ensure type of node1 + if isinstance( node1, basestring ): + if node1 in self.dcs: + node1 = self.dcs[node1].switch + elif node1 in self.switches: + node1 = self.switches[node1] + if isinstance( node1, Datacenter ): + node1 = node1.switch + # ensure type of node2 + if isinstance( node2, basestring ): + if node2 in self.dcs: + node2 = self.dcs[node2].switch + elif node2 in self.switches: + node2 = self.switches[node2] + if isinstance( node2, Datacenter ): + node2 = node2.switch + # create link if everything is correct + if (node1 is not None and isinstance(node1, OVSKernelSwitch) + and node2 is not None and isinstance(node2, OVSKernelSwitch)): + self.mnet.addLink(node1, node2) # TODO we need TCLinks with user defined performance her + else: + raise Exception( + "one of the given nodes is not a Mininet switch or None") + + def start(self): + # start + for dc in self.dcs.itervalues(): + dc.start() + self.mnet.start() + + def stop(self): + self.mnet.stop() + + def CLI(self): + CLI(self.mnet) + diff --git a/emuvim/dcemulator/node.py b/emuvim/dcemulator/node.py new file mode 100644 index 0000000..b12751f --- /dev/null +++ b/emuvim/dcemulator/node.py @@ -0,0 +1,46 @@ +""" +Distributed Cloud Emulator (dcemulator) +(c) 2015 by Manuel Peuster +""" +import logging + + +DCDPID_BASE = 1000 # start of switch dpid's used for data center switches + + +class Datacenter(object): + """ + Represents a logical data center to which compute resources + (Docker containers) can be added at runtime. + """ + + def __init__(self, name): + self.net = None # DCNetwork to which we belong + self.name = name + self.switch = None # first prototype assumes one "bigswitch" per DC + + def _get_next_dc_dpid(self): + global DCDPID_BASE + DCDPID_BASE += 1 + return DCDPID_BASE + + def create(self): + """ + Each data center is represented by a single switch to which + compute resources can be connected at run time. + + TODO: This will be changes in the future to support multiple networks + per data center + """ + self.switch = self.net.mnet.addSwitch( + "%s.s1" % self.name, dpid=hex(self._get_next_dc_dpid())[2:]) + logging.debug("created data center switch: %s" % str(self.switch)) + + def start(self): + pass + + def addCompute(self): + pass + + def removeCompute(self): + pass diff --git a/emuvim/example_topology.py b/emuvim/example_topology.py new file mode 100644 index 0000000..f2be310 --- /dev/null +++ b/emuvim/example_topology.py @@ -0,0 +1,43 @@ +""" +This is an example topology for the distributed cloud emulator (dcemulator). +(c) 2015 by Manuel Peuster + +The original Mininet API has to be completely hidden and not be used by this +script. +""" +import logging +from dcemulator.net import DCNetwork + +logging.basicConfig(level=logging.DEBUG) + + +def create_topology1(): + # initialize network + net = DCNetwork() + + # add data centers + dc1 = net.addDatacenter("dc1") + dc2 = net.addDatacenter("dc2") + dc3 = net.addDatacenter("dc3") + dc4 = net.addDatacenter("dc4") + # add additional SDN switches to our topology + s1 = net.addSwitch("s1") + # add links between data centers + net.addLink(dc1, dc2) + net.addLink("dc1", s1) + net.addLink(s1, "dc3") + net.addLink(s1, dc4) + # start network + net.start() + net.CLI() # TODO remove this when we integrate APIs? + net.stop() # TODO remove this when we integrate APIs? + # start APIs (to access emulated cloud data centers) + pass # TODO: how to reflect one API endpoint per DC? + + +def main(): + create_topology1() + + +if __name__ == '__main__': + main() -- 2.25.1