+ if BUILD_DOCKERFILE:
+ self._load_docker_files()
+ self._build_images_from_dockerfiles()
+ else:
+ self._load_docker_urls()
+ self._pull_predefined_dockerimages()
+ LOG.info("On-boarded service: %r" % self.manifest.get("name"))
+
+ def start_service(self):
+ """
+ This methods creates and starts a new service instance.
+ It computes placements, iterates over all VNFDs, and starts
+ each VNFD as a Docker container in the data center selected
+ by the placement algorithm.
+ :return:
+ """
+ LOG.info("Starting service %r" % self.uuid)
+
+ # 1. each service instance gets a new uuid to identify it
+ instance_uuid = str(uuid.uuid4())
+ # build a instances dict (a bit like a NSR :))
+ self.instances[instance_uuid] = dict()
+ self.instances[instance_uuid]["vnf_instances"] = list()
+
+ # 2. Configure the chaining of the network functions (currently only E-Line and E-LAN links supported)
+ vnf_id2vnf_name = defaultdict(lambda: "NotExistingNode",
+ reduce(lambda x, y: dict(x, **y),
+ map(lambda d: {d["vnf_id"]: d["vnf_name"]},
+ self.nsd["network_functions"])))
+
+ # 3. compute placement of this service instance (adds DC names to VNFDs)
+ if not GK_STANDALONE_MODE:
+ #self._calculate_placement(FirstDcPlacement)
+ self._calculate_placement(RoundRobinDcPlacement)
+ # iterate over all vnfds that we have to start
+ for vnfd in self.vnfds.itervalues():
+ vnfi = None
+ if not GK_STANDALONE_MODE:
+ 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
+ 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"])
+
+ LOG.info("Service started. Instance id: %r" % instance_uuid)
+ return instance_uuid
+
+ def stop_service(self, instance_uuid):
+ """
+ This method stops a running service instance.
+ It iterates over all VNF instances, stopping them each
+ and removing them from their data center.
+
+ :param instance_uuid: the uuid of the service instance to be stopped
+ """
+ LOG.info("Stopping service %r" % self.uuid)
+ # get relevant information
+ # instance_uuid = str(self.uuid.uuid4())
+ vnf_instances = self.instances[instance_uuid]["vnf_instances"]
+
+ for v in vnf_instances:
+ self._stop_vnfi(v)
+
+ if not GK_STANDALONE_MODE:
+ # remove placement?
+ # self._remove_placement(RoundRobinPlacement)
+ None
+
+ # last step: remove the instance from the list of all instances
+ del self.instances[instance_uuid]
+
+ def _start_vnfd(self, vnfd):
+ """
+ Start a single VNFD of this service
+ :param vnfd: vnfd descriptor dict
+ :return:
+ """
+ # iterate over all deployment units within each VNFDs
+ for u in vnfd.get("virtual_deployment_units"):
+ # 1. get the name of the docker image to start and the assigned DC
+ vnf_name = vnfd.get("name")
+ if vnf_name not in self.remote_docker_image_urls:
+ raise Exception("No image name for %r found. Abort." % vnf_name)
+ docker_name = self.remote_docker_image_urls.get(vnf_name)
+ target_dc = vnfd.get("dc")
+ # 2. perform some checks to ensure we can start the container
+ assert(docker_name is not None)
+ 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
+ # TODO consider flavors, and other annotations
+ intfs = vnfd.get("connection_points")
+
+ # TODO: get all vnf id's from the nsd for this vnfd and use those as dockername
+ # use the vnf_id in the nsd as docker name
+ # so deployed containers can be easily mapped back to the nsd
+ vnf_name2id = defaultdict(lambda: "NotExistingNode",
+ reduce(lambda x, y: dict(x, **y),
+ map(lambda d: {d["vnf_name"]: d["vnf_id"]},
+ self.nsd["network_functions"])))
+ self.vnf_name2docker_name[vnf_name] = vnf_name2id[vnf_name]
+ # self.vnf_name2docker_name[vnf_name] = GK.get_next_vnf_name()
+
+ 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")
+ return vnfi
+
+ def _stop_vnfi(self, vnfi):
+ """
+ Stop a VNF instance.