X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=src%2Femuvim%2Fdcemulator%2Fnet.py;h=7f31a4663df754b7e6843dbf9e602bb84dd129e4;hb=5ddd8f817f58c12d52ae1601cc4483f46f28fa06;hp=f7fafdf7a5c5b95975f1575e39fc1367afa21556;hpb=78f01d36a61761680c74af6fb8ae91a2b4315406;p=osm%2Fvim-emu.git diff --git a/src/emuvim/dcemulator/net.py b/src/emuvim/dcemulator/net.py index f7fafdf..7f31a46 100755 --- a/src/emuvim/dcemulator/net.py +++ b/src/emuvim/dcemulator/net.py @@ -9,10 +9,10 @@ import time from subprocess import Popen import os import re +import urllib2 +from functools import partial - - -from mininet.net import Dockernet +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 @@ -21,9 +21,9 @@ from emuvim.dcemulator.monitoring import DCNetworkMonitor from emuvim.dcemulator.node import Datacenter, EmulatorCompute from emuvim.dcemulator.resourcemodel import ResourceModelRegistrar -class DCNetwork(Dockernet): +class DCNetwork(Containernet): """ - Wraps the original Mininet/Dockernet class and provides + Wraps the original Mininet/Containernet class and provides methods to add data centers, switches, etc. This class is used by topology definition scripts. @@ -34,7 +34,7 @@ class DCNetwork(Dockernet): dc_emulation_max_mem=512, # emulation max mem in MB **kwargs): """ - Create an extended version of a Dockernet network + Create an extended version of a Containernet network :param dc_emulation_max_cpu: max. CPU time used by containers in data centers :param kwargs: path through for Mininet parameters :return: @@ -42,7 +42,7 @@ class DCNetwork(Dockernet): self.dcs = {} # call original Docker.__init__ and setup default controller - Dockernet.__init__( + Containernet.__init__( self, switch=OVSKernelSwitch, **kwargs) # Ryu management @@ -60,6 +60,11 @@ class DCNetwork(Dockernet): # initialize pool of vlan tags to setup the SDN paths self.vlans = range(4096)[::-1] + # link to Ryu REST_API + ryu_ip = '0.0.0.0' + ryu_port = '8080' + self.ryu_REST_api = 'http://{0}:{1}'.format(ryu_ip, ryu_port) + # monitoring agent if monitor: self.monitor_agent = DCNetworkMonitor(self) @@ -116,11 +121,11 @@ class DCNetwork(Dockernet): params["params2"]["ip"] = self.getNextIp() # ensure that we allow TCLinks between data centers # TODO this is not optimal, we use cls=Link for containers and TCLink for data centers - # see Dockernet issue: https://github.com/mpeuster/dockernet/issues/3 + # see Containernet issue: https://github.com/mpeuster/containernet/issues/3 if "cls" not in params: params["cls"] = TCLink - link = Dockernet.addLink(self, node1, node2, **params) + link = Containernet.addLink(self, node1, node2, **params) # try to give container interfaces a default id node1_port_id = node1.ports[link.intf1] @@ -138,7 +143,7 @@ class DCNetwork(Dockernet): # add edge and assigned port number to graph in both directions between node1 and node2 # port_id: id given in descriptor (if available, otherwise same as port) - # port: portnumber assigned by Dockernet + # port: portnumber assigned by Containernet attr_dict = {} # possible weight metrics allowed by TClink class: @@ -175,14 +180,14 @@ class DCNetwork(Dockernet): Wrapper for addDocker method to use custom container class. """ self.DCNetwork_graph.add_node(label) - return Dockernet.addDocker(self, label, cls=EmulatorCompute, **params) + return Containernet.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) + return Containernet.removeDocker(self, label, **params) def addSwitch( self, name, add_to_graph=True, **params ): """ @@ -190,7 +195,7 @@ class DCNetwork(Dockernet): """ if add_to_graph: self.DCNetwork_graph.add_node(name) - return Dockernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', **params) + return Containernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', **params) def getAllContainers(self): """ @@ -205,7 +210,7 @@ class DCNetwork(Dockernet): # start for dc in self.dcs.itervalues(): dc.start() - Dockernet.start(self) + Containernet.start(self) def stop(self): @@ -214,7 +219,7 @@ class DCNetwork(Dockernet): self.monitor_agent.stop() # stop emulator net - Dockernet.stop(self) + Containernet.stop(self) # stop Ryu controller self.stopRyu() @@ -292,10 +297,11 @@ class DCNetwork(Dockernet): switch_inport_nr = src_sw_inport_nr # choose free vlan if path contains more than 1 switch - if len(path) > 1: - vlan = self.vlans.pop() - else: - vlan = None + cmd = kwargs.get('cmd') + vlan = None + if cmd == 'add-flow': + if len(path) > 1: + vlan = self.vlans.pop() for i in range(0,len(path)): current_node = self.getNodeByName(current_hop) @@ -325,15 +331,80 @@ class DCNetwork(Dockernet): kwargs['vlan'] = vlan kwargs['path'] = path kwargs['current_hop'] = current_hop - self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs) - # TODO set entry via Ryu REST api (in case emulator is running remote...) + ## set flow entry via ovs-ofctl + #self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs) + ## set flow entry via ryu rest api + self._set_flow_entry_ryu_rest(current_node, switch_inport_nr, switch_outport_nr, **kwargs) # take first link between switches by default if isinstance( next_node, OVSSwitch ): switch_inport_nr = self.DCNetwork_graph[current_hop][next_hop][0]['dst_port_nr'] current_hop = next_hop - return "path added between {0} and {1}".format(vnf_src_name, vnf_dst_name) + return "path {2} between {0} and {1}".format(vnf_src_name, vnf_dst_name, cmd) + + def _set_flow_entry_ryu_rest(self, node, switch_inport_nr, switch_outport_nr, **kwargs): + match = 'in_port=%s' % switch_inport_nr + + cookie = kwargs.get('cookie') + match_input = kwargs.get('match') + cmd = kwargs.get('cmd') + path = kwargs.get('path') + current_hop = kwargs.get('current_hop') + vlan = kwargs.get('vlan') + + s = ',' + if match_input: + match = s.join([match, match_input]) + + flow = {} + flow['dpid'] = int(node.dpid, 16) + if cookie: + flow['cookie'] = int(cookie) + + + flow['actions'] = [] + + # possible Ryu actions, match fields: + # http://ryu.readthedocs.io/en/latest/app/ofctl_rest.html#add-a-flow-entry + if cmd == 'add-flow': + prefix = 'stats/flowentry/add' + action = {} + action['type'] = 'OUTPUT' + action['port'] = switch_outport_nr + flow['actions'].append(action) + if vlan != None: + if path.index(current_hop) == 0: # first node + action = {} + action['type'] = 'PUSH_VLAN' # Push a new VLAN tag if a input frame is non-VLAN-tagged + action['ethertype'] = 33024 # Ethertype 0x8100(=33024): IEEE 802.1Q VLAN-tagged frame + action['type'] = 'SET_FIELD' + action['field'] = 'vlan_vid' + action['value'] = vlan + flow['actions'].append(action) + elif path.index(current_hop) == len(path) - 1: # last node + match += ',dl_vlan=%s' % vlan + action = {} + action['type'] = 'POP_VLAN' + flow['actions'].append(action) + else: # middle nodes + match += ',dl_vlan=%s' % vlan + #flow['match'] = self._parse_match(match) + elif cmd == 'del-flows': + #del(flow['actions']) + prefix = 'stats/flowentry/delete' + if cookie: + flow['cookie_mask'] = cookie + #if cookie is None: + # flow['match'] = self._parse_match(match) + + action = {} + action['type'] = 'OUTPUT' + action['port'] = switch_outport_nr + flow['actions'].append(action) + + flow['match'] = self._parse_match(match) + self.ryu_REST(prefix, data=flow) def _set_flow_entry_dpctl(self, node, switch_inport_nr, switch_outport_nr, **kwargs): match = 'in_port=%s' % switch_inport_nr @@ -394,3 +465,33 @@ class DCNetwork(Dockernet): self.ryu_process.terminate() self.ryu_process.kill() + def ryu_REST(self, prefix, dpid=None, data=None): + if dpid: + url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid) + else: + url = self.ryu_REST_api + '/' + str(prefix) + if data: + #logging.info('POST: {0}'.format(str(data))) + req = urllib2.Request(url, str(data)) + else: + req = urllib2.Request(url) + + ret = urllib2.urlopen(req).read() + return ret + + # 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 + def _parse_match(self, match): + matches = match.split(',') + dict = {} + for m in matches: + match = m.split('=') + if len(match) == 2: + try: + m2 = int(match[1], 0) + except: + m2 = match[1] + + dict.update({match[0]:m2}) + return dict +