From: peusterm Date: Tue, 11 Jun 2019 12:55:44 +0000 (+0200) Subject: 5GTANGO LLCM: New placement algo. integration. X-Git-Tag: v6.0.0~2 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=refs%2Fchanges%2F41%2F7641%2F1;p=osm%2Fvim-emu.git 5GTANGO LLCM: New placement algo. integration. Added StaticConfig placement. Change-Id: Ib3734a8999644c93cc1154b6b62c459f556f27a9 Signed-off-by: peusterm --- diff --git a/src/emuvim/api/tango/__init__.py b/src/emuvim/api/tango/__init__.py index ea3bc54..6bebaa5 100755 --- a/src/emuvim/api/tango/__init__.py +++ b/src/emuvim/api/tango/__init__.py @@ -45,7 +45,8 @@ class TangoLLCMEndpoint(object): """ def __init__(self, listenip, port, deploy_sap=False, docker_management=False, - auto_deploy=False, auto_delete=False, sap_vnfd_path=None): + auto_deploy=False, auto_delete=False, sap_vnfd_path=None, + placement_algorithm_obj=None): self.dcs = {} self.ip = listenip self.port = port @@ -54,7 +55,13 @@ class TangoLLCMEndpoint(object): llcm.AUTO_DEPLOY = auto_deploy llcm.AUTO_DELETE = auto_delete llcm.SAP_VNFD = sap_vnfd_path + if placement_algorithm_obj is None: + # Default placement is RR placement + placement_algorithm_obj = llcm.RoundRobinDcPlacement() + llcm.PLACEMENT_ALGORITHM_OBJ = placement_algorithm_obj LOG.info("Created 5GTANGO LLCM API endpoint %s" % self) + LOG.info("Using placement algorithm: {}".format( + placement_algorithm_obj)) def __repr__(self): return "%s(%s:%d)" % (self.__class__.__name__, self.ip, self.port) diff --git a/src/emuvim/api/tango/llcm.py b/src/emuvim/api/tango/llcm.py index 0a62485..1d55d11 100755 --- a/src/emuvim/api/tango/llcm.py +++ b/src/emuvim/api/tango/llcm.py @@ -94,6 +94,11 @@ VNF_STOP_WAIT_TIME = 5 MULTI_INSTANCE_PORT_OFFSET = 1000 +# Selected Placement Algorithm: Points to the class of the selected +# placement algorithm. +PLACEMENT_ALGORITHM_OBJ = None + + class OnBoardingException(BaseException): pass @@ -200,10 +205,6 @@ class Service(object): # increase for next instance self._instance_counter += 1 - # 2. compute placement of this service instance (adds DC names to - # VNFDs) - # self._calculate_placement(FirstDcPlacement) - self._calculate_placement(RoundRobinDcPlacement) # 3. start all vnfds that we have in the service for vnf_id in self.vnfds: vnfd = self.vnfds[vnf_id] @@ -332,7 +333,7 @@ class Service(object): raise Exception("No image name for %r found. Abort." % vnf_container_name) docker_image_name = self.remote_docker_image_urls.get(vnf_container_name) # 2. select datacenter to start the VNF in - target_dc = vnfd.get("dc") + target_dc = self._place(vnfd, vnf_id, u, ssiid) # 3. perform some checks to ensure we can start the container assert(docker_image_name is not None) assert(target_dc is not None) @@ -389,7 +390,7 @@ class Service(object): # 6. Start the container LOG.info("Starting %r as %r in DC %r" % - (vnf_name, vnf_container_instance_name, vnfd.get("dc"))) + (vnf_name, vnf_container_instance_name, target_dc)) LOG.debug("Interfaces for %r: %r" % (vnf_id, intfs)) # start the container vnfi = target_dc.startCompute( @@ -810,21 +811,22 @@ class Service(object): """ return len(DockerClient().images.list(name=image_name)) > 0 - def _calculate_placement(self, algorithm): + def _place(self, vnfd, vnfid, vdu, ssiid): """ - Do placement by adding the a field "dc" to - each VNFD that points to one of our - data center objects known to the gatekeeper. + Do placement. Return the name of the DC to place + the given VDU. """ assert(len(self.vnfds) > 0) assert(len(GK.dcs) > 0) - # instantiate algorithm an place - p = algorithm() - p.place(self.nsd, self.vnfds, GK.dcs) - LOG.info("Using placement algorithm: %r" % p.__class__.__name__) - # lets print the placement result - for name, vnfd in self.vnfds.iteritems(): - LOG.info("Placed VNF %r on DC %r" % (name, str(vnfd.get("dc")))) + if PLACEMENT_ALGORITHM_OBJ is None: + LOG.error("No placement algorithm given. Using FirstDcPlacement!") + p = FirstDcPlacement() + else: + p = PLACEMENT_ALGORITHM_OBJ + cname = get_container_name(vnfid, vdu.get("id"), ssiid) + rdc = p.place(GK.dcs, vnfd, vnfid, vdu, ssiid, cname) + LOG.info("Placement: '{}' --> '{}'".format(cname, rdc)) + return rdc def _calculate_cpu_cfs_values(self, cpu_time_percentage): """ @@ -864,9 +866,8 @@ class FirstDcPlacement(object): Placement: Always use one and the same data center from the GK.dcs dict. """ - def place(self, nsd, vnfds, dcs): - for id, vnfd in vnfds.iteritems(): - vnfd["dc"] = list(dcs.itervalues())[0] + def place(self, dcs, vnfd, vnfid, vdu, ssiid, cname): + return list(dcs.itervalues())[0] class RoundRobinDcPlacement(object): @@ -874,12 +875,50 @@ class RoundRobinDcPlacement(object): Placement: Distribute VNFs across all available DCs in a round robin fashion. """ - def place(self, nsd, vnfds, dcs): - c = 0 + def __init__(self): + self.count = 0 + + def place(self, dcs, vnfd, vnfid, vdu, ssiid, cname): dcs_list = list(dcs.itervalues()) - for id, vnfd in vnfds.iteritems(): - vnfd["dc"] = dcs_list[c % len(dcs_list)] - c += 1 # inc. c to use next DC + rdc = dcs_list[self.count % len(dcs_list)] + self.count += 1 # inc. count to use next DC + return rdc + + +class StaticConfigPlacement(object): + """ + Placement: Fixed assignment based on config file. + """ + + def __init__(self, path=None): + if path is None: + path = "static_placement.yml" + path = os.path.expanduser(path) + self.static_placement = dict() + try: + self.static_placement = load_yaml(path) + except BaseException as ex: + LOG.error(ex) + LOG.error("Couldn't load placement from {}" + .format(path)) + LOG.info("Loaded static placement: {}" + .format(self.static_placement)) + + def place(self, dcs, vnfd, vnfid, vdu, ssiid, cname): + # check for container name entry + if cname not in self.static_placement: + LOG.error("Coudn't find {} in placement".format(cname)) + LOG.error("Using first DC as fallback!") + return list(dcs.itervalues())[0] + # lookup + candidate_dc = self.static_placement.get(cname) + # check if DC exsits + if candidate_dc not in dcs: + LOG.error("Coudn't find DC {}".format(candidate_dc)) + LOG.error("Using first DC as fallback!") + return list(dcs.itervalues())[0] + # return correct DC + return dcs.get(candidate_dc) """