X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=osm_lcm%2Fns.py;h=8ef5f2acc05525cd78d41896dbe5331a9f0089c8;hb=refs%2Ftags%2Fv10.1.1rc2;hp=8b2be1e9278084aa204d734e7daaff979f71cbdd;hpb=5697b8b03a3acd17827ce536cb8aff15df8776ad;p=osm%2FLCM.git diff --git a/osm_lcm/ns.py b/osm_lcm/ns.py index 8b2be1e..8ef5f2a 100644 --- a/osm_lcm/ns.py +++ b/osm_lcm/ns.py @@ -31,6 +31,7 @@ from jinja2 import ( ) from osm_lcm import ROclient +from osm_lcm.data_utils.nsr import get_deployed_kdu from osm_lcm.ng_ro import NgRoClient, NgRoException from osm_lcm.lcm_utils import ( LcmException, @@ -54,9 +55,10 @@ from osm_lcm.data_utils.vnfd import ( get_scaling_aspect, get_number_of_instances, get_juju_ee_ref, + get_kdu_profile, ) from osm_lcm.data_utils.list_utils import find_in_list -from osm_lcm.data_utils.vnfr import get_osm_params, get_vdur_index +from osm_lcm.data_utils.vnfr import get_osm_params, get_vdur_index, get_kdur from osm_lcm.data_utils.dict_utils import parse_yaml_strings from osm_lcm.data_utils.database.vim_account import VimAccountDB from n2vc.k8s_helm_conn import K8sHelmConnector @@ -326,43 +328,49 @@ class NsLcm(LcmBase): self.logger.warn("Error updating NS state for ns={}: {}".format(nsr_id, e)) async def _on_update_k8s_db( - self, cluster_uuid, kdu_instance, filter=None, vca_id=None + self, cluster_uuid, kdu_instance, filter=None, vca_id=None, cluster_type="juju" ): """ Updating vca status in NSR record :param cluster_uuid: UUID of a k8s cluster :param kdu_instance: The unique name of the KDU instance :param filter: To get nsr_id + :cluster_type: The cluster type (juju, k8s) :return: none """ # self.logger.debug("_on_update_k8s_db(cluster_uuid={}, kdu_instance={}, filter={}" # .format(cluster_uuid, kdu_instance, filter)) + nsr_id = filter.get("_id") try: - nsr_id = filter.get("_id") - - # get vca status for NS - vca_status = await self.k8sclusterjuju.status_kdu( - cluster_uuid, - kdu_instance, - complete_status=True, + vca_status = await self.k8scluster_map[cluster_type].status_kdu( + cluster_uuid=cluster_uuid, + kdu_instance=kdu_instance, yaml_format=False, + complete_status=True, vca_id=vca_id, ) + # vcaStatus db_dict = dict() db_dict["vcaStatus"] = {nsr_id: vca_status} - await self.k8sclusterjuju.update_vca_status( - db_dict["vcaStatus"], - kdu_instance, - vca_id=vca_id, + if cluster_type in ("juju-bundle", "juju"): + # TODO -> this should be done in a more uniform way, I think in N2VC, in order to update the K8s VCA + # status in a similar way between Juju Bundles and Helm Charts on this side + await self.k8sclusterjuju.update_vca_status( + db_dict["vcaStatus"], + kdu_instance, + vca_id=vca_id, + ) + + self.logger.debug( + f"Obtained VCA status for cluster type '{cluster_type}': {vca_status}" ) # write to database self.update_db_2("nsrs", nsr_id, db_dict) - except (asyncio.CancelledError, asyncio.TimeoutError): raise except Exception as e: @@ -412,7 +420,8 @@ class NsLcm(LcmBase): def _get_vdu_additional_params(self, db_vnfr, vdu_id): vdur = next( - vdur for vdur in db_vnfr.get("vdur") if vdu_id == vdur["vdu-id-ref"] + (vdur for vdur in db_vnfr.get("vdur") if vdu_id == vdur["vdu-id-ref"]), + {} ) additional_params = vdur.get("additionalParams") return parse_yaml_strings(additional_params) @@ -489,6 +498,7 @@ class NsLcm(LcmBase): def scale_vnfr(self, db_vnfr, vdu_create=None, vdu_delete=None, mark_delete=False): db_vdu_push_list = [] + template_vdur = [] db_update = {"_admin.modified": time()} if vdu_create: for vdu_id, vdu_count in vdu_create.items(): @@ -501,17 +511,27 @@ class NsLcm(LcmBase): None, ) if not vdur: - raise LcmException( - "Error scaling OUT VNFR for {}. There is not any existing vnfr. Scaled to 0?".format( + # Read the template saved in the db: + self.logger.debug(f"No vdur in the database. Using the vdur-template to scale") + vdur_template = db_vnfr.get("vdur-template") + if not vdur_template: + raise LcmException( + "Error scaling OUT VNFR for {}. No vnfr or template exists".format( vdu_id + ) ) - ) - + vdur = vdur_template[0] + #Delete a template from the database after using it + self.db.set_one("vnfrs", + {"_id": db_vnfr["_id"]}, + None, + pull={"vdur-template": {"_id": vdur['_id']}} + ) for count in range(vdu_count): vdur_copy = deepcopy(vdur) vdur_copy["status"] = "BUILD" vdur_copy["status-detailed"] = None - vdur_copy["ip-address"]: None + vdur_copy["ip-address"] = None vdur_copy["_id"] = str(uuid4()) vdur_copy["count-index"] += count + 1 vdur_copy["id"] = "{}-{}".format( @@ -531,12 +551,17 @@ class NsLcm(LcmBase): ) else: iface.pop("mac-address", None) - iface.pop( - "mgmt_vnf", None - ) # only first vdu can be managment of vnf + if db_vnfr["vdur"]: + iface.pop( + "mgmt_vnf", None + ) # only first vdu can be managment of vnf db_vdu_push_list.append(vdur_copy) # self.logger.debug("scale out, adding vdu={}".format(vdur_copy)) if vdu_delete: + if len(db_vnfr["vdur"]) == 1: + # The scale will move to 0 instances + self.logger.debug(f"Scaling to 0 !, creating the template with the last vdur") + template_vdur = [db_vnfr["vdur"][0]] for vdu_id, vdu_count in vdu_delete.items(): if mark_delete: indexes_to_delete = [ @@ -564,7 +589,14 @@ class NsLcm(LcmBase): None, pull={"vdur": {"_id": vdu["_id"]}}, ) - db_push = {"vdur": db_vdu_push_list} if db_vdu_push_list else None + db_push = {} + if db_vdu_push_list: + db_push["vdur"] = db_vdu_push_list + if template_vdur: + db_push["vdur-template"] = template_vdur + if not db_push: + db_push = None + db_vnfr["vdur-template"] = template_vdur self.db.set_one("vnfrs", {"_id": db_vnfr["_id"]}, db_update, push_list=db_push) # modify passed dictionary db_vnfr db_vnfr_ = self.db.get_one("vnfrs", {"_id": db_vnfr["_id"]}) @@ -794,6 +826,37 @@ class NsLcm(LcmBase): if vld_params.get("common_id"): target_vld["common_id"] = vld_params.get("common_id") + # modify target["ns"]["vld"] with instantiation parameters to override vnf vim-account + def update_ns_vld_target(target, ns_params): + for vnf_params in ns_params.get("vnf", ()): + if vnf_params.get("vimAccountId"): + target_vnf = next( + ( + vnfr + for vnfr in db_vnfrs.values() + if vnf_params["member-vnf-index"] + == vnfr["member-vnf-index-ref"] + ), + None, + ) + vdur = next((vdur for vdur in target_vnf.get("vdur", ())), None) + for a_index, a_vld in enumerate(target["ns"]["vld"]): + target_vld = find_in_list( + get_iterable(vdur, "interfaces"), + lambda iface: iface.get("ns-vld-id") == a_vld["name"], + ) + if target_vld: + if vnf_params.get("vimAccountId") not in a_vld.get( + "vim_info", {} + ): + target["ns"]["vld"][a_index].get("vim_info").update( + { + "vim:{}".format(vnf_params["vimAccountId"]): { + "vim_network_name": "" + } + } + ) + nslcmop_id = db_nslcmop["_id"] target = { "name": db_nsr["name"], @@ -808,6 +871,10 @@ class NsLcm(LcmBase): image["vim_info"] = {} for flavor in target["flavor"]: flavor["vim_info"] = {} + if db_nsr.get("affinity-or-anti-affinity-group"): + target["affinity-or-anti-affinity-group"] = deepcopy(db_nsr["affinity-or-anti-affinity-group"]) + for affinity_or_anti_affinity_group in target["affinity-or-anti-affinity-group"]: + affinity_or_anti_affinity_group["vim_info"] = {} if db_nslcmop.get("lcmOperationType") != "instantiate": # get parameters of instantiation: @@ -909,6 +976,8 @@ class NsLcm(LcmBase): vld_params.update(vld_instantiation_params) parse_vld_instantiation_params(target_vim, target_vld, vld_params, None) target["ns"]["vld"].append(target_vld) + # Update the target ns_vld if vnf vim_account is overriden by instantiation params + update_ns_vld_target(target, ns_params) for vnfr in db_vnfrs.values(): vnfd = find_in_list( @@ -1092,6 +1161,13 @@ class NsLcm(LcmBase): if target_vim not in ns_image["vim_info"]: ns_image["vim_info"][target_vim] = {} + # Affinity groups + if vdur.get("affinity-or-anti-affinity-group-id"): + for ags_id in vdur["affinity-or-anti-affinity-group-id"]: + ns_ags = target["affinity-or-anti-affinity-group"][int(ags_id)] + if target_vim not in ns_ags["vim_info"]: + ns_ags["vim_info"][target_vim] = {} + vdur["vim_info"] = {target_vim: {}} # instantiation parameters # if vnf_params: @@ -1555,9 +1631,13 @@ class NsLcm(LcmBase): raise LcmException("Configuration aborted because dependent charm/s timeout") def get_vca_id(self, db_vnfr: dict, db_nsr: dict): - return deep_get(db_vnfr, ("vca-id",)) or deep_get( - db_nsr, ("instantiate_params", "vcaId") - ) + vca_id = None + if db_vnfr: + vca_id = deep_get(db_vnfr, ("vca-id",)) + elif db_nsr: + vim_account_id = deep_get(db_nsr, ("instantiate_params", "vimAccountId")) + vca_id = VimAccountDB.get_vim_account_with_id(vim_account_id).get("vca") + return vca_id async def instantiate_N2VC( self, @@ -1601,17 +1681,22 @@ class NsLcm(LcmBase): namespace = "{nsi}.{ns}".format(nsi=nsi_id if nsi_id else "", ns=nsr_id) + if vca_type == "native_charm": + index_number = 0 + else: + index_number = vdu_index or 0 + if vnfr_id: element_type = "VNF" element_under_configuration = vnfr_id - namespace += ".{}-{}".format(vnfr_id, vdu_index or 0) + namespace += ".{}-{}".format(vnfr_id, index_number) if vdu_id: - namespace += ".{}-{}".format(vdu_id, vdu_index or 0) + namespace += ".{}-{}".format(vdu_id, index_number) element_type = "VDU" - element_under_configuration = "{}-{}".format(vdu_id, vdu_index or 0) + element_under_configuration = "{}-{}".format(vdu_id, index_number) osm_config["osm"]["vdu_id"] = vdu_id elif kdu_name: - namespace += ".{}.{}".format(kdu_name, vdu_index or 0) + namespace += ".{}".format(kdu_name) element_type = "KDU" element_under_configuration = kdu_name osm_config["osm"]["kdu_name"] = kdu_name @@ -1804,6 +1889,7 @@ class NsLcm(LcmBase): config=config, num_units=num_units, vca_id=vca_id, + vca_type=vca_type, ) # write in db flag of configuration_sw already installed @@ -1920,6 +2006,7 @@ class NsLcm(LcmBase): params_dict=primitive_params_, db_dict=db_dict, vca_id=vca_id, + vca_type=vca_type, ) # Once some primitive has been exec, check and write at db if it needs to exec terminated primitives if check_if_terminated_needed: @@ -2212,6 +2299,10 @@ class NsLcm(LcmBase): # read from db: operation stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id) db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id}) + if db_nslcmop["operationParams"].get("additionalParamsForVnf"): + db_nslcmop["operationParams"]["additionalParamsForVnf"] = json.loads( + db_nslcmop["operationParams"]["additionalParamsForVnf"] + ) ns_params = db_nslcmop.get("operationParams") if ns_params and ns_params.get("timeout_ns_deploy"): timeout_ns_deploy = ns_params["timeout_ns_deploy"] @@ -2239,6 +2330,16 @@ class NsLcm(LcmBase): # for each vnf in ns, read vnfd for vnfr in db_vnfrs_list: + if vnfr.get("kdur"): + kdur_list = [] + for kdur in vnfr["kdur"]: + if kdur.get("additionalParams"): + kdur["additionalParams"] = json.loads( + kdur["additionalParams"] + ) + kdur_list.append(kdur) + vnfr["kdur"] = kdur_list + db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr vnfd_id = vnfr["vnfd-id"] vnfd_ref = vnfr["vnfd-ref"] @@ -2388,9 +2489,7 @@ class NsLcm(LcmBase): deploy_params_vdu["OSM"] = get_osm_params( db_vnfr, vdu_id, vdu_count_index=0 ) - vdud_count = get_vdu_profile(vnfd, vdu_id).get( - "max-number-of-instances", 1 - ) + vdud_count = get_number_of_instances(vnfd, vdu_id) self.logger.debug("VDUD > {}".format(vdud)) self.logger.debug( @@ -2435,8 +2534,8 @@ class NsLcm(LcmBase): ) deploy_params_kdu = {"OSM": get_osm_params(db_vnfr)} if kdur.get("additionalParams"): - deploy_params_kdu = parse_yaml_strings( - kdur["additionalParams"] + deploy_params_kdu.update( + parse_yaml_strings(kdur["additionalParams"].copy()) ) self._deploy_n2vc( @@ -2846,16 +2945,55 @@ class NsLcm(LcmBase): "path": nsr_db_path, } - kdu_instance = self.k8scluster_map[ - k8sclustertype - ].generate_kdu_instance_name( - db_dict=db_dict_install, - kdu_model=k8s_instance_info["kdu-model"], - kdu_name=k8s_instance_info["kdu-name"], - ) + if k8s_instance_info.get("kdu-deployment-name"): + kdu_instance = k8s_instance_info.get("kdu-deployment-name") + else: + kdu_instance = self.k8scluster_map[ + k8sclustertype + ].generate_kdu_instance_name( + db_dict=db_dict_install, + kdu_model=k8s_instance_info["kdu-model"], + kdu_name=k8s_instance_info["kdu-name"], + ) + + # Update the nsrs table with the kdu-instance value self.update_db_2( - "nsrs", nsr_id, {nsr_db_path + ".kdu-instance": kdu_instance} + item="nsrs", + _id=nsr_id, + _desc={nsr_db_path + ".kdu-instance": kdu_instance}, ) + + # Update the nsrs table with the actual namespace being used, if the k8scluster-type is `juju` or + # `juju-bundle`. This verification is needed because there is not a standard/homogeneous namespace + # between the Helm Charts and Juju Bundles-based KNFs. If we found a way of having an homogeneous + # namespace, this first verification could be removed, and the next step would be done for any kind + # of KNF. + # TODO -> find a way to have an homogeneous namespace between the Helm Charts and Juju Bundles-based + # KNFs (Bug 2027: https://osm.etsi.org/bugzilla/show_bug.cgi?id=2027) + if k8sclustertype in ("juju", "juju-bundle"): + # First, verify if the current namespace is present in the `_admin.projects_read` (if not, it means + # that the user passed a namespace which he wants its KDU to be deployed in) + if ( + self.db.count( + table="nsrs", + q_filter={ + "_id": nsr_id, + "_admin.projects_write": k8s_instance_info["namespace"], + "_admin.projects_read": k8s_instance_info["namespace"], + }, + ) + > 0 + ): + self.logger.debug( + f"Updating namespace/model for Juju Bundle from {k8s_instance_info['namespace']} to {kdu_instance}" + ) + self.update_db_2( + item="nsrs", + _id=nsr_id, + _desc={f"{nsr_db_path}.namespace": kdu_instance}, + ) + k8s_instance_info["namespace"] = kdu_instance + await self.k8scluster_map[k8sclustertype].install( cluster_uuid=k8s_instance_info["k8scluster-uuid"], kdu_model=k8s_instance_info["kdu-model"], @@ -2868,9 +3006,6 @@ class NsLcm(LcmBase): kdu_instance=kdu_instance, vca_id=vca_id, ) - self.update_db_2( - "nsrs", nsr_id, {nsr_db_path + ".kdu-instance": kdu_instance} - ) # Obtain services to obtain management service ip services = await self.k8scluster_map[k8sclustertype].get_services( @@ -3091,6 +3226,7 @@ class NsLcm(LcmBase): if kdud["name"] == kdur["kdu-name"] ) namespace = kdur.get("k8s-namespace") + kdu_deployment_name = kdur.get("kdu-deployment-name") if kdur.get("helm-chart"): kdumodel = kdur["helm-chart"] # Default version: helm3, if helm-version is v2 assign v2 @@ -3203,6 +3339,7 @@ class NsLcm(LcmBase): "kdu-name": kdur["kdu-name"], "kdu-model": kdumodel, "namespace": namespace, + "kdu-deployment-name": kdu_deployment_name, } db_path = "_admin.deployed.K8s.{}".format(index) db_nsr_update[db_path] = k8s_instance_info @@ -3220,7 +3357,7 @@ class NsLcm(LcmBase): vnfd_with_id, k8s_instance_info, k8params=desc_params, - timeout=600, + timeout=1800, vca_id=vca_id, ) ) @@ -3280,6 +3417,8 @@ class NsLcm(LcmBase): ) if "execution-environment-list" in descriptor_config: ee_list = descriptor_config.get("execution-environment-list", []) + elif "juju" in descriptor_config: + ee_list = [descriptor_config] # ns charms else: # other types as script are not supported ee_list = [] @@ -3738,6 +3877,7 @@ class NsLcm(LcmBase): await self.vca_map[vca_type].delete_execution_environment( vca_deployed["ee_id"], scaling_in=scaling_in, + vca_type=vca_type, vca_id=vca_id, ) @@ -4033,8 +4173,13 @@ class NsLcm(LcmBase): for vca_index, vca in enumerate(get_iterable(nsr_deployed, "VCA")): config_descriptor = None - - vca_id = self.get_vca_id(db_vnfrs_dict[vca["member-vnf-index"]], db_nsr) + vca_member_vnf_index = vca.get("member-vnf-index") + vca_id = self.get_vca_id( + db_vnfrs_dict.get(vca_member_vnf_index) + if vca_member_vnf_index + else None, + db_nsr, + ) if not vca or not vca.get("ee_id"): continue if not vca.get("member-vnf-index"): @@ -4506,6 +4651,7 @@ class NsLcm(LcmBase): total_timeout=self.timeout_primitive, db_dict=db_dict, vca_id=vca_id, + vca_type=vca_type, ), timeout=timeout or self.timeout_primitive, ) @@ -4547,10 +4693,18 @@ class NsLcm(LcmBase): db_nsr = self.db.get_one("nsrs", {"_id": nsr_id}) vca_id = self.get_vca_id({}, db_nsr) if db_nsr["_admin"]["deployed"]["K8s"]: - for k8s_index, k8s in enumerate(db_nsr["_admin"]["deployed"]["K8s"]): - cluster_uuid, kdu_instance = k8s["k8scluster-uuid"], k8s["kdu-instance"] + for _, k8s in enumerate(db_nsr["_admin"]["deployed"]["K8s"]): + cluster_uuid, kdu_instance, cluster_type = ( + k8s["k8scluster-uuid"], + k8s["kdu-instance"], + k8s["k8scluster-type"], + ) await self._on_update_k8s_db( - cluster_uuid, kdu_instance, filter={"_id": nsr_id}, vca_id=vca_id + cluster_uuid=cluster_uuid, + kdu_instance=kdu_instance, + filter={"_id": nsr_id}, + vca_id=vca_id, + cluster_type=cluster_type, ) else: for vca_index, _ in enumerate(db_nsr["_admin"]["deployed"]["VCA"]): @@ -4592,6 +4746,10 @@ class NsLcm(LcmBase): step = "Getting information from database" db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id}) db_nsr = self.db.get_one("nsrs", {"_id": nsr_id}) + if db_nslcmop["operationParams"].get("primitive_params"): + db_nslcmop["operationParams"]["primitive_params"] = json.loads( + db_nslcmop["operationParams"]["primitive_params"] + ) nsr_deployed = db_nsr["_admin"].get("deployed") vnf_index = db_nslcmop["operationParams"].get("member_vnf_index") @@ -4609,6 +4767,15 @@ class NsLcm(LcmBase): db_vnfr = self.db.get_one( "vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id} ) + if db_vnfr.get("kdur"): + kdur_list = [] + for kdur in db_vnfr["kdur"]: + if kdur.get("additionalParams"): + kdur["additionalParams"] = json.loads( + kdur["additionalParams"] + ) + kdur_list.append(kdur) + db_vnfr["kdur"] = kdur_list step = "Getting vnfd from database" db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]}) else: @@ -4947,14 +5114,6 @@ class NsLcm(LcmBase): self.update_db_2("nsrs", nsr_id, db_nsr_update) nsr_deployed = db_nsr["_admin"].get("deployed") - ####### - nsr_deployed = db_nsr["_admin"].get("deployed") - vnf_index = db_nslcmop["operationParams"].get("member_vnf_index") - # vdu_id = db_nslcmop["operationParams"].get("vdu_id") - # vdu_count_index = db_nslcmop["operationParams"].get("vdu_count_index") - # vdu_name = db_nslcmop["operationParams"].get("vdu_name") - ####### - vnf_index = db_nslcmop["operationParams"]["scaleVnfData"][ "scaleByStepData" ]["member-vnf-index"] @@ -5017,9 +5176,9 @@ class NsLcm(LcmBase): db_nsr_update[ "_admin.scaling-group.{}.name".format(admin_scale_index) ] = scaling_group - RO_scaling_info = [] - VCA_scaling_info = [] - vdu_scaling_info = {"scaling_group_name": scaling_group, "vdu": []} + + vca_scaling_info = [] + scaling_info = {"scaling_group_name": scaling_group, "vdu": [], "kdu": []} if scaling_type == "SCALE_OUT": if "aspect-delta-details" not in scaling_descriptor: raise LcmException( @@ -5030,12 +5189,14 @@ class NsLcm(LcmBase): # count if max-instance-count is reached deltas = scaling_descriptor.get("aspect-delta-details")["deltas"] - vdu_scaling_info["scaling_direction"] = "OUT" - vdu_scaling_info["vdu-create"] = {} + scaling_info["scaling_direction"] = "OUT" + scaling_info["vdu-create"] = {} + scaling_info["kdu-create"] = {} for delta in deltas: - for vdu_delta in delta["vdu-delta"]: + for vdu_delta in delta.get("vdu-delta", {}): vdud = get_vdu(db_vnfd, vdu_delta["id"]) - vdu_index = get_vdur_index(db_vnfr, vdu_delta) + # vdu_index also provides the number of instance of the targeted vdu + vdu_count = vdu_index = get_vdur_index(db_vnfr, vdu_delta) cloud_init_text = self._get_vdu_cloud_init_content( vdud, db_vnfd ) @@ -5056,10 +5217,18 @@ class NsLcm(LcmBase): default_instance_num = get_number_of_instances( db_vnfd, vdud["id"] ) + instances_number = vdu_delta.get("number-of-instances", 1) + nb_scale_op += instances_number + + new_instance_count = nb_scale_op + default_instance_num + # Control if new count is over max and vdu count is less than max. + # Then assign new instance count + if new_instance_count > max_instance_count > vdu_count: + instances_number = new_instance_count - max_instance_count + else: + instances_number = instances_number - nb_scale_op += vdu_delta.get("number-of-instances", 1) - - if nb_scale_op + default_instance_num > max_instance_count: + if new_instance_count > max_instance_count: raise LcmException( "reached the limit of {} (max-instance-count) " "scaling-out operations for the " @@ -5081,7 +5250,7 @@ class NsLcm(LcmBase): vdud["id"], ) ) - VCA_scaling_info.append( + vca_scaling_info.append( { "osm_vdu_id": vdu_delta["id"], "member-vnf-index": vnf_index, @@ -5089,33 +5258,102 @@ class NsLcm(LcmBase): "vdu_index": vdu_index + x, } ) - RO_scaling_info.append( + scaling_info["vdu-create"][vdu_delta["id"]] = instances_number + for kdu_delta in delta.get("kdu-resource-delta", {}): + kdu_profile = get_kdu_profile(db_vnfd, kdu_delta["id"]) + kdu_name = kdu_profile["kdu-name"] + resource_name = kdu_profile["resource-name"] + + # Might have different kdus in the same delta + # Should have list for each kdu + if not scaling_info["kdu-create"].get(kdu_name, None): + scaling_info["kdu-create"][kdu_name] = [] + + kdur = get_kdur(db_vnfr, kdu_name) + if kdur.get("helm-chart"): + k8s_cluster_type = "helm-chart-v3" + self.logger.debug("kdur: {}".format(kdur)) + if ( + kdur.get("helm-version") + and kdur.get("helm-version") == "v2" + ): + k8s_cluster_type = "helm-chart" + raise NotImplementedError + elif kdur.get("juju-bundle"): + k8s_cluster_type = "juju-bundle" + else: + raise LcmException( + "kdu type for kdu='{}.{}' is neither helm-chart nor " + "juju-bundle. Maybe an old NBI version is running".format( + db_vnfr["member-vnf-index-ref"], kdu_name + ) + ) + + max_instance_count = 10 + if kdu_profile and "max-number-of-instances" in kdu_profile: + max_instance_count = kdu_profile.get( + "max-number-of-instances", 10 + ) + + nb_scale_op += kdu_delta.get("number-of-instances", 1) + deployed_kdu, _ = get_deployed_kdu( + nsr_deployed, kdu_name, vnf_index + ) + if deployed_kdu is None: + raise LcmException( + "KDU '{}' for vnf '{}' not deployed".format( + kdu_name, vnf_index + ) + ) + kdu_instance = deployed_kdu.get("kdu-instance") + instance_num = await self.k8scluster_map[ + k8s_cluster_type + ].get_scale_count(resource_name, kdu_instance, vca_id=vca_id) + kdu_replica_count = instance_num + kdu_delta.get( + "number-of-instances", 1 + ) + + # Control if new count is over max and instance_num is less than max. + # Then assign max instance number to kdu replica count + if kdu_replica_count > max_instance_count > instance_num: + kdu_replica_count = max_instance_count + if kdu_replica_count > max_instance_count: + raise LcmException( + "reached the limit of {} (max-instance-count) " + "scaling-out operations for the " + "scaling-group-descriptor '{}'".format( + instance_num, scaling_group + ) + ) + + for x in range(kdu_delta.get("number-of-instances", 1)): + vca_scaling_info.append( + { + "osm_kdu_id": kdu_name, + "member-vnf-index": vnf_index, + "type": "create", + "kdu_index": instance_num + x - 1, + } + ) + scaling_info["kdu-create"][kdu_name].append( { - "osm_vdu_id": vdu_delta["id"], "member-vnf-index": vnf_index, "type": "create", - "count": vdu_delta.get("number-of-instances", 1), + "k8s-cluster-type": k8s_cluster_type, + "resource-name": resource_name, + "scale": kdu_replica_count, } ) - if cloud_init_list: - RO_scaling_info[-1]["cloud_init"] = cloud_init_list - vdu_scaling_info["vdu-create"][vdu_delta["id"]] = vdu_delta.get( - "number-of-instances", 1 - ) - elif scaling_type == "SCALE_IN": - if ( - "min-instance-count" in scaling_descriptor - and scaling_descriptor["min-instance-count"] is not None - ): - min_instance_count = int(scaling_descriptor["min-instance-count"]) - - vdu_scaling_info["scaling_direction"] = "IN" - vdu_scaling_info["vdu-delete"] = {} deltas = scaling_descriptor.get("aspect-delta-details")["deltas"] + + scaling_info["scaling_direction"] = "IN" + scaling_info["vdu-delete"] = {} + scaling_info["kdu-delete"] = {} + for delta in deltas: - for vdu_delta in delta["vdu-delta"]: - vdu_index = get_vdur_index(db_vnfr, vdu_delta) + for vdu_delta in delta.get("vdu-delta", {}): + vdu_count = vdu_index = get_vdur_index(db_vnfr, vdu_delta) min_instance_count = 0 vdu_profile = get_vdu_profile(db_vnfd, vdu_delta["id"]) if vdu_profile and "min-number-of-instances" in vdu_profile: @@ -5124,26 +5362,25 @@ class NsLcm(LcmBase): default_instance_num = get_number_of_instances( db_vnfd, vdu_delta["id"] ) + instance_num = vdu_delta.get("number-of-instances", 1) + nb_scale_op -= instance_num - nb_scale_op -= vdu_delta.get("number-of-instances", 1) - if nb_scale_op + default_instance_num < min_instance_count: + new_instance_count = nb_scale_op + default_instance_num + + if new_instance_count < min_instance_count < vdu_count: + instances_number = min_instance_count - new_instance_count + else: + instances_number = instance_num + + if new_instance_count < min_instance_count: raise LcmException( "reached the limit of {} (min-instance-count) scaling-in operations for the " "scaling-group-descriptor '{}'".format( nb_scale_op, scaling_group ) ) - RO_scaling_info.append( - { - "osm_vdu_id": vdu_delta["id"], - "member-vnf-index": vnf_index, - "type": "delete", - "count": vdu_delta.get("number-of-instances", 1), - "vdu_index": vdu_index - 1, - } - ) for x in range(vdu_delta.get("number-of-instances", 1)): - VCA_scaling_info.append( + vca_scaling_info.append( { "osm_vdu_id": vdu_delta["id"], "member-vnf-index": vnf_index, @@ -5151,17 +5388,93 @@ class NsLcm(LcmBase): "vdu_index": vdu_index - 1 - x, } ) - vdu_scaling_info["vdu-delete"][vdu_delta["id"]] = vdu_delta.get( + scaling_info["vdu-delete"][vdu_delta["id"]] = instances_number + for kdu_delta in delta.get("kdu-resource-delta", {}): + kdu_profile = get_kdu_profile(db_vnfd, kdu_delta["id"]) + kdu_name = kdu_profile["kdu-name"] + resource_name = kdu_profile["resource-name"] + + if not scaling_info["kdu-delete"].get(kdu_name, None): + scaling_info["kdu-delete"][kdu_name] = [] + + kdur = get_kdur(db_vnfr, kdu_name) + if kdur.get("helm-chart"): + k8s_cluster_type = "helm-chart-v3" + self.logger.debug("kdur: {}".format(kdur)) + if ( + kdur.get("helm-version") + and kdur.get("helm-version") == "v2" + ): + k8s_cluster_type = "helm-chart" + raise NotImplementedError + elif kdur.get("juju-bundle"): + k8s_cluster_type = "juju-bundle" + else: + raise LcmException( + "kdu type for kdu='{}.{}' is neither helm-chart nor " + "juju-bundle. Maybe an old NBI version is running".format( + db_vnfr["member-vnf-index-ref"], kdur["kdu-name"] + ) + ) + + min_instance_count = 0 + if kdu_profile and "min-number-of-instances" in kdu_profile: + min_instance_count = kdu_profile["min-number-of-instances"] + + nb_scale_op -= kdu_delta.get("number-of-instances", 1) + deployed_kdu, _ = get_deployed_kdu( + nsr_deployed, kdu_name, vnf_index + ) + if deployed_kdu is None: + raise LcmException( + "KDU '{}' for vnf '{}' not deployed".format( + kdu_name, vnf_index + ) + ) + kdu_instance = deployed_kdu.get("kdu-instance") + instance_num = await self.k8scluster_map[ + k8s_cluster_type + ].get_scale_count(resource_name, kdu_instance, vca_id=vca_id) + kdu_replica_count = instance_num - kdu_delta.get( "number-of-instances", 1 ) + if kdu_replica_count < min_instance_count < instance_num: + kdu_replica_count = min_instance_count + if kdu_replica_count < min_instance_count: + raise LcmException( + "reached the limit of {} (min-instance-count) scaling-in operations for the " + "scaling-group-descriptor '{}'".format( + instance_num, scaling_group + ) + ) + + for x in range(kdu_delta.get("number-of-instances", 1)): + vca_scaling_info.append( + { + "osm_kdu_id": kdu_name, + "member-vnf-index": vnf_index, + "type": "delete", + "kdu_index": instance_num - x - 1, + } + ) + scaling_info["kdu-delete"][kdu_name].append( + { + "member-vnf-index": vnf_index, + "type": "delete", + "k8s-cluster-type": k8s_cluster_type, + "resource-name": resource_name, + "scale": kdu_replica_count, + } + ) + # update VDU_SCALING_INFO with the VDUs to delete ip_addresses - vdu_delete = copy(vdu_scaling_info.get("vdu-delete")) - if vdu_scaling_info["scaling_direction"] == "IN": + vdu_delete = copy(scaling_info.get("vdu-delete")) + if scaling_info["scaling_direction"] == "IN": for vdur in reversed(db_vnfr["vdur"]): if vdu_delete.get(vdur["vdu-id-ref"]): vdu_delete[vdur["vdu-id-ref"]] -= 1 - vdu_scaling_info["vdu"].append( + scaling_info["vdu"].append( { "name": vdur.get("name") or vdur.get("vdu-name"), "vdu_id": vdur["vdu-id-ref"], @@ -5169,7 +5482,7 @@ class NsLcm(LcmBase): } ) for interface in vdur["interfaces"]: - vdu_scaling_info["vdu"][-1]["interface"].append( + scaling_info["vdu"][-1]["interface"].append( { "name": interface["name"], "ip_address": interface["ip-address"], @@ -5213,7 +5526,7 @@ class NsLcm(LcmBase): "primitive".format(scaling_group, vnf_config_primitive) ) - vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info} + vnfr_params = {"VDU_SCALE_INFO": scaling_info} if db_vnfr.get("additionalParamsForVnf"): vnfr_params.update(db_vnfr["additionalParamsForVnf"]) @@ -5226,7 +5539,6 @@ class NsLcm(LcmBase): # Pre-scale retry check: Check if this sub-operation has been executed before op_index = self._check_or_add_scale_suboperation( db_nslcmop, - nslcmop_id, vnf_index, vnf_config_primitive, primitive_params, @@ -5315,24 +5627,33 @@ class NsLcm(LcmBase): ] = time() # SCALE-IN VCA - BEGIN - if VCA_scaling_info: + if vca_scaling_info: step = db_nslcmop_update[ "detailed-status" ] = "Deleting the execution environments" scale_process = "VCA" - for vdu_info in VCA_scaling_info: - if vdu_info["type"] == "delete": - member_vnf_index = str(vdu_info["member-vnf-index"]) + for vca_info in vca_scaling_info: + if vca_info["type"] == "delete": + member_vnf_index = str(vca_info["member-vnf-index"]) self.logger.debug( - logging_text + "vdu info: {}".format(vdu_info) - ) - vdu_id = vdu_info["osm_vdu_id"] - vdu_index = int(vdu_info["vdu_index"]) - stage[ - 1 - ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format( - member_vnf_index, vdu_id, vdu_index + logging_text + "vdu info: {}".format(vca_info) ) + if vca_info.get("osm_vdu_id"): + vdu_id = vca_info["osm_vdu_id"] + vdu_index = int(vca_info["vdu_index"]) + stage[ + 1 + ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format( + member_vnf_index, vdu_id, vdu_index + ) + else: + vdu_index = 0 + kdu_id = vca_info["osm_kdu_id"] + stage[ + 1 + ] = "Scaling member_vnf_index={}, kdu_id={}, vdu_index={} ".format( + member_vnf_index, kdu_id, vdu_index + ) stage[2] = step = "Scaling in VCA" self._write_op_status(op_id=nslcmop_id, stage=stage) vca_update = db_nsr["_admin"]["deployed"]["VCA"] @@ -5414,117 +5735,165 @@ class NsLcm(LcmBase): # SCALE-IN VCA - END # SCALE RO - BEGIN - if RO_scaling_info: + if scaling_info.get("vdu-create") or scaling_info.get("vdu-delete"): scale_process = "RO" if self.ro_config.get("ng"): await self._scale_ng_ro( - logging_text, - db_nsr, - db_nslcmop, - db_vnfr, - vdu_scaling_info, - stage, + logging_text, db_nsr, db_nslcmop, db_vnfr, scaling_info, stage ) - vdu_scaling_info.pop("vdu-create", None) - vdu_scaling_info.pop("vdu-delete", None) + scaling_info.pop("vdu-create", None) + scaling_info.pop("vdu-delete", None) scale_process = None + # SCALE RO - END + + # SCALE KDU - BEGIN + if scaling_info.get("kdu-create") or scaling_info.get("kdu-delete"): + scale_process = "KDU" + await self._scale_kdu( + logging_text, nsr_id, nsr_deployed, db_vnfd, vca_id, scaling_info + ) + scaling_info.pop("kdu-create", None) + scaling_info.pop("kdu-delete", None) + + scale_process = None + # SCALE KDU - END + if db_nsr_update: self.update_db_2("nsrs", nsr_id, db_nsr_update) - # SCALE RO - END # SCALE-UP VCA - BEGIN - if VCA_scaling_info: + if vca_scaling_info: step = db_nslcmop_update[ "detailed-status" ] = "Creating new execution environments" scale_process = "VCA" - for vdu_info in VCA_scaling_info: - if vdu_info["type"] == "create": - member_vnf_index = str(vdu_info["member-vnf-index"]) + for vca_info in vca_scaling_info: + if vca_info["type"] == "create": + member_vnf_index = str(vca_info["member-vnf-index"]) self.logger.debug( - logging_text + "vdu info: {}".format(vdu_info) + logging_text + "vdu info: {}".format(vca_info) ) vnfd_id = db_vnfr["vnfd-ref"] - vdu_index = int(vdu_info["vdu_index"]) - deploy_params = {"OSM": get_osm_params(db_vnfr)} - if db_vnfr.get("additionalParamsForVnf"): - deploy_params.update( - parse_yaml_strings( - db_vnfr["additionalParamsForVnf"].copy() + if vca_info.get("osm_vdu_id"): + vdu_index = int(vca_info["vdu_index"]) + deploy_params = {"OSM": get_osm_params(db_vnfr)} + if db_vnfr.get("additionalParamsForVnf"): + deploy_params.update( + parse_yaml_strings( + db_vnfr["additionalParamsForVnf"].copy() + ) ) + descriptor_config = get_configuration( + db_vnfd, db_vnfd["id"] ) - descriptor_config = get_configuration(db_vnfd, db_vnfd["id"]) - if descriptor_config: - vdu_id = None - vdu_name = None - kdu_name = None - self._deploy_n2vc( - logging_text=logging_text - + "member_vnf_index={} ".format(member_vnf_index), - db_nsr=db_nsr, - db_vnfr=db_vnfr, - nslcmop_id=nslcmop_id, - nsr_id=nsr_id, - nsi_id=nsi_id, - vnfd_id=vnfd_id, - vdu_id=vdu_id, - kdu_name=kdu_name, - member_vnf_index=member_vnf_index, - vdu_index=vdu_index, - vdu_name=vdu_name, - deploy_params=deploy_params, - descriptor_config=descriptor_config, - base_folder=base_folder, - task_instantiation_info=tasks_dict_info, - stage=stage, - ) - vdu_id = vdu_info["osm_vdu_id"] - vdur = find_in_list( - db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id - ) - descriptor_config = get_configuration(db_vnfd, vdu_id) - if vdur.get("additionalParams"): - deploy_params_vdu = parse_yaml_strings( - vdur["additionalParams"] + if descriptor_config: + vdu_id = None + vdu_name = None + kdu_name = None + self._deploy_n2vc( + logging_text=logging_text + + "member_vnf_index={} ".format(member_vnf_index), + db_nsr=db_nsr, + db_vnfr=db_vnfr, + nslcmop_id=nslcmop_id, + nsr_id=nsr_id, + nsi_id=nsi_id, + vnfd_id=vnfd_id, + vdu_id=vdu_id, + kdu_name=kdu_name, + member_vnf_index=member_vnf_index, + vdu_index=vdu_index, + vdu_name=vdu_name, + deploy_params=deploy_params, + descriptor_config=descriptor_config, + base_folder=base_folder, + task_instantiation_info=tasks_dict_info, + stage=stage, + ) + vdu_id = vca_info["osm_vdu_id"] + vdur = find_in_list( + db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id ) - else: - deploy_params_vdu = deploy_params - deploy_params_vdu["OSM"] = get_osm_params( - db_vnfr, vdu_id, vdu_count_index=vdu_index - ) - if descriptor_config: - vdu_name = None - kdu_name = None - stage[ - 1 - ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format( - member_vnf_index, vdu_id, vdu_index + descriptor_config = get_configuration(db_vnfd, vdu_id) + if vdur.get("additionalParams"): + deploy_params_vdu = parse_yaml_strings( + vdur["additionalParams"] + ) + else: + deploy_params_vdu = deploy_params + deploy_params_vdu["OSM"] = get_osm_params( + db_vnfr, vdu_id, vdu_count_index=vdu_index ) - stage[2] = step = "Scaling out VCA" - self._write_op_status(op_id=nslcmop_id, stage=stage) - self._deploy_n2vc( - logging_text=logging_text - + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format( + if descriptor_config: + vdu_name = None + kdu_name = None + stage[ + 1 + ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format( member_vnf_index, vdu_id, vdu_index - ), - db_nsr=db_nsr, - db_vnfr=db_vnfr, - nslcmop_id=nslcmop_id, - nsr_id=nsr_id, - nsi_id=nsi_id, - vnfd_id=vnfd_id, - vdu_id=vdu_id, - kdu_name=kdu_name, - member_vnf_index=member_vnf_index, - vdu_index=vdu_index, - vdu_name=vdu_name, - deploy_params=deploy_params_vdu, - descriptor_config=descriptor_config, - base_folder=base_folder, - task_instantiation_info=tasks_dict_info, - stage=stage, - ) + ) + stage[2] = step = "Scaling out VCA" + self._write_op_status(op_id=nslcmop_id, stage=stage) + self._deploy_n2vc( + logging_text=logging_text + + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format( + member_vnf_index, vdu_id, vdu_index + ), + db_nsr=db_nsr, + db_vnfr=db_vnfr, + nslcmop_id=nslcmop_id, + nsr_id=nsr_id, + nsi_id=nsi_id, + vnfd_id=vnfd_id, + vdu_id=vdu_id, + kdu_name=kdu_name, + member_vnf_index=member_vnf_index, + vdu_index=vdu_index, + vdu_name=vdu_name, + deploy_params=deploy_params_vdu, + descriptor_config=descriptor_config, + base_folder=base_folder, + task_instantiation_info=tasks_dict_info, + stage=stage, + ) + else: + kdu_name = vca_info["osm_kdu_id"] + descriptor_config = get_configuration(db_vnfd, kdu_name) + if descriptor_config: + vdu_id = None + kdu_index = int(vca_info["kdu_index"]) + vdu_name = None + kdur = next( + x + for x in db_vnfr["kdur"] + if x["kdu-name"] == kdu_name + ) + deploy_params_kdu = {"OSM": get_osm_params(db_vnfr)} + if kdur.get("additionalParams"): + deploy_params_kdu = parse_yaml_strings( + kdur["additionalParams"] + ) + + self._deploy_n2vc( + logging_text=logging_text, + db_nsr=db_nsr, + db_vnfr=db_vnfr, + nslcmop_id=nslcmop_id, + nsr_id=nsr_id, + nsi_id=nsi_id, + vnfd_id=vnfd_id, + vdu_id=vdu_id, + kdu_name=kdu_name, + member_vnf_index=member_vnf_index, + vdu_index=kdu_index, + vdu_name=vdu_name, + deploy_params=deploy_params_kdu, + descriptor_config=descriptor_config, + base_folder=base_folder, + task_instantiation_info=tasks_dict_info, + stage=stage, + ) # SCALE-UP VCA - END scale_process = None @@ -5551,7 +5920,7 @@ class NsLcm(LcmBase): vnf_config_primitive ) - vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info} + vnfr_params = {"VDU_SCALE_INFO": scaling_info} if db_vnfr.get("additionalParamsForVnf"): vnfr_params.update(db_vnfr["additionalParamsForVnf"]) @@ -5578,7 +5947,6 @@ class NsLcm(LcmBase): # Post-scale retry check: Check if this sub-operation has been executed before op_index = self._check_or_add_scale_suboperation( db_nslcmop, - nslcmop_id, vnf_index, vnf_config_primitive, primitive_params, @@ -5761,6 +6129,107 @@ class NsLcm(LcmBase): self.logger.debug(logging_text + "Exit") self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_scale") + async def _scale_kdu( + self, logging_text, nsr_id, nsr_deployed, db_vnfd, vca_id, scaling_info + ): + _scaling_info = scaling_info.get("kdu-create") or scaling_info.get("kdu-delete") + for kdu_name in _scaling_info: + for kdu_scaling_info in _scaling_info[kdu_name]: + deployed_kdu, index = get_deployed_kdu( + nsr_deployed, kdu_name, kdu_scaling_info["member-vnf-index"] + ) + cluster_uuid = deployed_kdu["k8scluster-uuid"] + kdu_instance = deployed_kdu["kdu-instance"] + scale = int(kdu_scaling_info["scale"]) + k8s_cluster_type = kdu_scaling_info["k8s-cluster-type"] + + db_dict = { + "collection": "nsrs", + "filter": {"_id": nsr_id}, + "path": "_admin.deployed.K8s.{}".format(index), + } + + step = "scaling application {}".format( + kdu_scaling_info["resource-name"] + ) + self.logger.debug(logging_text + step) + + if kdu_scaling_info["type"] == "delete": + kdu_config = get_configuration(db_vnfd, kdu_name) + if ( + kdu_config + and kdu_config.get("terminate-config-primitive") + and get_juju_ee_ref(db_vnfd, kdu_name) is None + ): + terminate_config_primitive_list = kdu_config.get( + "terminate-config-primitive" + ) + terminate_config_primitive_list.sort( + key=lambda val: int(val["seq"]) + ) + + for ( + terminate_config_primitive + ) in terminate_config_primitive_list: + primitive_params_ = self._map_primitive_params( + terminate_config_primitive, {}, {} + ) + step = "execute terminate config primitive" + self.logger.debug(logging_text + step) + await asyncio.wait_for( + self.k8scluster_map[k8s_cluster_type].exec_primitive( + cluster_uuid=cluster_uuid, + kdu_instance=kdu_instance, + primitive_name=terminate_config_primitive["name"], + params=primitive_params_, + db_dict=db_dict, + vca_id=vca_id, + ), + timeout=600, + ) + + await asyncio.wait_for( + self.k8scluster_map[k8s_cluster_type].scale( + kdu_instance, + scale, + kdu_scaling_info["resource-name"], + vca_id=vca_id, + ), + timeout=self.timeout_vca_on_error, + ) + + if kdu_scaling_info["type"] == "create": + kdu_config = get_configuration(db_vnfd, kdu_name) + if ( + kdu_config + and kdu_config.get("initial-config-primitive") + and get_juju_ee_ref(db_vnfd, kdu_name) is None + ): + initial_config_primitive_list = kdu_config.get( + "initial-config-primitive" + ) + initial_config_primitive_list.sort( + key=lambda val: int(val["seq"]) + ) + + for initial_config_primitive in initial_config_primitive_list: + primitive_params_ = self._map_primitive_params( + initial_config_primitive, {}, {} + ) + step = "execute initial config primitive" + self.logger.debug(logging_text + step) + await asyncio.wait_for( + self.k8scluster_map[k8s_cluster_type].exec_primitive( + cluster_uuid=cluster_uuid, + kdu_instance=kdu_instance, + primitive_name=initial_config_primitive["name"], + params=primitive_params_, + db_dict=db_dict, + vca_id=vca_id, + ), + timeout=600, + ) + async def _scale_ng_ro( self, logging_text, db_nsr, db_nslcmop, db_vnfr, vdu_scaling_info, stage ):