X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=src%2Femuvim%2Fdcemulator%2Fnet.py;h=cbe668f1751a80213c7679d69e1e68d81c9ab2b5;hb=8b04b532bcb52f45ac3465d834142b766d45e771;hp=c29c786f299fc2b351c36989fb5a1fd1183521a0;hpb=81955a5f09513f1bba581af38584adbb4bff1eaf;p=osm%2Fvim-emu.git diff --git a/src/emuvim/dcemulator/net.py b/src/emuvim/dcemulator/net.py index c29c786..cbe668f 100755 --- a/src/emuvim/dcemulator/net.py +++ b/src/emuvim/dcemulator/net.py @@ -1,6 +1,29 @@ """ -Distributed Cloud Emulator (dcemulator) -(c) 2015 by Manuel Peuster +Copyright (c) 2015 SONATA-NFV and Paderborn University +ALL RIGHTS RESERVED. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION] +nor the names of its contributors may be used to endorse or promote +products derived from this software without specific prior written +permission. + +This work has been performed in the framework of the SONATA project, +funded by the European Commission under Grant number 671517 through +the Horizon 2020 and 5G-PPP programmes. The authors would like to +acknowledge the contributions of their colleagues of the SONATA +partner consortium (www.sonata-nfv.eu). """ import logging @@ -16,11 +39,15 @@ from mininet.net import Containernet from mininet.node import Controller, DefaultController, OVSSwitch, OVSKernelSwitch, Docker, RemoteController from mininet.cli import CLI from mininet.link import TCLink +from mininet.clean import cleanup import networkx as nx from emuvim.dcemulator.monitoring import DCNetworkMonitor from emuvim.dcemulator.node import Datacenter, EmulatorCompute from emuvim.dcemulator.resourcemodel import ResourceModelRegistrar +LOG = logging.getLogger("dcemulator.net") +LOG.setLevel(logging.DEBUG) + class DCNetwork(Containernet): """ Wraps the original Mininet/Containernet class and provides @@ -40,15 +67,19 @@ class DCNetwork(Containernet): :param kwargs: path through for Mininet parameters :return: """ + # members self.dcs = {} + self.ryu_process = None + + # always cleanup environment before we start the emulator + self.killRyu() + cleanup() # call original Docker.__init__ and setup default controller Containernet.__init__( self, switch=OVSKernelSwitch, controller=controller, **kwargs) - # Ryu management - self.ryu_process = None if controller == RemoteController: # start Ryu controller self.startRyu(learning_switch=enable_learning) @@ -87,7 +118,7 @@ class DCNetwork(Containernet): dc.net = self # set reference to network self.dcs[label] = dc dc.create() # finally create the data center in our Mininet instance - logging.info("added data center: %s" % label) + LOG.info("added data center: %s" % label) return dc def addLink(self, node1, node2, **params): @@ -97,7 +128,7 @@ class DCNetwork(Containernet): """ assert node1 is not None assert node2 is not None - logging.debug("addLink: n1=%s n2=%s" % (str(node1), str(node2))) + LOG.debug("addLink: n1=%s n2=%s" % (str(node1), str(node2))) # ensure type of node1 if isinstance( node1, basestring ): if node1 in self.dcs: @@ -224,7 +255,7 @@ class DCNetwork(Containernet): Containernet.stop(self) # stop Ryu controller - self.stopRyu() + self.killRyu() def CLI(self): @@ -238,7 +269,7 @@ class DCNetwork(Containernet): if kwargs.get('bidirectional'): ret = ret +'\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs) - elif cmd == 'del-flows': # TODO: del-flow to be implemented + elif cmd == 'del-flows': ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs) if kwargs.get('bidirectional'): ret = ret + '\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs) @@ -251,7 +282,14 @@ class DCNetwork(Containernet): def _chainAddFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, **kwargs): - # TODO: this needs to be cleaned up + src_sw = None + dst_sw = None + src_sw_inport_nr = 0 + dst_sw_outport_nr = 0 + + LOG.debug("call chainAddFlow vnf_src_name=%r, vnf_src_interface=%r, vnf_dst_name=%r, vnf_dst_interface=%r", + vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface) + #check if port is specified (vnf:port) if vnf_src_interface is None: # take first interface by default @@ -262,10 +300,10 @@ class DCNetwork(Containernet): for connected_sw in self.DCNetwork_graph.neighbors(vnf_src_name): link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw] for link in link_dict: - if link_dict[link]['src_port_id'] == vnf_src_interface: + if (link_dict[link]['src_port_id'] == vnf_src_interface or + link_dict[link]['src_port_name'] == vnf_src_interface): # Fix: we might also get interface names, e.g, from a son-emu-cli call # found the right link and connected switch src_sw = connected_sw - src_sw_inport_nr = link_dict[link]['dst_port_nr'] break @@ -279,7 +317,8 @@ class DCNetwork(Containernet): for connected_sw in self.DCNetwork_graph.neighbors(vnf_dst_name): link_dict = self.DCNetwork_graph[connected_sw][vnf_dst_name] for link in link_dict: - if link_dict[link]['dst_port_id'] == vnf_dst_interface: + if link_dict[link]['dst_port_id'] == vnf_dst_interface or \ + link_dict[link]['dst_port_name'] == vnf_dst_interface: # Fix: we might also get interface names, e.g, from a son-emu-cli call # found the right link and connected switch dst_sw = connected_sw dst_sw_outport_nr = link_dict[link]['src_port_nr'] @@ -292,10 +331,15 @@ class DCNetwork(Containernet): # if all shortest paths are wanted, use: all_shortest_paths path = nx.shortest_path(self.DCNetwork_graph, src_sw, dst_sw, weight=kwargs.get('weight')) except: - logging.info("No path could be found between {0} and {1}".format(vnf_src_name, vnf_dst_name)) + LOG.exception("No path could be found between {0} and {1} using src_sw={2} and dst_sw={3}".format( + vnf_src_name, vnf_dst_name, src_sw, dst_sw)) + LOG.debug("Graph nodes: %r" % self.DCNetwork_graph.nodes()) + LOG.debug("Graph edges: %r" % self.DCNetwork_graph.edges()) + for e, v in self.DCNetwork_graph.edges(): + LOG.debug("%r" % self.DCNetwork_graph[e][v]) return "No path could be found between {0} and {1}".format(vnf_src_name, vnf_dst_name) - logging.info("Path between {0} and {1}: {2}".format(vnf_src_name, vnf_dst_name, path)) + LOG.info("Path between {0} and {1}: {2}".format(vnf_src_name, vnf_dst_name, path)) current_hop = src_sw switch_inport_nr = src_sw_inport_nr @@ -320,9 +364,9 @@ class DCNetwork(Containernet): if next_hop == vnf_dst_name: switch_outport_nr = dst_sw_outport_nr - logging.info("end node reached: {0}".format(vnf_dst_name)) + LOG.info("end node reached: {0}".format(vnf_dst_name)) elif not isinstance( next_node, OVSSwitch ): - logging.info("Next node: {0} is not a switch".format(next_hop)) + LOG.info("Next node: {0} is not a switch".format(next_hop)) return "Next node: {0} is not a switch".format(next_hop) else: # take first link between switches by default @@ -406,9 +450,8 @@ class DCNetwork(Containernet): elif cmd == 'del-flows': prefix = 'stats/flowentry/delete' - # if cookie is given, only delete flows by cookie - # do not specify other match -> also other cookies can be matched if cookie: + # TODO: add cookie_mask as argument flow['cookie_mask'] = int('0xffffffffffffffff', 16) # need full mask to match complete cookie action = {} @@ -453,7 +496,7 @@ class DCNetwork(Containernet): ofcmd = '' node.dpctl(cmd, ofcmd) - logging.info("{3} in switch: {0} in_port: {1} out_port: {2}".format(node.name, switch_inport_nr, + LOG.info("{3} in switch: {0} in_port: {1} out_port: {2}".format(node.name, switch_inport_nr, switch_outport_nr, cmd)) # start Ryu Openflow controller as Remote Controller for the DCNetwork @@ -471,14 +514,21 @@ class DCNetwork(Containernet): if learning_switch: self.ryu_process = Popen([ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL) else: - # no learning switch + # no learning switch, but with rest api self.ryu_process = Popen([ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL) time.sleep(1) - def stopRyu(self): + def killRyu(self): + """ + Stop the Ryu controller that might be started by son-emu. + :return: + """ + # try it nicely if self.ryu_process is not None: self.ryu_process.terminate() self.ryu_process.kill() + # ensure its death ;-) + Popen(['pkill', '-f', 'ryu-manager']) def ryu_REST(self, prefix, dpid=None, data=None): try: @@ -487,7 +537,7 @@ class DCNetwork(Containernet): else: url = self.ryu_REST_api + '/' + str(prefix) if data: - #logging.info('POST: {0}'.format(str(data))) + #LOG.info('POST: {0}'.format(str(data))) req = urllib2.Request(url, str(data)) else: req = urllib2.Request(url) @@ -495,8 +545,8 @@ class DCNetwork(Containernet): ret = urllib2.urlopen(req).read() return ret except: - logging.info('error url: {0}'.format(str(url))) - if data: logging.info('error POST: {0}'.format(str(data))) + LOG.info('error url: {0}'.format(str(url))) + if data: LOG.info('error POST: {0}'.format(str(data))) # need to respect that some match fields must be integers # http://ryu.readthedocs.io/en/latest/app/ofctl_rest.html#description-of-match-and-actions