X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2Fvim-emu.git;a=blobdiff_plain;f=src%2Femuvim%2Fdcemulator%2Fnet.py;h=0e3882c47cfb3b261381363ef7ed3aa50ae94c7f;hp=248dc3481e61bc3337541eb6023a090f30d0af65;hb=e131bf5fefb90aadfe698dd72126cd2fdfe2fb7a;hpb=27b6d95a9c00319584a2d0ddfd63b17bb7e30dab diff --git a/src/emuvim/dcemulator/net.py b/src/emuvim/dcemulator/net.py index 248dc34..0e3882c 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 @@ -12,7 +35,7 @@ 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,20 +44,21 @@ 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. """ def __init__(self, controller=RemoteController, monitor=False, + enable_learning = True, # in case of RemoteController (Ryu), learning switch behavior can be turned off/on dc_emulation_max_cpu=1.0, # fraction of overall CPU time for emulation 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,14 +66,15 @@ class DCNetwork(Dockernet): self.dcs = {} # call original Docker.__init__ and setup default controller - Dockernet.__init__( - self, switch=OVSKernelSwitch, **kwargs) + Containernet.__init__( + self, switch=OVSKernelSwitch, controller=controller, **kwargs) + # Ryu management self.ryu_process = None if controller == RemoteController: # start Ryu controller - self.startRyu() + self.startRyu(learning_switch=enable_learning) # add the specified controller self.addController('c0', controller=controller) @@ -121,11 +146,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] @@ -143,7 +168,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: @@ -180,14 +205,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 ): """ @@ -195,7 +220,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): """ @@ -210,7 +235,7 @@ class DCNetwork(Dockernet): # start for dc in self.dcs.itervalues(): dc.start() - Dockernet.start(self) + Containernet.start(self) def stop(self): @@ -219,7 +244,7 @@ class DCNetwork(Dockernet): self.monitor_agent.stop() # stop emulator net - Dockernet.stop(self) + Containernet.stop(self) # stop Ryu controller self.stopRyu() @@ -234,20 +259,21 @@ class DCNetwork(Dockernet): if cmd == 'add-flow': ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs) if kwargs.get('bidirectional'): - return ret +'\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs) + 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'): - return ret + '\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs) + ret = ret + '\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs) else: - return "Command unknown" + ret = "Command unknown" + + return ret 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 #check if port is specified (vnf:port) if vnf_src_interface is None: # take first interface by default @@ -331,10 +357,15 @@ class DCNetwork(Dockernet): kwargs['vlan'] = vlan kwargs['path'] = path kwargs['current_hop'] = current_hop - ## 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) + + if self.controller == RemoteController: + ## set flow entry via ryu rest api + self._set_flow_entry_ryu_rest(current_node, switch_inport_nr, switch_outport_nr, **kwargs) + else: + ## set flow entry via ovs-ofctl + self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs) + + # take first link between switches by default if isinstance( next_node, OVSSwitch ): @@ -359,6 +390,7 @@ class DCNetwork(Dockernet): flow = {} flow['dpid'] = int(node.dpid, 16) + if cookie: flow['cookie'] = int(cookie) @@ -369,15 +401,13 @@ class DCNetwork(Dockernet): # 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 + flow['actions'].append(action) + action = {} action['type'] = 'SET_FIELD' action['field'] = 'vlan_vid' action['value'] = vlan @@ -389,14 +419,18 @@ class DCNetwork(Dockernet): flow['actions'].append(action) else: # middle nodes match += ',dl_vlan=%s' % vlan - #flow['match'] = self._parse_match(match) + # output action must come last + action = {} + action['type'] = 'OUTPUT' + action['port'] = switch_outport_nr + flow['actions'].append(action) + 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) + # TODO: add cookie_mask as argument + flow['cookie_mask'] = int('0xffffffffffffffff', 16) # need full mask to match complete cookie action = {} action['type'] = 'OUTPUT' @@ -444,7 +478,7 @@ class DCNetwork(Dockernet): switch_outport_nr, cmd)) # start Ryu Openflow controller as Remote Controller for the DCNetwork - def startRyu(self): + def startRyu(self, learning_switch=True): # start Ryu controller with rest-API python_install_path = site.getsitepackages()[0] ryu_path = python_install_path + '/ryu/app/simple_switch_13.py' @@ -455,9 +489,11 @@ class DCNetwork(Dockernet): ryu_of_port = '6653' ryu_cmd = 'ryu-manager' FNULL = open("/tmp/ryu.log", 'w') - self.ryu_process = Popen([ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL) - # no learning switch - #self.ryu_process = Popen([ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL) + 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, 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): @@ -466,18 +502,22 @@ class DCNetwork(Dockernet): 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) + try: + 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 + 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))) # 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