From: stevenvanrossem Date: Fri, 27 Jan 2017 22:37:29 +0000 (+0100) Subject: merge with latest upstream status X-Git-Tag: v3.1~45^2~25 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2Fvim-emu.git;a=commitdiff_plain;h=9cc7360bf2c0dacb869d8ed0674d9d9e269a1082;hp=-c merge with latest upstream status --- 9cc7360bf2c0dacb869d8ed0674d9d9e269a1082 diff --combined Vagrantfile index b87a9f5,7c9c68f..7c9c68f mode 100755,100644..100755 --- a/Vagrantfile +++ b/Vagrantfile @@@ -111,7 -111,7 +111,7 @@@ Vagrant.configure(2) do |config sudo apt-get install -y git ansible aptitude sudo echo "localhost ansible_connection=local" >> /etc/ansible/hosts # install containernet - git clone https://github.com/mpeuster/containernet.git + git clone https://github.com/containernet/containernet.git echo "Installing containernet (will take some time ~30 minutes) ..." cd /home/vagrant/containernet/ansible sudo ansible-playbook install.yml diff --combined ansible/install.yml index fd3aa4b,fd3aa4b..4e4ff01 --- a/ansible/install.yml +++ b/ansible/install.yml @@@ -57,8 -57,8 +57,8 @@@ - name: install requests pip: name=requests state=latest -- - name: install docker-py -- pip: name=docker-py version=1.7.1 ++ - name: install docker ++ pip: name=docker version=2.0.2 - name: install prometheus_client pip: name=prometheus_client state=latest diff --combined misc/sonata-demo-service.son index c4e58e2,c4e58e2..c4e58e2 mode 100644,100644..100755 Binary files differ diff --combined setup.py index 0baad49,0baad49..ce1c47f --- a/setup.py +++ b/setup.py @@@ -52,7 -52,7 +52,7 @@@ setup(name='emuvim' 'pytest', 'Flask', 'flask_restful', -- 'docker-py==1.7.1', ++ 'docker==2.0.2', 'requests', 'prometheus_client', 'urllib3' diff --combined src/emuvim/api/sonata/dummygatekeeper.py index f0a7ccd,4018b69..d5daf42 --- a/src/emuvim/api/sonata/dummygatekeeper.py +++ b/src/emuvim/api/sonata/dummygatekeeper.py @@@ -39,7 -39,7 +39,7 @@@ import hashli import zipfile import yaml import threading -from docker import Client as DockerClient +from docker import DockerClient from flask import Flask, request import flask_restful as fr from collections import defaultdict @@@ -75,7 -75,6 +75,7 @@@ class Gatekeeper(object) def __init__(self): self.services = dict() self.dcs = dict() + self.net = None self.vnf_counter = 0 # used to generate short names for VNFs (Mininet limitation) LOG.info("Create SONATA dummy gatekeeper.") @@@ -176,98 -175,83 +176,99 @@@ class Service(object) vnfi = self._start_vnfd(vnfd) self.instances[instance_uuid]["vnf_instances"].append(vnfi) - vlinks = self.nsd["virtual_links"] - fwd_links = self.nsd["forwarding_graphs"][0]["constituent_virtual_links"] - eline_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-Line")] - elan_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-LAN")] - - GK.net.deployed_elines.extend(eline_fwd_links) - GK.net.deployed_elans.extend(elan_fwd_links) - - # 4a. deploy E-Line links - # cookie is used as identifier for the flowrules installed by the dummygatekeeper - # eg. different services get a unique cookie for their flowrules - cookie = 1 - for link in eline_fwd_links: - src_id, src_if_name = link["connection_points_reference"][0].split(":") - dst_id, dst_if_name = link["connection_points_reference"][1].split(":") - - # check if there is a SAP in the link - if src_id in self.sap_identifiers: - src_docker_name = "{0}_{1}".format(src_id, src_if_name) - src_id = src_docker_name - else: - src_docker_name = src_id - - if dst_id in self.sap_identifiers: - dst_docker_name = "{0}_{1}".format(dst_id, dst_if_name) - dst_id = dst_docker_name - else: - dst_docker_name = dst_id - - src_name = vnf_id2vnf_name[src_id] - dst_name = vnf_id2vnf_name[dst_id] - - LOG.debug( - "Setting up E-Line link. %s(%s:%s) -> %s(%s:%s)" % ( - src_name, src_id, src_if_name, dst_name, dst_id, dst_if_name)) - - if (src_name in self.vnfds) and (dst_name in self.vnfds): - network = self.vnfds[src_name].get("dc").net # there should be a cleaner way to find the DCNetwork - LOG.debug(src_docker_name) - ret = network.setChain( - src_docker_name, dst_docker_name, - vnf_src_interface=src_if_name, vnf_dst_interface=dst_if_name, - bidirectional=BIDIRECTIONAL_CHAIN, cmd="add-flow", cookie=cookie, priority=10) - - # re-configure the VNFs IP assignment and ensure that a new subnet is used for each E-Link - src_vnfi = self._get_vnf_instance(instance_uuid, src_name) - if src_vnfi is not None: - self._vnf_reconfigure_network(src_vnfi, src_if_name, self.eline_subnets_src.pop(0)) - dst_vnfi = self._get_vnf_instance(instance_uuid, dst_name) - if dst_vnfi is not None: - self._vnf_reconfigure_network(dst_vnfi, dst_if_name, self.eline_subnets_dst.pop(0)) - - # 4b. deploy E-LAN links - base = 10 - for link in elan_fwd_links: - - elan_vnf_list=[] - - # generate lan ip address - ip = 1 - for intf in link["connection_points_reference"]: - ip_address = generate_lan_string("10.0", base, subnet_size=24, ip=ip) - vnf_id, intf_name = intf.split(":") - if vnf_id in self.sap_identifiers: - src_docker_name = "{0}_{1}".format(vnf_id, intf_name) - vnf_id = src_docker_name + if "virtual_links" in self.nsd: + vlinks = self.nsd["virtual_links"] + fwd_links = self.nsd["forwarding_graphs"][0]["constituent_virtual_links"] + eline_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-Line")] + elan_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-LAN")] + ++ GK.net.deployed_elines.extend(eline_fwd_links) ++ GK.net.deployed_elans.extend(elan_fwd_links) ++ + # 4a. deploy E-Line links + # cookie is used as identifier for the flowrules installed by the dummygatekeeper + # eg. different services get a unique cookie for their flowrules + cookie = 1 + for link in eline_fwd_links: + src_id, src_if_name = link["connection_points_reference"][0].split(":") + dst_id, dst_if_name = link["connection_points_reference"][1].split(":") + + # check if there is a SAP in the link + if src_id in self.sap_identifiers: + src_docker_name = "{0}_{1}".format(src_id, src_if_name) + src_id = src_docker_name else: - src_docker_name = vnf_id - vnf_name = vnf_id2vnf_name[vnf_id] - LOG.debug( - "Setting up E-LAN link. %s(%s:%s) -> %s" % ( - vnf_name, vnf_id, intf_name, ip_address)) - - if vnf_name in self.vnfds: - # re-configure the VNFs IP assignment and ensure that a new subnet is used for each E-LAN - # E-LAN relies on the learning switch capability of Ryu which has to be turned on in the topology - # (DCNetwork(controller=RemoteController, enable_learning=True)), so no explicit chaining is necessary. - vnfi = self._get_vnf_instance(instance_uuid, vnf_name) - if vnfi is not None: - self._vnf_reconfigure_network(vnfi, intf_name, ip_address) - # increase for the next ip address on this E-LAN - ip += 1 + src_docker_name = src_id - # add this vnf and interface to the E-LAN for tagging - network = self.vnfds[vnf_name].get("dc").net # there should be a cleaner way to find the DCNetwork - elan_vnf_list.append({'name':src_docker_name,'interface':intf_name}) + if dst_id in self.sap_identifiers: + dst_docker_name = "{0}_{1}".format(dst_id, dst_if_name) + dst_id = dst_docker_name + else: + dst_docker_name = dst_id + src_name = vnf_id2vnf_name[src_id] + dst_name = vnf_id2vnf_name[dst_id] - # install the VLAN tags for this E-LAN - network.setLAN(elan_vnf_list) - # increase the base ip address for the next E-LAN - base += 1 + LOG.debug( + "Setting up E-Line link. %s(%s:%s) -> %s(%s:%s)" % ( + src_name, src_id, src_if_name, dst_name, dst_id, dst_if_name)) + + if (src_name in self.vnfds) and (dst_name in self.vnfds): + network = self.vnfds[src_name].get("dc").net # there should be a cleaner way to find the DCNetwork + LOG.debug(src_docker_name) + ret = network.setChain( + src_docker_name, dst_docker_name, + vnf_src_interface=src_if_name, vnf_dst_interface=dst_if_name, + bidirectional=BIDIRECTIONAL_CHAIN, cmd="add-flow", cookie=cookie, priority=10) + + # re-configure the VNFs IP assignment and ensure that a new subnet is used for each E-Link + src_vnfi = self._get_vnf_instance(instance_uuid, src_name) + if src_vnfi is not None: + self._vnf_reconfigure_network(src_vnfi, src_if_name, self.eline_subnets_src.pop(0)) + dst_vnfi = self._get_vnf_instance(instance_uuid, dst_name) + if dst_vnfi is not None: + self._vnf_reconfigure_network(dst_vnfi, dst_if_name, self.eline_subnets_dst.pop(0)) + + # 4b. deploy E-LAN links + base = 10 + for link in elan_fwd_links: ++ ++ elan_vnf_list=[] ++ + # generate lan ip address + ip = 1 + for intf in link["connection_points_reference"]: + ip_address = generate_lan_string("10.0", base, subnet_size=24, ip=ip) + vnf_id, intf_name = intf.split(":") + if vnf_id in self.sap_identifiers: + src_docker_name = "{0}_{1}".format(vnf_id, intf_name) + vnf_id = src_docker_name ++ else: ++ src_docker_name = vnf_id + vnf_name = vnf_id2vnf_name[vnf_id] + LOG.debug( + "Setting up E-LAN link. %s(%s:%s) -> %s" % ( + vnf_name, vnf_id, intf_name, ip_address)) + + if vnf_name in self.vnfds: + # re-configure the VNFs IP assignment and ensure that a new subnet is used for each E-LAN + # E-LAN relies on the learning switch capability of Ryu which has to be turned on in the topology + # (DCNetwork(controller=RemoteController, enable_learning=True)), so no explicit chaining is necessary. + vnfi = self._get_vnf_instance(instance_uuid, vnf_name) + if vnfi is not None: + self._vnf_reconfigure_network(vnfi, intf_name, ip_address) + # increase for the next ip address on this E-LAN + ip += 1 ++ ++ # add this vnf and interface to the E-LAN for tagging ++ network = self.vnfds[vnf_name].get("dc").net # there should be a cleaner way to find the DCNetwork ++ elan_vnf_list.append({'name':src_docker_name,'interface':intf_name}) ++ ++ ++ # install the VLAN tags for this E-LAN ++ network.setLAN(elan_vnf_list) + # increase the base ip address for the next E-LAN + base += 1 # 5. run the emulator specific entrypoint scripts in the VNFIs of this service instance self._trigger_emulator_start_scripts_in_vnfis(self.instances[instance_uuid]["vnf_instances"]) @@@ -318,7 -302,32 +319,32 @@@ assert(target_dc is not None) if not self._check_docker_image_exists(docker_name): raise Exception("Docker image %r not found. Abort." % docker_name) - # 3. do the dc.startCompute(name="foobar") call to run the container + + # 3. get the resource limits + res_req = u.get("resource_requirements") + cpu_list = res_req.get("cpu").get("cores") + if not cpu_list or len(cpu_list)==0: + cpu_list="1" + cpu_bw = res_req.get("cpu").get("cpu_bw") + if not cpu_bw: + cpu_bw=1 + mem_num = str(res_req.get("memory").get("size")) + if len(mem_num)==0: + mem_num="2" + mem_unit = str(res_req.get("memory").get("size_unit")) + if str(mem_unit)==0: + mem_unit="GB" + mem_limit = float(mem_num) + if mem_unit=="GB": + mem_limit=mem_limit*1024*1024*1024 + elif mem_unit=="MB": + mem_limit=mem_limit*1024*1024 + elif mem_unit=="KB": + mem_limit=mem_limit*1024 + mem_lim = int(mem_limit) + cpu_period, cpu_quota = self._calculate_cpu_cfs_values(float(cpu_bw)) + + # 4. do the dc.startCompute(name="foobar") call to run the container # TODO consider flavors, and other annotations intfs = vnfd.get("connection_points") @@@ -334,7 -343,8 +360,8 @@@ LOG.info("Starting %r as %r in DC %r" % (vnf_name, self.vnf_name2docker_name[vnf_name], vnfd.get("dc"))) LOG.debug("Interfaces for %r: %r" % (vnf_name, intfs)) - vnfi = target_dc.startCompute(self.vnf_name2docker_name[vnf_name], network=intfs, image=docker_name, flavor_name="small") + vnfi = target_dc.startCompute(self.vnf_name2docker_name[vnf_name], network=intfs, image=docker_name, flavor_name="small", + cpu_quota=cpu_quota, cpu_period=cpu_period, cpuset=cpu_list, mem_limit=mem_lim) return vnfi def _stop_vnfi(self, vnfi): @@@ -389,8 -399,9 +416,9 @@@ config = vnfi.dcinfo.get("Config", dict()) env = config.get("Env", list()) for env_var in env: - if "SON_EMU_CMD=" in env_var: - cmd = str(env_var.split("=")[1]) + var, cmd = map(str.strip, map(str, env_var.split('=', 1))) + LOG.debug("%r = %r" % (var , cmd)) + if var=="SON_EMU_CMD": LOG.info("Executing entry point script in %r: %r" % (vnfi.name, cmd)) # execute command in new thread to ensure that GK is not blocked by VNF t = threading.Thread(target=vnfi.cmdPrint, args=(cmd,)) @@@ -425,7 -436,6 +453,7 @@@ self.package_content_path, make_relative_path(self.manifest.get("entry_service_template"))) self.nsd = load_yaml(nsd_path) + GK.net.deployed_nsds.append(self.nsd) LOG.debug("Loaded NSD: %r" % self.nsd.get("name")) def _load_vnfd(self): @@@ -514,7 -524,7 +542,7 @@@ dc = DockerClient() for url in self.remote_docker_image_urls.itervalues(): if not FORCE_PULL: # only pull if not present (speedup for development) - if len(dc.images(name=url)) > 0: + if len(dc.images.list(name=url)) > 0: LOG.debug("Image %r present. Skipping pull." % url) continue LOG.info("Pulling image: %r" % url) @@@ -527,7 -537,7 +555,7 @@@ :param image_name: name of the docker image :return: """ - return len(DockerClient().images(image_name)) > 0 + return len(DockerClient().images.list(name=image_name)) > 0 def _calculate_placement(self, algorithm): """ @@@ -545,6 -555,29 +573,29 @@@ for name, vnfd in self.vnfds.iteritems(): LOG.info("Placed VNF %r on DC %r" % (name, str(vnfd.get("dc")))) + def _calculate_cpu_cfs_values(self, cpu_time_percentage): + """ + Calculate cpu period and quota for CFS + :param cpu_time_percentage: percentage of overall CPU to be used + :return: cpu_period, cpu_quota + """ + if cpu_time_percentage is None: + return -1, -1 + if cpu_time_percentage < 0: + return -1, -1 + # (see: https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt) + # Attention minimum cpu_quota is 1ms (micro) + cpu_period = 1000000 # lets consider a fixed period of 1000000 microseconds for now + LOG.debug("cpu_period is %r, cpu_percentage is %r" % (cpu_period, cpu_time_percentage)) + cpu_quota = cpu_period * cpu_time_percentage # calculate the fraction of cpu time for this container + # ATTENTION >= 1000 to avoid a invalid argument system error ... no idea why + if cpu_quota < 1000: + LOG.debug("cpu_quota before correcting: %r" % cpu_quota) + cpu_quota = 1000 + LOG.warning("Increased CPU quota to avoid system error.") + LOG.debug("Calculated: cpu_period=%f / cpu_quota=%f" % (cpu_period, cpu_quota)) + return int(cpu_period), int(cpu_quota) + """ Some (simple) placement algorithms @@@ -676,8 -709,7 +727,7 @@@ class Instantiations(fr.Resource) if service_uuid in GK.services and instance_uuid in GK.services[service_uuid].instances: # valid service and instance UUID, stop service GK.services.get(service_uuid).stop_service(instance_uuid) - del GK.services.get(service_uuid).instances[instance_uuid] - return + return "service instance with uuid %r stopped." % instance_uuid,200 return "Service not found", 404 class Exit(fr.Resource): @@@ -686,7 -718,7 +736,7 @@@ """ Stop the running Containernet instance regardless of data transmitted """ - GK.net.stop() + list(GK.dcs.values())[0].net.stop() def initialize_GK(): @@@ -715,7 -747,6 +765,7 @@@ api.add_resource(Exit, '/emulator/exit' def start_rest_api(host, port, datacenters=dict()): GK.dcs = datacenters + GK.net = get_dc_network() # start the Flask server (not the best performance but ok for our use case) app.run(host=host, port=port, @@@ -764,14 -795,6 +814,14 @@@ def generate_subnet_strings(n, start=1 r.append("%d.0.0.%d/%d" % (i, ip, subnet_size)) return r +def get_dc_network(): + """ + retrieve the DCnetwork where this dummygatekeeper (GK) connects to. + Assume at least 1 datacenter is connected to this GK, and that all datacenters belong to the same DCNetwork + :return: + """ + assert (len(GK.dcs) > 0) + return GK.dcs.values()[0].net if __name__ == '__main__': """ diff --combined src/emuvim/dcemulator/net.py index 191d6a8,faf758c..b0838e0 --- a/src/emuvim/dcemulator/net.py +++ b/src/emuvim/dcemulator/net.py @@@ -32,7 -32,6 +32,7 @@@ import tim from subprocess import Popen import re import requests +import os from mininet.net import Containernet from mininet.node import Controller, DefaultController, OVSSwitch, OVSKernelSwitch, Docker, RemoteController @@@ -47,9 -46,6 +47,9 @@@ from emuvim.dcemulator.resourcemodel im LOG = logging.getLogger("dcemulator.net") LOG.setLevel(logging.DEBUG) +# default CPU period used for cpu percentage-based cfs values (microseconds) +CPU_PERIOD = 1000000 + class DCNetwork(Containernet): """ Wraps the original Mininet/Containernet class and provides @@@ -59,7 -55,7 +59,7 @@@ """ def __init__(self, controller=RemoteController, monitor=False, - enable_learning = True, # in case of RemoteController (Ryu), learning switch behavior can be turned off/on + enable_learning=False, # learning switch behavior of the default ovs switches icw Ryu controller can be turned off/on, needed for E-LAN functionality dc_emulation_max_cpu=1.0, # fraction of overall CPU time for emulation dc_emulation_max_mem=512, # emulation max mem in MB **kwargs): @@@ -72,12 -68,6 +72,12 @@@ # members self.dcs = {} self.ryu_process = None + #list of deployed nsds.E_Lines and E_LANs (uploaded from the dummy gatekeeper) + self.deployed_nsds = [] + self.deployed_elines = [] + self.deployed_elans = [] + self.installed_chains = [] + # always cleanup environment before we start the emulator self.killRyu() @@@ -87,18 -77,10 +87,18 @@@ Containernet.__init__( self, switch=OVSKernelSwitch, controller=controller, **kwargs) + # default switch configuration + enable_ryu_learning = False + if enable_learning : + self.failMode = 'standalone' + enable_ryu_learning = True + else: + self.failMode = 'secure' + # Ryu management if controller == RemoteController: # start Ryu controller - self.startRyu(learning_switch=enable_learning) + self.startRyu(learning_switch=enable_ryu_learning) # add the specified controller self.addController('c0', controller=controller) @@@ -124,7 -106,6 +124,7 @@@ # initialize resource model registrar self.rm_registrar = ResourceModelRegistrar( dc_emulation_max_cpu, dc_emulation_max_mem) + self.cpu_period = CPU_PERIOD def addDatacenter(self, label, metadata={}, resource_log_path=None): """ @@@ -146,7 -127,7 +146,7 @@@ """ assert node1 is not None assert node2 is not None - 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,9 -205,6 +224,9 @@@ attr_dict2.update(attr_dict) self.DCNetwork_graph.add_edge(node2.name, node1.name, attr_dict=attr_dict2) + LOG.debug("addLink: n1={0} intf1={1} -- n2={2} intf2={3}".format( + str(node1),node1_port_name, str(node2), node2_port_name)) + return link def addDocker( self, label, **params ): @@@ -247,26 -225,9 +247,26 @@@ """ Wrapper for addSwitch method to store switch also in graph. """ + + # add this switch to the global topology overview if add_to_graph: self.DCNetwork_graph.add_node(name) - return Containernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', **params) + + # set the learning switch behavior + if 'failMode' in params : + failMode = params['failMode'] + else : + failMode = self.failMode + + s = Containernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', failMode=failMode, **params) + + # set flow entry that enables learning switch behavior (needed to enable E-LAN functionality) + #LOG.info('failmode {0}'.format(failMode)) + #if failMode == 'standalone' : + # LOG.info('add NORMAL') + # s.dpctl('add-flow', 'actions=NORMAL') + + return s def getAllContainers(self): """ @@@ -299,193 -260,6 +299,193 @@@ def CLI(self): CLI(self) + def setLAN(self, vnf_list): + """ + setup an E-LAN network by assigning the same VLAN tag to each DC interface of the VNFs in the E-LAN + + :param vnf_list: names of the VNFs in this E-LAN [{name:,interface:},...] + :return: + """ + src_sw = None + src_sw_inport_nr = 0 + src_sw_inport_name = None + + # get a vlan tag for this E-LAN + vlan = self.vlans.pop() + + for vnf in vnf_list: + vnf_src_name = vnf['name'] + vnf_src_interface = vnf['interface'] + + # 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] + link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw] + vnf_src_interface = link_dict[0]['src_port_id'] + + 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 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'] + src_sw_inport_name = link_dict[link]['dst_port_name'] + break + + # set the tag on the dc switch interface + LOG.debug('set E-LAN: vnf name: {0} interface: {1} tag: {2}'.format(vnf_src_name, vnf_src_interface,vlan)) + switch_node = self.getNodeByName(src_sw) + self._set_vlan_tag(switch_node, src_sw_inport_name, vlan) + + def _addMonitorFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, + tag=None, **kwargs): + """ + Add a monitoring flow entry that adds a special flowentry/counter at the begin or end of a chain. + So this monitoring flowrule exists on top of a previously defined chain rule and uses the same vlan tag/routing. + :param vnf_src_name: + :param vnf_dst_name: + :param vnf_src_interface: + :param vnf_dst_interface: + :param tag: vlan tag to be used for this chain (same tag as existing chain) + :param monitor_placement: 'tx' or 'rx' indicating to place the extra flowentry resp. at the beginning or end of the chain + :return: + """ + + src_sw = None + src_sw_inport_nr = 0 + src_sw_inport_name = None + dst_sw = None + dst_sw_outport_nr = 0 + dst_sw_outport_name = None + + LOG.debug("call AddMonitorFlow 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 + connected_sw = 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'] + + 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 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'] + src_sw_inport_name = link_dict[link]['dst_port_name'] + break + + if vnf_dst_interface is None: + # take first interface by default + connected_sw = 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'] + + vnf_dst_name = vnf_dst_name.split(':')[0] + 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 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'] + dst_sw_outport_name = link_dict[link]['src_port_name'] + break + + if not tag >= 0: + LOG.exception('tag not valid: {0}'.format(tag)) + + # get shortest path + try: + # returns the first found shortest path + # 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: + 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) + + 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 + + cmd = kwargs.get('cmd') + + #iterate through the path to install the flow-entries + for i in range(0,len(path)): + current_node = self.getNodeByName(current_hop) + + if path.index(current_hop) < len(path)-1: + next_hop = path[path.index(current_hop)+1] + else: + #last switch reached + next_hop = vnf_dst_name + + next_node = self.getNodeByName(next_hop) + + if next_hop == vnf_dst_name: + switch_outport_nr = dst_sw_outport_nr + LOG.info("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) + else: + # take first link between switches by default + index_edge_out = 0 + switch_outport_nr = self.DCNetwork_graph[current_hop][next_hop][index_edge_out]['src_port_nr'] + + + # set of entry via ovs-ofctl + if isinstance( current_node, OVSSwitch ): + kwargs['vlan'] = tag + kwargs['path'] = path + kwargs['current_hop'] = current_hop + kwargs['switch_inport_name'] = src_sw_inport_name + kwargs['switch_outport_name'] = dst_sw_outport_name + kwargs['skip_vlan_tag'] = True + + monitor_placement = kwargs.get('monitor_placement').strip() + # put monitor flow at the dst switch + insert_flow = False + if monitor_placement == 'tx' and path.index(current_hop) == 0: # first node: + insert_flow = True + # put monitoring flow at the src switch + elif monitor_placement == 'rx' and path.index(current_hop) == len(path) - 1: # last node: + insert_flow = True + elif monitor_placement not in ['rx', 'tx']: + LOG.exception('invalid monitor command: {0}'.format(monitor_placement)) + + + if self.controller == RemoteController and insert_flow: + ## set flow entry via ryu rest api + self._set_flow_entry_ryu_rest(current_node, switch_inport_nr, switch_outport_nr, **kwargs) + break + elif insert_flow: + ## set flow entry via ovs-ofctl + self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs) + break + + # 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 {2} between {0} and {1}".format(vnf_src_name, vnf_dst_name, cmd) + + def setChain(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, **kwargs): """ Chain 2 vnf interfaces together by installing the flowrules in the switches along their path. @@@ -500,41 -274,24 +500,47 @@@ :param cookie: cookie for the installed flowrules (can be used later as identifier for a set of installed chains) :param match: custom match entry to be added to the flowrules (default: only in_port and vlan tag) :param priority: custom flowrule priority ++<<<<<<< HEAD + :param monitor: boolean to indicate whether this chain is a monitoring chain + :param tag: vlan tag to be used for this chain (pre-defined or new one if none is specified) ++======= + :param path: custom path between the two VNFs (list of switches) ++>>>>>>> upstream/master :return: output log string """ + + # special procedure for monitoring flows + if kwargs.get('monitor'): + + # check if chain already exists + found_chains = [chain_dict for chain_dict in self.installed_chains if + (chain_dict['vnf_src_name'] == vnf_src_name and chain_dict['vnf_src_interface'] == vnf_src_interface + and chain_dict['vnf_dst_name'] == vnf_dst_name and chain_dict['vnf_dst_interface'] == vnf_dst_interface)] + + if len(found_chains) > 0: + # this chain exists, so need an extra monitoring flow + # assume only 1 chain per vnf/interface pair + LOG.debug('*** installing monitoring chain on top of pre-defined chain from {0}:{1} -> {2}:{3}'. + format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface)) + tag = found_chains[0]['tag'] + ret = self._addMonitorFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, + tag=tag, table_id=0, **kwargs) + return ret + else: + # no chain existing (or E-LAN) -> install normal chain + LOG.warning('*** installing monitoring chain without pre-defined NSD chain from {0}:{1} -> {2}:{3}'. + format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface)) + pass + + cmd = kwargs.get('cmd') - if cmd == 'add-flow': + if cmd == 'add-flow' or cmd == 'del-flows': ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs) if kwargs.get('bidirectional'): + if kwargs.get('path') is not None: + kwargs['path'] = list(reversed(kwargs.get('path'))) ret = ret +'\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs) - elif cmd == 'del-flows': - ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs) - if kwargs.get('bidirectional'): - if kwargs.get('path') is not None: - kwargs['path'] = list(reversed(kwargs.get('path'))) - ret = ret + '\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs) - else: ret = "Command unknown" @@@ -544,11 -301,9 +550,11 @@@ def _chainAddFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, **kwargs): src_sw = None - dst_sw = None src_sw_inport_nr = 0 + src_sw_inport_name = None + dst_sw = None dst_sw_outport_nr = 0 + dst_sw_outport_name = None 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) @@@ -568,7 -323,6 +574,7 @@@ # found the right link and connected switch src_sw = connected_sw src_sw_inport_nr = link_dict[link]['dst_port_nr'] + src_sw_inport_name = link_dict[link]['dst_port_name'] break if vnf_dst_interface is None: @@@ -586,58 -340,43 +592,59 @@@ # found the right link and connected switch dst_sw = connected_sw dst_sw_outport_nr = link_dict[link]['src_port_nr'] + dst_sw_outport_name = link_dict[link]['src_port_name'] break - - # get shortest path - try: - # returns the first found shortest path - # 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: - 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) + path = kwargs.get('path') + if path is None: + # get shortest path + try: + # returns the first found shortest path + # 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: + 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) 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 - # choose free vlan if path contains more than 1 switch + # choose free vlan + ## if path contains more than 1 switch cmd = kwargs.get('cmd') vlan = None if cmd == 'add-flow': - if len(path) > 1: + if kwargs.get('tag'): + # use pre-defined tag + vlan = kwargs.get('tag') + else: vlan = self.vlans.pop() + # store the used vlan tag to identify this chain + if not kwargs.get('monitor'): + chain_dict = {} + chain_dict['vnf_src_name'] = vnf_src_name + chain_dict['vnf_dst_name'] = vnf_dst_name + chain_dict['vnf_src_interface'] = vnf_src_interface + chain_dict['vnf_dst_interface'] = vnf_dst_interface + chain_dict['tag'] = vlan + self.installed_chains.append(chain_dict) + + #iterate through the path to install the flow-entries for i in range(0,len(path)): current_node = self.getNodeByName(current_hop) - if path.index(current_hop) < len(path)-1: - next_hop = path[path.index(current_hop)+1] + if i < len(path) - 1: + next_hop = path[i + 1] else: - #last switch reached + # last switch reached next_hop = vnf_dst_name next_node = self.getNodeByName(next_hop) @@@ -658,9 -397,7 +665,10 @@@ if isinstance( current_node, OVSSwitch ): kwargs['vlan'] = vlan kwargs['path'] = path + kwargs['current_hop'] = current_hop + kwargs['switch_inport_name'] = src_sw_inport_name + kwargs['switch_outport_name'] = dst_sw_outport_name + kwargs['pathindex'] = i if self.controller == RemoteController: ## set flow entry via ryu rest api @@@ -669,6 -406,8 +677,6 @@@ ## 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 ): switch_inport_nr = self.DCNetwork_graph[current_hop][next_hop][0]['dst_port_nr'] @@@ -683,15 -422,10 +691,16 @@@ match_input = kwargs.get('match') cmd = kwargs.get('cmd') path = kwargs.get('path') - current_hop = kwargs.get('current_hop') + index = kwargs.get('pathindex') + vlan = kwargs.get('vlan') priority = kwargs.get('priority') + # flag to not set the ovs port vlan tag + skip_vlan_tag = kwargs.get('skip_vlan_tag') + # table id to put this flowentry + table_id = kwargs.get('table_id') + if not table_id: + table_id = 0 s = ',' if match_input: @@@ -705,8 -439,6 +714,8 @@@ if priority: flow['priority'] = int(priority) + flow['table_id'] = table_id + flow['actions'] = [] # possible Ryu actions, match fields: @@@ -714,38 -446,24 +723,39 @@@ if cmd == 'add-flow': prefix = 'stats/flowentry/add' if vlan != None: - if path.index(current_hop) == 0: # first node + if index == 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' - # ryu expects the field to be masked - action['value'] = vlan | 0x1000 - flow['actions'].append(action) - elif index == len(path) -1: # last node - match += ',dl_vlan=%s' % vlan - action = {} - action['type'] = 'POP_VLAN' - flow['actions'].append(action) + # set vlan tag in ovs instance (to isolate E-LANs) + if not skip_vlan_tag: + in_port_name = kwargs.get('switch_inport_name') + self._set_vlan_tag(node, in_port_name, vlan) + # set vlan push action if more than 1 switch in the path + if len(path) > 1: + 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 ++ # ryu expects the field to be masked ++ action['value'] = vlan | 0x1000 + flow['actions'].append(action) + - if path.index(current_hop) == len(path) - 1: # last node ++ elif index == len(path) - 1: # last node + # set vlan tag in ovs instance (to isolate E-LANs) + if not skip_vlan_tag: + out_port_name = kwargs.get('switch_outport_name') + self._set_vlan_tag(node, out_port_name, vlan) + # set vlan pop action if more than 1 switch in the path + if len(path) > 1: + match += ',dl_vlan=%s' % vlan + action = {} + action['type'] = 'POP_VLAN' + flow['actions'].append(action) + - if 0 < path.index(current_hop) < (len(path) - 1): # middle nodes + else: # middle nodes match += ',dl_vlan=%s' % vlan + # output action must come last action = {} action['type'] = 'OUTPUT' @@@ -767,19 -485,14 +777,19 @@@ flow['match'] = self._parse_match(match) self.ryu_REST(prefix, data=flow) + def _set_vlan_tag(self, node, switch_port, tag): + node.vsctl('set', 'port {0} tag={1}'.format(switch_port,tag)) + LOG.debug("set vlan in switch: {0} in_port: {1} vlan tag: {2}".format(node.name, switch_port, tag)) + def _set_flow_entry_dpctl(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') + index = kwargs.get('pathindex') vlan = kwargs.get('vlan') s = ',' @@@ -791,10 -504,10 +801,10 @@@ if cmd == 'add-flow': action = 'action=%s' % switch_outport_nr if vlan != None: - if path.index(current_hop) == 0: # first node + if index == 0: # first node action = ('action=mod_vlan_vid:%s' % vlan) + (',output=%s' % switch_outport_nr) match = '-O OpenFlow13 ' + match - elif path.index(current_hop) == len(path) - 1: # last node + elif index == len(path) - 1: # last node match += ',dl_vlan=%s' % vlan action = 'action=strip_vlan,output=%s' % switch_outport_nr else: # middle nodes @@@ -813,11 -526,7 +823,11 @@@ 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' + # ryu default learning switch + #ryu_path = 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' # change the default Openflow controller port to 6653 (official IANA-assigned port number), as used by Mininet # Ryu still uses 6633 as default @@@ -827,12 -536,9 +837,12 @@@ 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)) 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)) time.sleep(1) def killRyu(self): @@@ -894,14 -600,3 +904,14 @@@ dict.update({match[0]:m2}) return dict + def find_connected_dc_interface(self, vnf_src_name, vnf_src_interface): + 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 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'] + src_sw_inport_name = link_dict[link]['dst_port_name'] + return src_sw_inport_name diff --combined src/emuvim/dcemulator/node.py index 8a7be0a,a0112cf..fd4b8fb --- a/src/emuvim/dcemulator/node.py +++ b/src/emuvim/dcemulator/node.py @@@ -38,6 -38,7 +38,6 @@@ LOG.setLevel(logging.DEBUG DCDPID_BASE = 1000 # start of switch dpid's used for data center switches - class EmulatorCompute(Docker): """ Emulator specific compute node class. @@@ -60,17 -61,9 +60,17 @@@ Helper method to receive information about the virtual networks this compute instance is connected to. """ - # format list of tuples (name, Ip, MAC, isUp, status) - return [{'intf_name':str(i), 'ip':i.IP(), 'mac':i.MAC(), 'up':i.isUp(), 'status':i.status()} - for i in self.intfList()] + # get all links and find dc switch interface + networkStatusList = [] + for i in self.intfList(): + vnf_name = self.name + vnf_interface = str(i) + dc_port_name = self.datacenter.net.find_connected_dc_interface(vnf_name, vnf_interface) + # format list of tuples (name, Ip, MAC, isUp, status, dc_portname) + intf_dict = {'intf_name': str(i), 'ip': i.IP(), 'mac': i.MAC(), 'up': i.isUp(), 'status': i.status(), 'dc_portname': dc_port_name} + networkStatusList.append(intf_dict) + + return networkStatusList def getStatus(self): """ @@@ -90,7 -83,6 +90,7 @@@ status["memswap_limit"] = self.memswap_limit status["state"] = self.dcli.inspect_container(self.dc)["State"] status["id"] = self.dcli.inspect_container(self.dc)["Id"] + status["short_id"] = self.dcli.inspect_container(self.dc)["Id"][:12] status["datacenter"] = (None if self.datacenter is None else self.datacenter.label) return status @@@ -113,7 -105,7 +113,7 @@@ class Datacenter(object) self.name = "dc%d" % Datacenter.DC_COUNTER Datacenter.DC_COUNTER += 1 # use this for user defined names that can be longer than self.name - self.label = label + self.label = label # dict to store arbitrary metadata (e.g. latitude and longitude) self.metadata = metadata # path to which resource information should be logged (e.g. for experiments). None = no logging @@@ -148,7 -140,7 +148,7 @@@ def start(self): pass - def startCompute(self, name, image=None, command=None, network=None, flavor_name="tiny", **kwargs): + def startCompute(self, name, image=None, command=None, network=None, flavor_name="tiny", **params): """ Create a new container as compute resource and connect it to this data center. @@@ -174,15 -166,6 +174,15 @@@ if len(network) < 1: network.append({}) + # apply hard-set resource limits=0 + cpu_percentage = kwargs.get('cpu_percent') + if cpu_percentage: + cpu_period = self.net.cpu_period + cpu_quota = self.net.cpu_period * float(cpu_percentage) + else: + cpu_quota = None + cpu_period = None + # create the container d = self.net.addDocker( "%s" % (name), @@@ -190,13 -173,9 +190,14 @@@ dcmd=command, datacenter=self, flavor_name=flavor_name, + cpu_period = cpu_period, + cpu_quota = cpu_quota, + environment = {'VNF_NAME':name} + **params ) + + # apply resource limits to container if a resource model is defined if self._resource_model is not None: try: diff --combined src/emuvim/test/unittests/test_sonata_dummy_gatekeeper.py index 053fdb6,c96e1ba..bfa9541 --- a/src/emuvim/test/unittests/test_sonata_dummy_gatekeeper.py +++ b/src/emuvim/test/unittests/test_sonata_dummy_gatekeeper.py @@@ -44,7 -44,7 +44,7 @@@ class testSonataDummyGatekeeper(SimpleT # @unittest.skip("disabled") def test_GK_Api_start_service(self): # create network - self.createNet(nswitches=0, ndatacenter=2, nhosts=2, ndockers=0) + self.createNet(nswitches=0, ndatacenter=2, nhosts=2, ndockers=0, enable_learning=True) # setup links self.net.addLink(self.dc[0], self.h[0]) self.net.addLink(self.dc[0], self.dc[1]) @@@ -87,42 -87,14 +87,42 @@@ # check compute list result self.assertEqual(len(self.dc[0].listCompute()), 2) # check connectivity by using ping + ELAN_list=[] + for i in [0]: + for vnf in self.dc[i].listCompute(): + # check connection + p = self.net.ping([self.h[i], vnf]) + print p + self.assertTrue(p <= 0.0) + + # check E LAN connection + network_list = vnf.getNetworkStatus() + mgmt_ip = [intf['ip'] for intf in network_list if intf['intf_name'] == 'mgmt'] + self.assertTrue(len(mgmt_ip) > 0) + ip_address = mgmt_ip[0] + ELAN_list.append(ip_address) + print ip_address + + # check ELAN connection by ping over the mgmt network (needs to be configured as ELAN in the test service) for vnf in self.dc[0].listCompute(): - p = self.net.ping([self.h[0], vnf]) - self.assertTrue(p <= 50.0) + network_list = vnf.getNetworkStatus() + mgmt_ip = [intf['ip'] for intf in network_list if intf['intf_name'] == 'mgmt'] + self.assertTrue(len(mgmt_ip) > 0) + ip_address = mgmt_ip[0] + print ELAN_list + print ip_address + test_ip_list = list(ELAN_list) + test_ip_list.remove(ip_address) + for ip in test_ip_list: + p = self.net.ping([vnf],manualdestip=ip) + print p + self.assertTrue(p <= 0.0) + # stop Mininet network self.stopNet() initialize_GK() - + # @unittest.skip("disabled") def test_GK_Api_stop_service(self): # create network self.createNet(ndatacenter=2, nhosts=2) @@@ -179,3 -151,51 +179,51 @@@ # stop Mininet network self.stopNet() initialize_GK() + + def test_GK_stress_service(self): + # create network + self.createNet(ndatacenter=2, nhosts=2) + # connect dummy GK to data centers + sdkg1 = SonataDummyGatekeeperEndpoint("0.0.0.0", 5000) + sdkg1.connectDatacenter(self.dc[0]) + sdkg1.connectDatacenter(self.dc[1]) + # run the dummy gatekeeper (in another thread, don't block) + sdkg1.start() + # start Mininet network + self.startNet() + time.sleep(1) + + print "starting tests" + # board package + files = {"package": open("misc/sonata-stress-service.son", "rb")} + r = requests.post("http://127.0.0.1:5000/packages", files=files) + self.assertEqual(r.status_code, 201) + self.assertTrue(json.loads(r.text).get("service_uuid") is not None) + + # instantiate service + self.service_uuid = json.loads(r.text).get("service_uuid") + r2 = requests.post("http://127.0.0.1:5000/instantiations", data=json.dumps({"service_uuid": self.service_uuid})) + self.assertEqual(r2.status_code, 201) + + # give the emulator some time to instantiate everything + time.sleep(2) + + # check get request APIs + r3 = requests.get("http://127.0.0.1:5000/packages") + self.assertEqual(len(json.loads(r3.text).get("service_uuid_list")), 1) + r4 = requests.get("http://127.0.0.1:5000/instantiations") + self.assertEqual(len(json.loads(r4.text).get("service_instantiations_list")), 1) + + # stop the service + service_instance_uuid = json.loads(r2.text).get("service_instance_uuid") + self.assertTrue(service_instance_uuid is not None) + requests.delete("http://127.0.0.1:5000/instantiations", data=json.dumps({"service_uuid": self.service_uuid, "service_instance_uuid":service_instance_uuid})) + + r5 = requests.get("http://127.0.0.1:5000/instantiations") + self.assertTrue(len(json.loads(r5.text).get("service_instantiations_list")), 0) # note that there was 1 instance before + + # stop Mininet network + self.stopNet() + initialize_GK() + +