X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=src%2Femuvim%2Fdcemulator%2Fnet.py;h=9e8c9be78ccd50072d2fd029ff292e77dace8282;hb=efdda12a005a63a449ea7afb9baab1743b4d04ab;hp=005e272c64ad88942d7a7e946eefab32bc6e7711;hpb=72f09885db3935e77901c3dee132cb176b927c7b;p=osm%2Fvim-emu.git diff --git a/src/emuvim/dcemulator/net.py b/src/emuvim/dcemulator/net.py index 005e272..9e8c9be 100755 --- a/src/emuvim/dcemulator/net.py +++ b/src/emuvim/dcemulator/net.py @@ -24,25 +24,27 @@ # acknowledge the contributions of their colleagues of the SONATA # partner consortium (www.sonata-nfv.eu). import logging - -import site import time -from subprocess import Popen import re import requests import os import json - +import networkx as nx +from subprocess import Popen +# from gevent import monkey from mininet.net import Containernet from mininet.node import 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 +# ensure correct functionality of all gevent based REST servers +# monkey.patch_all() + +# setup logging LOG = logging.getLogger("dcemulator.net") LOG.setLevel(logging.DEBUG) @@ -115,7 +117,7 @@ class DCNetwork(Containernet): self.DCNetwork_graph = nx.MultiDiGraph() # initialize pool of vlan tags to setup the SDN paths - self.vlans = range(1, 4095)[::-1] + self.vlans = list(range(1, 4095))[::-1] # link to Ryu REST_API ryu_ip = 'localhost' @@ -157,13 +159,13 @@ class DCNetwork(Containernet): assert node2 is not None # ensure type of node1 - if isinstance(node1, basestring): + if isinstance(node1, str): if node1 in self.dcs: node1 = self.dcs[node1].switch if isinstance(node1, Datacenter): node1 = node1.switch # ensure type of node2 - if isinstance(node2, basestring): + if isinstance(node2, str): if node2 in self.dcs: node2 = self.dcs[node2].switch if isinstance(node2, Datacenter): @@ -224,7 +226,7 @@ class DCNetwork(Containernet): 'dst_port_name': node2_port_name} attr_dict2.update(attr_dict) self.DCNetwork_graph.add_edge( - node1.name, node2.name, attr_dict=attr_dict2) + node1.name, node2.name, **attr_dict2) attr_dict2 = {'src_port_id': node2_port_id, 'src_port_nr': node2.ports[link.intf2], 'src_port_name': node2_port_name, @@ -232,7 +234,7 @@ class DCNetwork(Containernet): 'dst_port_name': node1_port_name} attr_dict2.update(attr_dict) self.DCNetwork_graph.add_edge( - node2.name, node1.name, attr_dict=attr_dict2) + node2.name, node1.name, **attr_dict2) LOG.debug("addLink: n1={0} intf1={1} -- n2={2} intf2={3}".format( str(node1), node1_port_name, str(node2), node2_port_name)) @@ -318,13 +320,13 @@ class DCNetwork(Containernet): Returns a list with all containers within all data centers. """ all_containers = [] - for dc in self.dcs.itervalues(): + for dc in self.dcs.values(): all_containers += dc.listCompute() return all_containers def start(self): # start - for dc in self.dcs.itervalues(): + for dc in self.dcs.values(): dc.start() Containernet.start(self) @@ -383,6 +385,18 @@ class DCNetwork(Containernet): switch_node = self.getNodeByName(src_sw) self._set_vlan_tag(switch_node, src_sw_inport_name, vlan) + def getNodeByName(self, name): + """ + Wraps Containernet's getNodeByName method to avoid + key not found exceptions. + """ + try: + return super(DCNetwork, self).getNodeByName(name) + except BaseException as ex: + LOG.warning("Node not found: {}".format(name)) + LOG.debug("Node not found: {}".format(ex)) + return None + def _addMonitorFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, tag=None, **kwargs): """ @@ -462,7 +476,7 @@ class DCNetwork(Containernet): return "No path could be found between {0} and {1}".format( vnf_src_name, vnf_dst_name) - LOG.info("Path between {0} and {1}: {2}".format( + LOG.debug("Creating path between {0} and {1}: {2}".format( vnf_src_name, vnf_dst_name, path)) current_hop = src_sw @@ -484,7 +498,7 @@ class DCNetwork(Containernet): if next_hop == vnf_dst_name: switch_outport_nr = dst_sw_outport_nr - LOG.info("end node reached: {0}".format(vnf_dst_name)) + LOG.debug("end node reached: {0}".format(vnf_dst_name)) elif not isinstance(next_node, OVSSwitch): LOG.info("Next node: {0} is not a switch".format(next_hop)) return "Next node: {0} is not a switch".format(next_hop) @@ -615,7 +629,7 @@ class DCNetwork(Containernet): # check if port is specified (vnf:port) if vnf_src_interface is None: # take first interface by default - connected_sw = self.DCNetwork_graph.neighbors(vnf_src_name)[0] + connected_sw = list(self.DCNetwork_graph.neighbors(vnf_src_name))[0] link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw] vnf_src_interface = link_dict[0]['src_port_id'] @@ -632,7 +646,7 @@ class DCNetwork(Containernet): if vnf_dst_interface is None: # take first interface by default - connected_sw = self.DCNetwork_graph.neighbors(vnf_dst_name)[0] + connected_sw = list(self.DCNetwork_graph.neighbors(vnf_dst_name))[0] link_dict = self.DCNetwork_graph[connected_sw][vnf_dst_name] vnf_dst_interface = link_dict[0]['dst_port_id'] @@ -666,7 +680,7 @@ class DCNetwork(Containernet): return "No path could be found between {0} and {1}".format( vnf_src_name, vnf_dst_name) - LOG.info("Path between {0} and {1}: {2}".format( + LOG.debug("Creating path between {0} and {1}: {2}".format( vnf_src_name, vnf_dst_name, path)) current_hop = src_sw @@ -706,7 +720,7 @@ class DCNetwork(Containernet): if next_hop == vnf_dst_name: switch_outport_nr = dst_sw_outport_nr - LOG.info("end node reached: {0}".format(vnf_dst_name)) + LOG.debug("end node reached: {0}".format(vnf_dst_name)) elif not isinstance(next_node, OVSSwitch): LOG.info("Next node: {0} is not a switch".format(next_hop)) return "Next node: {0} is not a switch".format(next_hop) @@ -746,6 +760,8 @@ class DCNetwork(Containernet): 'match_input': kwargs.get('match') } flow_options_str = json.dumps(flow_options, indent=1) + LOG.info("Installed flow rule: ({}:{}) -> ({}:{}) with options: {}" + .format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface, flow_options)) return "success: {2} between {0} and {1} with options: {3}".format( vnf_src_name, vnf_dst_name, cmd, flow_options_str) @@ -758,6 +774,7 @@ class DCNetwork(Containernet): cmd = kwargs.get('cmd') path = kwargs.get('path') index = kwargs.get('pathindex') + mod_dl_dst = kwargs.get('mod_dl_dst') vlan = kwargs.get('vlan') priority = kwargs.get('priority', DEFAULT_PRIORITY) @@ -825,6 +842,12 @@ class DCNetwork(Containernet): else: # middle nodes match += ',dl_vlan=%s' % vlan + if mod_dl_dst: + action = {} + action['type'] = 'SET_FIELD' + action['field'] = 'eth_dst' + action['value'] = mod_dl_dst + flow['actions'].append(action) # output action must come last action = {} @@ -896,14 +919,14 @@ class DCNetwork(Containernet): # start Ryu Openflow controller as Remote Controller for the DCNetwork def startRyu(self, learning_switch=True): # start Ryu controller with rest-API - python_install_path = site.getsitepackages()[0] + # ryu default learning switch - # ryu_path = python_install_path + '/ryu/app/simple_switch_13.py' + # ryu_learning_app = python_install_path + '/ryu/app/simple_switch_13.py' # custom learning switch that installs a default NORMAL action in the # ovs switches dir_path = os.path.dirname(os.path.realpath(__file__)) - ryu_path = dir_path + '/son_emu_simple_switch_13.py' - ryu_path2 = python_install_path + '/ryu/app/ofctl_rest.py' + ryu_learning_app = dir_path + '/son_emu_simple_switch_13.py' + ryu_rest_app = 'ryu.app.ofctl_rest' # change the default Openflow controller port to 6653 (official IANA-assigned port number), as used by Mininet # Ryu still uses 6633 as default ryu_option = '--ofp-tcp-listen-port' @@ -911,15 +934,13 @@ class DCNetwork(Containernet): ryu_cmd = 'ryu-manager' FNULL = open("/tmp/ryu.log", 'w') if learning_switch: - self.ryu_process = Popen( - [ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL) - LOG.debug('starting ryu-controller with {0}'.format(ryu_path)) - LOG.debug('starting ryu-controller with {0}'.format(ryu_path2)) + # learning and rest api + args = [ryu_cmd, ryu_learning_app, ryu_rest_app, ryu_option, ryu_of_port] else: # no learning switch, but with rest api - self.ryu_process = Popen( - [ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL) - LOG.debug('starting ryu-controller with {0}'.format(ryu_path2)) + args = [ryu_cmd, ryu_rest_app, ryu_option, ryu_of_port] + self.ryu_process = Popen(args, stdout=FNULL, stderr=FNULL) + LOG.debug('starting ryu-controller with %s' % args) time.sleep(1) def killRyu(self): @@ -928,9 +949,12 @@ class DCNetwork(Containernet): :return: """ # try it nicely - if self.ryu_process is not None: - self.ryu_process.terminate() - self.ryu_process.kill() + try: + if self.ryu_process is not None: + self.ryu_process.terminate() + self.ryu_process.kill() + except BaseException as ex: + LOG.warning("Error during Ryu stop: {}".format(ex)) # ensure its death ;-) Popen(['pkill', '-f', 'ryu-manager']) @@ -940,6 +964,8 @@ class DCNetwork(Containernet): url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid) else: url = self.ryu_REST_api + '/' + str(prefix) + + LOG.debug('sending RYU command: %s, payload: %s', url, data) if data: req = self.RyuSession.post(url, json=data) else: @@ -947,7 +973,7 @@ class DCNetwork(Containernet): # do extra logging if status code is not 200 (OK) if req.status_code is not requests.codes.ok: - logging.info( + LOG.info( 'type {0} encoding: {1} text: {2} headers: {3} history: {4}'.format(req.headers['content-type'], req.encoding, req.text, req.headers, req.history))