tacker/tacker/sol_refactored/conductor/vnflcm_driver_v2.py

427 lines
18 KiB
Python

# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import pickle
import subprocess
from oslo_log import log as logging
from oslo_utils import uuidutils
from tacker.sol_refactored.common import config
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import lcm_op_occ_utils as lcmocc_utils
from tacker.sol_refactored.common import vim_utils
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
from tacker.sol_refactored.infra_drivers.openstack import openstack
from tacker.sol_refactored.nfvo import nfvo_client
from tacker.sol_refactored import objects
from tacker.sol_refactored.objects.v2 import fields as v2fields
LOG = logging.getLogger(__name__)
CONF = config.CONF
class VnfLcmDriverV2(object):
def __init__(self):
self.endpoint = CONF.v2_vnfm.endpoint
self.nfvo_client = nfvo_client.NfvoClient()
def grant(self, context, lcmocc, inst, vnfd):
method = getattr(self, "%s_%s" % (lcmocc.operation.lower(), 'grant'))
return method(context, lcmocc, inst, vnfd)
def post_grant(self, context, lcmocc, inst, grant_req, grant, vnfd):
method = getattr(self,
"%s_%s" % (lcmocc.operation.lower(), 'post_grant'),
None)
if method:
method(context, lcmocc, inst, grant_req, grant, vnfd)
def _exec_mgmt_driver_script(self, operation, flavour_id, req, inst,
grant_req, grant, vnfd):
script = vnfd.get_interface_script(flavour_id, operation)
if script is None:
return
tmp_csar_dir = vnfd.make_tmp_csar_dir()
script_dict = {
'operation': operation,
'request': req.to_dict(),
'vnf_instance': inst.to_dict(),
'grant_request': grant_req.to_dict(),
'grant_response': grant.to_dict(),
'tmp_csar_dir': tmp_csar_dir
}
# script is relative path to Definitions/xxx.yaml
script_path = os.path.join(tmp_csar_dir, "Definitions", script)
out = subprocess.run(["python3", script_path],
input=pickle.dumps(script_dict),
capture_output=True)
vnfd.remove_tmp_csar_dir(tmp_csar_dir)
if out.returncode != 0:
LOG.debug("execute %s failed: %s", operation, out.stderr)
msg = "{} failed: {}".format(operation, out.stderr)
raise sol_ex.MgmtDriverExecutionFailed(sol_detail=msg)
LOG.debug("execute %s of %s success.", operation, script)
def process(self, context, lcmocc, inst, grant_req, grant, vnfd):
# perform preamble LCM script
req = lcmocc.operationParams
operation = "%s_%s" % (lcmocc.operation.lower(), 'start')
if lcmocc.operation == v2fields.LcmOperationType.INSTANTIATE:
flavour_id = req.flavourId
else:
flavour_id = inst.instantiatedVnfInfo.flavourId
self._exec_mgmt_driver_script(operation,
flavour_id, req, inst, grant_req, grant, vnfd)
# main process
method = getattr(self, "%s_%s" % (lcmocc.operation.lower(), 'process'))
method(context, lcmocc, inst, grant_req, grant, vnfd)
# perform postamble LCM script
operation = "%s_%s" % (lcmocc.operation.lower(), 'end')
self._exec_mgmt_driver_script(operation,
flavour_id, req, inst, grant_req, grant, vnfd)
def rollback(self, context, lcmocc, inst, grant_req, grant, vnfd):
method = getattr(self,
"%s_%s" % (lcmocc.operation.lower(), 'rollback'),
None)
if method:
method(context, lcmocc, inst, grant_req, grant, vnfd)
else:
raise sol_ex.RollbackNotSupported(op=lcmocc.operation)
def _get_link_ports(self, inst_req):
names = []
if inst_req.obj_attr_is_set('extVirtualLinks'):
for ext_vl in inst_req.extVirtualLinks:
for ext_cp in ext_vl.extCps:
for cp_config in ext_cp.cpConfig.values():
if cp_config.obj_attr_is_set('linkPortId'):
names.append(ext_cp.cpdId)
if inst_req.obj_attr_is_set('extManagedVirtualLinks'):
for ext_mgd_vl in inst_req.extManagedVirtualLinks:
if ext_mgd_vl.obj_attr_is_set('vnfLinkPort'):
names.append(ext_mgd_vl.vnfVirtualLinkDescId)
return names
def instantiate_grant(self, context, lcmocc, inst, vnfd):
req = lcmocc.operationParams
flavour_id = req.flavourId
if vnfd.get_vnfd_flavour(flavour_id) is None:
raise sol_ex.FlavourIdNotFound(flavour_id=flavour_id)
# grant exchange
# NOTE: the api_version of NFVO supposes 1.4.0 at the moment.
grant_req = objects.GrantRequestV1(
vnfInstanceId=inst.id,
vnfLcmOpOccId=lcmocc.id,
vnfdId=inst.vnfdId,
flavourId=flavour_id,
operation=lcmocc.operation,
isAutomaticInvocation=lcmocc.isAutomaticInvocation
)
if req.obj_attr_is_set('instantiationLevelId'):
inst_level = req.instantiationLevelId
grant_req.instantiationLevelId = inst_level
else:
inst_level = vnfd.get_default_instantiation_level(flavour_id)
add_reses = []
nodes = vnfd.get_vdu_nodes(flavour_id)
link_port_names = self._get_link_ports(req)
for name, node in nodes.items():
num = vnfd.get_vdu_num(flavour_id, name, inst_level)
vdu_cp_names = vnfd.get_vdu_cps(flavour_id, name)
vdu_storage_names = vnfd.get_vdu_storages(node)
for _ in range(num):
res_def = objects.ResourceDefinitionV1(
id=uuidutils.generate_uuid(),
type='COMPUTE',
resourceTemplateId=name)
add_reses.append(res_def)
for cp_name in vdu_cp_names:
if cp_name in link_port_names:
continue
for _ in range(num):
res_def = objects.ResourceDefinitionV1(
id=uuidutils.generate_uuid(),
type='LINKPORT',
resourceTemplateId=cp_name)
add_reses.append(res_def)
for storage_name in vdu_storage_names:
for _ in range(num):
res_def = objects.ResourceDefinitionV1(
id=uuidutils.generate_uuid(),
type='STORAGE',
resourceTemplateId=storage_name)
add_reses.append(res_def)
ext_mgd_vls = []
if req.obj_attr_is_set('extManagedVirtualLinks'):
ext_mgd_vls = [ext_mgd_vl.vnfVirtualLinkDescId
for ext_mgd_vl in req.extManagedVirtualLinks]
nodes = vnfd.get_virtual_link_nodes(flavour_id)
for name in nodes.keys():
if name in ext_mgd_vls:
continue
res_def = objects.ResourceDefinitionV1(
id=uuidutils.generate_uuid(),
type='VL',
resourceTemplateId=name)
add_reses.append(res_def)
if add_reses:
grant_req.addResources = add_reses
# placementConstraints
affinity_policies = {
'AFFINITY': vnfd.get_affinity_targets(flavour_id),
'ANTI_AFFINITY': vnfd.get_anti_affinity_targets(flavour_id)
}
plc_consts = []
for key, value in affinity_policies.items():
for targets, scope in value:
res_refs = []
for target in targets:
for res in add_reses:
if res.resourceTemplateId == target:
res_ref = objects.ConstraintResourceRefV1(
idType='GRANT',
resourceId=res.id)
res_refs.append(res_ref)
plc_const = objects.PlacementConstraintV1(
affinityOrAntiAffinity=key,
scope=scope.upper(),
resource=res_refs)
plc_consts.append(plc_const)
if plc_consts:
grant_req.placementConstraints = plc_consts
if req.obj_attr_is_set('additionalParams'):
grant_req.additionalParams = req.additionalParams
grant_req._links = objects.GrantRequestV1_Links(
vnfLcmOpOcc=objects.Link(
href=lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)),
vnfInstance=objects.Link(
href=inst_utils.inst_href(inst.id, self.endpoint)))
# NOTE: if not granted, 403 error raised.
grant = self.nfvo_client.grant(context, grant_req)
return grant_req, grant
def instantiate_post_grant(self, context, lcmocc, inst, grant_req,
grant, vnfd):
# set inst vimConnectionInfo
req = lcmocc.operationParams
vim_infos = {}
if req.obj_attr_is_set('vimConnectionInfo'):
vim_infos = req.vimConnectionInfo
if grant.obj_attr_is_set('vimConnectionInfo'):
# if NFVO returns vimConnectionInfo use it.
# As the controller does for req.vimConnectionInfo, if accessInfo
# or interfaceInfo is not specified, get them from VIM DB.
# vimId must be in VIM DB.
res_vim_infos = grant.vimConnectioninfo
for key, res_vim_info in res_vim_infos.items():
if not (res_vim_info.obj_attr_is_set('accessInfo') and
res_vim_info.obj_attr_is_set('interfaceInfo')):
vim_info = vim_utils.get_vim(context, res_vim_info.vimId)
res_vim_infos[key] = vim_info
vim_infos = inst_utils.json_merge_patch(vim_infos, res_vim_infos)
if not vim_infos:
# use the default VIM for the project. tacker special.
vim_info = vim_utils.get_default_vim(context)
if vim_info:
vim_infos["default"] = vim_info
else:
# must be one vimConnectionInfo at least.
raise sol_ex.NoVimConnectionInfo()
inst.vimConnectionInfo = vim_infos
def instantiate_process(self, context, lcmocc, inst, grant_req,
grant, vnfd):
req = lcmocc.operationParams
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
driver = openstack.Openstack()
driver.instantiate(req, inst, grant_req, grant, vnfd)
else:
# only support openstack at the moment
raise sol_ex.SolException(sol_detail='not support vim type')
inst.instantiationState = 'INSTANTIATED'
lcmocc_utils.make_instantiate_lcmocc(lcmocc, inst)
def instantiate_rollback(self, context, lcmocc, inst, grant_req,
grant, vnfd):
req = lcmocc.operationParams
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
driver = openstack.Openstack()
driver.instantiate_rollback(req, inst, grant_req, grant, vnfd)
else:
# only support openstack at the moment
raise sol_ex.SolException(sol_detail='not support vim type')
def terminate_grant(self, context, lcmocc, inst, vnfd):
# grant exchange
# NOTE: the api_version of NFVO supposes 1.4.0 at the moment.
grant_req = objects.GrantRequestV1(
vnfInstanceId=inst.id,
vnfLcmOpOccId=lcmocc.id,
vnfdId=inst.vnfdId,
operation=lcmocc.operation,
isAutomaticInvocation=lcmocc.isAutomaticInvocation
)
inst_info = inst.instantiatedVnfInfo
rm_reses = []
vnfc_cps = {}
if inst_info.obj_attr_is_set('vnfcResourceInfo'):
for inst_vnc in inst_info.vnfcResourceInfo:
res_def = objects.ResourceDefinitionV1(
id=uuidutils.generate_uuid(),
type='COMPUTE',
resourceTemplateId=inst_vnc.vduId,
resource=inst_vnc.computeResource)
rm_reses.append(res_def)
if inst_vnc.obj_attr_is_set('vnfcCpInfo'):
for cp_info in inst_vnc.vnfcCpInfo:
if not (cp_info.obj_attr_is_set('vnfExtCpId') or
cp_info.obj_attr_is_set('vnfLinkPortId')):
# it means extLinkPorts of extVirtualLinks was
# specified. so it is not the resource to be
# deleted.
continue
res_def = objects.ResourceDefinitionV1(
id=uuidutils.generate_uuid(),
resourceTemplateId=cp_info.cpdId,
type='LINKPORT')
rm_reses.append(res_def)
if cp_info.obj_attr_is_set('vnfExtCpId'):
vnfc_cps[cp_info.vnfExtCpId] = res_def
else: # vnfLinkPortId
vnfc_cps[cp_info.vnfLinkPortId] = res_def
if inst_info.obj_attr_is_set('vnfVirtualLinkResourceInfo'):
for inst_vl in inst_info.vnfVirtualLinkResourceInfo:
res_def = objects.ResourceDefinitionV1(
id=uuidutils.generate_uuid(),
type='VL',
resourceTemplateId=inst_vl.vnfVirtualLinkDescId,
resource=inst_vl.networkResource)
rm_reses.append(res_def)
if inst_vl.obj_attr_is_set('vnfLinkPorts'):
for port in inst_vl.vnfLinkPorts:
if port.id in vnfc_cps:
res_def = vnfc_cps[port.id]
res_def.resource = port.resourceHandle
if inst_info.obj_attr_is_set('virtualStorageResourceInfo'):
for inst_str in inst_info.virtualStorageResourceInfo:
res_def = objects.ResourceDefinitionV1(
id=uuidutils.generate_uuid(),
type='STORAGE',
resourceTemplateId=inst_str.virtualStorageDescId,
resource=inst_str.storageResource)
rm_reses.append(res_def)
if inst_info.obj_attr_is_set('extVirtualLinkInfo'):
for ext_vl in inst_info.extVirtualLinkInfo:
if ext_vl.obj_attr_is_set('extLinkPorts'):
for port in ext_vl.extLinkPorts:
if (port.obj_attr_is_set('cpInstanceId') and
port.cpInstanceId in vnfc_cps):
res_def = vnfc_cps[port.cpInstanceId]
res_def.resource = port.resourceHandle
if inst_info.obj_attr_is_set('extManagedVirtualLinkInfo'):
for ext_mgd_vl in inst_info.extManagedVirtualLinkInfo:
if ext_mgd_vl.obj_attr_is_set('vnfLinkPorts'):
for port in ext_mgd_vl.vnfLinkPorts:
if (port.obj_attr_is_set('cpInstanceId') and
port.id in vnfc_cps):
res_def = vnfc_cps[port.id]
res_def.resource = port.resourceHandle
if rm_reses:
grant_req.removeResources = rm_reses
grant_req._links = objects.GrantRequestV1_Links(
vnfLcmOpOcc=objects.Link(
href=lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)),
vnfInstance=objects.Link(
href=inst_utils.inst_href(inst.id, self.endpoint)))
# NOTE: if not granted, 403 error raised.
grant_res = self.nfvo_client.grant(context, grant_req)
return grant_req, grant_res
def terminate_process(self, context, lcmocc, inst, grant_req,
grant, vnfd):
req = lcmocc.operationParams
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
driver = openstack.Openstack()
driver.terminate(req, inst, grant_req, grant, vnfd)
else:
# only support openstack at the moment
raise sol_ex.SolException(sol_detail='not support vim type')
inst.instantiationState = 'NOT_INSTANTIATED'
lcmocc_utils.make_terminate_lcmocc(lcmocc, inst)
# reset instantiatedVnfInfo
# NOTE: reset after update lcmocc
inst_vnf_info = objects.VnfInstanceV2_InstantiatedVnfInfo(
flavourId=inst.instantiatedVnfInfo.flavourId,
vnfState='STOPPED',
# NOTE: extCpInfo is omitted. its cardinality is 1..N but it is
# meaningless to have it for terminated vnf instance.
)
inst.instantiatedVnfInfo = inst_vnf_info
# reset vimConnectionInfo
inst.vimConnectionInfo = {}