Merge "Support ChangeCurrentVNFPackage for VNF of v2 API"
This commit is contained in:
commit
a47f8cf945
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Add Change Current VNF Package API based on ETSI NFV specifications.
|
||||
Tacker supports a VNF upgrade using this API.
|
||||
Currently, it only supports "RollingUpdate" out of several methods for
|
||||
a VNF upgrade.
|
||||
|
|
@ -129,6 +129,15 @@ rules = [
|
|||
'path': VNF_INSTANCES_ID_PATH + '/change_ext_conn'}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('change_vnfpkg'),
|
||||
check_str=RULE_ANY,
|
||||
description="Change vnf package.",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'path': VNF_INSTANCES_ID_PATH + '/change_vnfpkg'}
|
||||
]
|
||||
),
|
||||
# NOTE: add when the operation supported
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('subscription_create'),
|
||||
|
|
|
@ -40,6 +40,7 @@ class VnflcmAPIRouterV2(sol_wsgi.SolAPIRouter):
|
|||
("/vnf_instances/{id}/terminate", {"POST": "terminate"}),
|
||||
("/vnf_instances/{id}/scale", {"POST": "scale"}),
|
||||
("/vnf_instances/{id}/change_ext_conn", {"POST": "change_ext_conn"}),
|
||||
("/vnf_instances/{id}/change_vnfpkg", {"POST": "change_vnfpkg"}),
|
||||
("/api_versions", {"GET": "api_versions"}),
|
||||
("/subscriptions", {"GET": "subscription_list",
|
||||
"POST": "subscription_create"}),
|
||||
|
|
|
@ -77,6 +77,33 @@ TerminateVnfRequest_V200 = {
|
|||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL002 5.5.2.11a
|
||||
# SOL003 5.5.2.11a
|
||||
ChangeCurrentVnfPkgRequest_V200 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vnfdId': common_types.Identifier,
|
||||
'extVirtualLinks': {
|
||||
'type': 'array',
|
||||
'items': common_types.ExtVirtualLinkData},
|
||||
'extManagedVirtualLinks': {
|
||||
'type': 'array',
|
||||
'items': common_types.ExtManagedVirtualLinkData},
|
||||
# NOTE: 'vimConnectionInfo' field supports only NFV-SOL 003
|
||||
'vimConnectionInfo': {
|
||||
'type': 'object',
|
||||
'patternProperties': {
|
||||
'^.*$': common_types.VimConnectionInfo
|
||||
},
|
||||
},
|
||||
'additionalParams': parameter_types.keyvalue_pairs,
|
||||
'extensions': parameter_types.keyvalue_pairs,
|
||||
'vnfConfigurableProperties': parameter_types.keyvalue_pairs
|
||||
},
|
||||
'required': ['vnfdId'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 5.5.2.5
|
||||
ScaleVnfRequest_V200 = {
|
||||
'type': 'object',
|
||||
|
@ -263,6 +290,7 @@ _LifecycleChangeNotificationsFilter = {
|
|||
'SCALE',
|
||||
'SCALE_TO_LEVEL',
|
||||
'CHANGE_FLAVOUR',
|
||||
'CHANGE_VNFPKG',
|
||||
'TERMINATE',
|
||||
'HEAL',
|
||||
'OPERATE',
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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.
|
||||
|
||||
|
||||
from openstack import connection
|
||||
from openstack import exceptions as os_ex
|
||||
from oslo_log import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CinderClient(object):
|
||||
|
||||
def __init__(self, vim_info):
|
||||
auth = dict(
|
||||
auth_url=vim_info.interfaceInfo['endpoint'],
|
||||
username=vim_info.accessInfo['username'],
|
||||
password=vim_info.accessInfo['password'],
|
||||
project_name=vim_info.accessInfo['project'],
|
||||
user_domain_name=vim_info.accessInfo['userDomain'],
|
||||
project_domain_name=vim_info.accessInfo['projectDomain']
|
||||
)
|
||||
self.conn = connection.Connection(
|
||||
region_name=vim_info.accessInfo.get('region'),
|
||||
auth=auth,
|
||||
identity_interface='internal')
|
||||
|
||||
def get_volume(self, volume_id):
|
||||
try:
|
||||
return self.conn.volume.get_volume(volume_id)
|
||||
except os_ex.ResourceNotFound:
|
||||
LOG.debug("volume %s not found.", volume_id)
|
|
@ -0,0 +1,279 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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.
|
||||
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
|
||||
|
||||
def get_vnfd(vnfd_id, csar_dir):
|
||||
vnfd = vnfd_utils.Vnfd(vnfd_id)
|
||||
vnfd.init_from_csar_dir(csar_dir)
|
||||
return vnfd
|
||||
|
||||
|
||||
def get_vdu_info(grant, inst, vnfd):
|
||||
volume_name = ''
|
||||
volume_size = ''
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
vdu_nodes = vnfd.get_vdu_nodes(flavour_id)
|
||||
storage_nodes = vnfd.get_storage_nodes(flavour_id)
|
||||
vdu_info_dict = {}
|
||||
for name, node in vdu_nodes.items():
|
||||
flavor = get_param_flavor(name, flavour_id, vnfd, grant)
|
||||
image = get_param_image(name, flavour_id, vnfd, grant)
|
||||
vdu_storage_names = vnfd.get_vdu_storages(node)
|
||||
for vdu_storage_name in vdu_storage_names:
|
||||
if storage_nodes[vdu_storage_name].get(
|
||||
'properties', {}).get('sw_image_data'):
|
||||
image = get_param_image(vdu_storage_name, flavour_id, vnfd,
|
||||
grant)
|
||||
volume_name = vdu_storage_name
|
||||
volume_size = storage_nodes[vdu_storage_name].get(
|
||||
'properties', {}).get(
|
||||
'virtual_block_storage_data', '').get(
|
||||
'size_of_storage', ''
|
||||
)
|
||||
volume_size = volume_size.rstrip(' GB')
|
||||
if not volume_size.isdigit():
|
||||
raise sol_ex.VmRunningFailed(
|
||||
error_info='The volume size set in VNFD is invalid.')
|
||||
break
|
||||
|
||||
vdu_info_dict[name] = {
|
||||
"flavor": flavor,
|
||||
"image": image
|
||||
}
|
||||
|
||||
if volume_name:
|
||||
vdu_info_dict[name]['volume_info'] = {
|
||||
"volume_name": volume_name,
|
||||
"volume_size": volume_size
|
||||
}
|
||||
return vdu_info_dict
|
||||
|
||||
|
||||
def init_nfv_dict(hot_template):
|
||||
get_params = []
|
||||
|
||||
def _get_get_param(prop):
|
||||
if isinstance(prop, dict):
|
||||
for key, value in prop.items():
|
||||
if key == 'get_param':
|
||||
get_params.append(value)
|
||||
else:
|
||||
_get_get_param(value)
|
||||
elif isinstance(prop, list):
|
||||
for value in prop:
|
||||
_get_get_param(value)
|
||||
|
||||
for res in hot_template.get('resources', {}).values():
|
||||
_get_get_param(res.get('properties', {}))
|
||||
|
||||
nfv = {}
|
||||
|
||||
for param in get_params:
|
||||
if (not isinstance(param, list) or len(param) < 4 or
|
||||
param[0] != 'nfv'):
|
||||
continue
|
||||
parent = nfv
|
||||
for item in param[1:-1]:
|
||||
parent.setdefault(item, {})
|
||||
parent = parent[item]
|
||||
parent[param[-1]] = None
|
||||
|
||||
# TODO(YiFeng): enhance to handle list
|
||||
# NOTE: List is not considered here and only 'fixed_ips' is treated as
|
||||
# list in userdata_default.py at the moment.
|
||||
# Note that if handling list is enhanced, userdata_default.py is
|
||||
# necessary to modify.
|
||||
return nfv
|
||||
|
||||
|
||||
def get_param_flavor(vdu_name, flavour_id, vnfd, grant):
|
||||
# try to get from grant
|
||||
if 'vimAssets' in grant:
|
||||
assets = grant['vimAssets']
|
||||
if 'computeResourceFlavours' in assets:
|
||||
flavours = assets['computeResourceFlavours']
|
||||
for flavour in flavours:
|
||||
if flavour['vnfdVirtualComputeDescId'] == vdu_name:
|
||||
return flavour['vimFlavourId']
|
||||
|
||||
# if specified in VNFD, use it
|
||||
# NOTE: if not found. parameter is set to None.
|
||||
# may be error when stack create
|
||||
return vnfd.get_compute_flavor(flavour_id, vdu_name)
|
||||
|
||||
|
||||
def get_param_image(vdu_name, flavour_id, vnfd, grant):
|
||||
# try to get from grant
|
||||
if 'vimAssets' in grant:
|
||||
assets = grant['vimAssets']
|
||||
if 'softwareImages' in assets:
|
||||
images = assets['softwareImages']
|
||||
for image in images:
|
||||
if image['vnfdSoftwareImageId'] == vdu_name:
|
||||
return image['vimSoftwareImageId']
|
||||
|
||||
# if specified in VNFD, use it
|
||||
# NOTE: if not found. parameter is set to None.
|
||||
# may be error when stack create
|
||||
sw_images = vnfd.get_sw_image(flavour_id)
|
||||
for name, image in sw_images.items():
|
||||
if name == vdu_name:
|
||||
return image
|
||||
|
||||
|
||||
def get_param_zone(vdu_name, grant_req, grant):
|
||||
if 'zones' not in grant or 'addResources' not in grant:
|
||||
return
|
||||
|
||||
for res in grant['addResources']:
|
||||
if 'zoneId' not in res:
|
||||
continue
|
||||
for req_res in grant_req['addResources']:
|
||||
if req_res['id'] == res['resourceDefinitionId']:
|
||||
if req_res.get('resourceTemplateId') == vdu_name:
|
||||
for zone in grant['zones']:
|
||||
if zone['id'] == res['zoneId']: # must be found
|
||||
return zone['zoneId']
|
||||
|
||||
|
||||
def get_current_capacity(vdu_name, inst):
|
||||
count = 0
|
||||
inst_vnfcs = (inst.get('instantiatedVnfInfo', {})
|
||||
.get('vnfcResourceInfo', []))
|
||||
for inst_vnfc in inst_vnfcs:
|
||||
if inst_vnfc['vduId'] == vdu_name:
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
|
||||
def get_param_capacity(vdu_name, inst, grant_req):
|
||||
# NOTE: refer grant_req here since interpretation of VNFD was done when
|
||||
# making grant_req.
|
||||
count = get_current_capacity(vdu_name, inst)
|
||||
|
||||
add_reses = grant_req.get('addResources', [])
|
||||
for res_def in add_reses:
|
||||
if (res_def['type'] == 'COMPUTE' and
|
||||
res_def['resourceTemplateId'] == vdu_name):
|
||||
count += 1
|
||||
|
||||
rm_reses = grant_req.get('removeResources', [])
|
||||
for res_def in rm_reses:
|
||||
if (res_def['type'] == 'COMPUTE' and
|
||||
res_def['resourceTemplateId'] == vdu_name):
|
||||
count -= 1
|
||||
|
||||
return count
|
||||
|
||||
|
||||
def _get_fixed_ips_from_extcp(extcp):
|
||||
fixed_ips = []
|
||||
for cp_conf in extcp['cpConfig'].values():
|
||||
if 'cpProtocolData' not in cp_conf:
|
||||
continue
|
||||
for prot_data in cp_conf['cpProtocolData']:
|
||||
if 'ipOverEthernet' not in prot_data:
|
||||
continue
|
||||
if 'ipAddresses' not in prot_data['ipOverEthernet']:
|
||||
continue
|
||||
for ip in prot_data['ipOverEthernet']['ipAddresses']:
|
||||
data = {}
|
||||
if 'fixedAddresses' in ip:
|
||||
# pick up only one ip address
|
||||
data['ip_address'] = str(ip['fixedAddresses'][0])
|
||||
if 'subnetId' in ip:
|
||||
data['subnet'] = ip['subnetId']
|
||||
if data:
|
||||
fixed_ips.append(data)
|
||||
return fixed_ips
|
||||
|
||||
|
||||
def get_param_network(cp_name, grant, req):
|
||||
# see grant first then instantiateVnfRequest
|
||||
vls = grant.get('extVirtualLinks', []) + req.get('extVirtualLinks', [])
|
||||
for vl in vls:
|
||||
for extcp in vl['extCps']:
|
||||
if extcp['cpdId'] == cp_name:
|
||||
return vl['resourceId']
|
||||
|
||||
|
||||
def get_param_fixed_ips(cp_name, grant, req):
|
||||
# see grant first then instantiateVnfRequest
|
||||
vls = grant.get('extVirtualLinks', []) + req.get('extVirtualLinks', [])
|
||||
for vl in vls:
|
||||
for extcp in vl['extCps']:
|
||||
if extcp['cpdId'] == cp_name:
|
||||
return _get_fixed_ips_from_extcp(extcp)
|
||||
|
||||
|
||||
def get_param_network_from_inst(cp_name, inst):
|
||||
for vl in inst['instantiatedVnfInfo'].get('extVirtualLinkInfo', []):
|
||||
for extcp in vl.get('currentVnfExtCpData', []):
|
||||
if extcp['cpdId'] == cp_name:
|
||||
return vl['resourceHandle']['resourceId']
|
||||
|
||||
|
||||
def get_param_fixed_ips_from_inst(cp_name, inst):
|
||||
for vl in inst['instantiatedVnfInfo'].get('extVirtualLinkInfo', []):
|
||||
for extcp in vl.get('currentVnfExtCpData', []):
|
||||
if extcp['cpdId'] == cp_name:
|
||||
return _get_fixed_ips_from_extcp(extcp)
|
||||
|
||||
|
||||
def apply_ext_managed_vls(hot_dict, req, grant):
|
||||
# see grant first then instantiateVnfRequest
|
||||
mgd_vls = (grant.get('extManagedVirtualLinks', []) +
|
||||
req.get('extManagedVirtualLinks', []))
|
||||
|
||||
# NOTE: refer HOT only here, not refer VNFD.
|
||||
# HOT and VNFD must be consistent.
|
||||
|
||||
for mgd_vl in mgd_vls:
|
||||
vl_name = mgd_vl['vnfVirtualLinkDescId']
|
||||
network_id = mgd_vl['resourceId']
|
||||
get_res = {'get_resource': vl_name}
|
||||
|
||||
def _change(item):
|
||||
if not isinstance(item, dict):
|
||||
return
|
||||
for key, value in item.items():
|
||||
if value == get_res:
|
||||
item[key] = network_id
|
||||
else:
|
||||
_change(value)
|
||||
|
||||
del_reses = []
|
||||
for res_name, res_data in hot_dict.get('resources', {}).items():
|
||||
# delete network definition
|
||||
if res_name == vl_name:
|
||||
del_reses.append(res_name)
|
||||
|
||||
# delete subnet definition
|
||||
if res_data['type'] == 'OS::Neutron::Subnet':
|
||||
net = (res_data.get('properties', {})
|
||||
.get('network', {})
|
||||
.get('get_resource'))
|
||||
if net == vl_name:
|
||||
del_reses.append(res_name)
|
||||
|
||||
# change '{get_resource: vl_name}' to network_id
|
||||
_change(res_data)
|
||||
|
||||
for res_name in del_reses:
|
||||
hot_dict['resources'].pop(res_name)
|
|
@ -114,6 +114,10 @@ class VnfInstanceNotFound(SolHttpError404):
|
|||
message = _("VnfInstance %(inst_id)s not found.")
|
||||
|
||||
|
||||
class NotSupportUpgradeType(SolHttpError400):
|
||||
message = _("not support upgrade_type %(upgrade_type)s")
|
||||
|
||||
|
||||
class VnfInstanceIsInstantiated(SolHttpError409):
|
||||
message = _("VnfInstance %(inst_id)s is instantiated.")
|
||||
|
||||
|
@ -122,6 +126,10 @@ class VnfInstanceIsNotInstantiated(SolHttpError409):
|
|||
message = _("VnfInstance %(inst_id)s isn't instantiated.")
|
||||
|
||||
|
||||
class VnfInstanceIsNotChanged(SolHttpError409):
|
||||
message = _("VnfInstance %(inst_id)s isn't changed.")
|
||||
|
||||
|
||||
class LccnSubscriptionNotFound(SolHttpError404):
|
||||
message = _("LccnSubscription %(subsc_id)s not found.")
|
||||
|
||||
|
@ -245,3 +253,24 @@ class DeltaMissingInVnfd(SolHttpError400):
|
|||
class ConductorProcessingError(SolException):
|
||||
title = 'Internal Server Error'
|
||||
message = _("Failure due to conductor processing error.")
|
||||
|
||||
|
||||
class InvalidVolumeSize(SolHttpError400):
|
||||
message = _("The volume size set in VNFD is invalid.")
|
||||
|
||||
|
||||
class VduIdNotFound(SolHttpError404):
|
||||
message = _("This vdu_id '%(vdu_id)s' does not exist"
|
||||
" in current VnfInstance.")
|
||||
|
||||
|
||||
class SshIpNotFoundException(SolHttpError404):
|
||||
message = _("Ssh ip not found.")
|
||||
|
||||
|
||||
class CoordinateVNFExecutionFailed(SolHttpError422):
|
||||
message = _('CoordinateVNF execution failed.')
|
||||
|
||||
|
||||
class VmRunningFailed(SolHttpError422):
|
||||
message = _("VM is running incorrectly. Reason: '%(error_info)s'")
|
||||
|
|
|
@ -126,6 +126,8 @@ def _make_affected_vnfc(vnfc, change_type, strgs):
|
|||
changeType=change_type,
|
||||
computeResource=vnfc.computeResource
|
||||
)
|
||||
if vnfc.obj_attr_is_set('metadata'):
|
||||
affected_vnfc.metadata = vnfc.metadata
|
||||
if vnfc.obj_attr_is_set('vnfcCpInfo'):
|
||||
cp_ids = [cp.id for cp in vnfc.vnfcCpInfo]
|
||||
affected_vnfc.affectedVnfcCpIds = cp_ids
|
||||
|
@ -381,7 +383,15 @@ def update_lcmocc(lcmocc, inst_saved, inst):
|
|||
for strg in inst_info.virtualStorageResourceInfo
|
||||
if strg.id in added_strgs]
|
||||
|
||||
removed_vnfcs, added_vnfcs, _ = _calc_diff('vnfcResourceInfo')
|
||||
removed_vnfcs, added_vnfcs, common_objs = _calc_diff('vnfcResourceInfo')
|
||||
updated_vnfcs = []
|
||||
if lcmocc.operation == fields.LcmOperationType.CHANGE_VNFPKG:
|
||||
updated_vnfcs = [
|
||||
obj.id for obj in inst_info.vnfcResourceInfo
|
||||
if obj.metadata.get('current_vnfd_id') != inst_saved.vnfdId
|
||||
and obj.id in common_objs and
|
||||
obj.metadata.get('current_vnfd_id') is not None]
|
||||
|
||||
affected_vnfcs = []
|
||||
if removed_vnfcs:
|
||||
affected_vnfcs += [
|
||||
|
@ -396,6 +406,11 @@ def update_lcmocc(lcmocc, inst_saved, inst):
|
|||
if vnfc.id in added_vnfcs
|
||||
]
|
||||
|
||||
if updated_vnfcs:
|
||||
affected_vnfcs += [_make_affected_vnfc(vnfc, 'MODIFIED', added_strgs)
|
||||
for vnfc in inst_info.vnfcResourceInfo
|
||||
if vnfc.id in updated_vnfcs]
|
||||
|
||||
removed_vls, added_vls, common_vls = _calc_diff(
|
||||
'vnfVirtualLinkResourceInfo')
|
||||
affected_vls = []
|
||||
|
@ -491,6 +506,17 @@ def update_lcmocc(lcmocc, inst_saved, inst):
|
|||
lcmocc.changedExtConnectivity = chg_ext_conn
|
||||
|
||||
|
||||
def get_inst_lcmocc(context, inst):
|
||||
lcmoccs = objects.VnfLcmOpOccV2.get_by_filter(
|
||||
context, vnfInstanceId=inst.id,
|
||||
operationState=fields.LcmOperationStateType.COMPLETED,
|
||||
operation=fields.LcmOperationType.INSTANTIATE)
|
||||
inst_lcmocc = [inst_lcmocc for inst_lcmocc in lcmoccs
|
||||
if inst_lcmocc.startTime ==
|
||||
max([lcmocc.startTime for lcmocc in lcmoccs])][0]
|
||||
return inst_lcmocc
|
||||
|
||||
|
||||
def get_grant_req_and_grant(context, lcmocc):
|
||||
if lcmocc.operation == fields.LcmOperationType.MODIFY_INFO:
|
||||
return None, None
|
||||
|
|
|
@ -49,6 +49,7 @@ def make_inst_links(inst, endpoint):
|
|||
links.scale = objects.Link(href=self_href + "/scale")
|
||||
links.heal = objects.Link(href=self_href + "/heal")
|
||||
links.changeExtConn = objects.Link(href=self_href + "/change_ext_conn")
|
||||
links.changeVnfPkg = objects.Link(href=self_href + "/change_vnfpkg")
|
||||
# NOTE: add when the operation supported
|
||||
|
||||
return links
|
||||
|
|
|
@ -18,10 +18,10 @@ import io
|
|||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import yaml
|
||||
import zipfile
|
||||
|
||||
from oslo_log import log as logging
|
||||
import yaml
|
||||
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.common import log
|
||||
from tacker import context as tacker_context
|
||||
|
@ -103,8 +104,12 @@ class ConductorV2(object):
|
|||
self.endpoint)
|
||||
|
||||
try:
|
||||
vnfd = self.nfvo_client.get_vnfd(context, inst.vnfdId,
|
||||
all_contents=True)
|
||||
if lcmocc.operation == fields.LcmOperationType.CHANGE_VNFPKG:
|
||||
vnfd = self.nfvo_client.get_vnfd(
|
||||
context, lcmocc.operationParams.vnfdId, all_contents=True)
|
||||
else:
|
||||
vnfd = self.nfvo_client.get_vnfd(context, inst.vnfdId,
|
||||
all_contents=True)
|
||||
|
||||
# NOTE: perform grant exchange mainly but also perform
|
||||
# something to do at STATING phase ex. request check.
|
||||
|
@ -237,20 +242,38 @@ class ConductorV2(object):
|
|||
|
||||
try:
|
||||
vnfd = self.nfvo_client.get_vnfd(context, inst.vnfdId)
|
||||
grant_req, grant = lcmocc_utils.get_grant_req_and_grant(context,
|
||||
lcmocc)
|
||||
grant_req, grant = lcmocc_utils.get_grant_req_and_grant(
|
||||
context, lcmocc)
|
||||
self.vnflcm_driver.post_grant(context, lcmocc, inst, grant_req,
|
||||
grant, vnfd)
|
||||
self.vnflcm_driver.rollback(context, lcmocc, inst, grant_req,
|
||||
grant, vnfd)
|
||||
if lcmocc.operation == fields.LcmOperationType.CHANGE_VNFPKG:
|
||||
inst_lcmocc = lcmocc_utils.get_inst_lcmocc(context, inst)
|
||||
inst_grant_req = objects.GrantRequestV1(
|
||||
vnfInstanceId=inst.id,
|
||||
vnfLcmOpOccId=inst_lcmocc.id,
|
||||
operation=inst_lcmocc.operation,
|
||||
isAutomaticInvocation=lcmocc.isAutomaticInvocation
|
||||
)
|
||||
inst_grant = objects.GrantV1(
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfInstanceId=inst_grant_req.vnfInstanceId,
|
||||
vnfLcmOpOccId=inst_grant_req.vnfLcmOpOccId
|
||||
)
|
||||
self.vnflcm_driver.rollback(
|
||||
context, lcmocc, inst, inst_grant_req, inst_grant, vnfd)
|
||||
else:
|
||||
self.vnflcm_driver.rollback(context, lcmocc, inst, grant_req,
|
||||
grant, vnfd)
|
||||
|
||||
lcmocc.operationState = fields.LcmOperationStateType.ROLLED_BACK
|
||||
with context.session.begin(subtransactions=True):
|
||||
lcmocc.update(context)
|
||||
# NOTE: Basically inst is not changed. But there is a case
|
||||
# that VIM resources may be changed while rollback. Only
|
||||
# change_ext_conn_rollback at the moment.
|
||||
if lcmocc.operation == fields.LcmOperationType.CHANGE_EXT_CONN:
|
||||
# change_ext_conn_rollback and change_vnfpkg at the moment.
|
||||
if lcmocc.operation in [
|
||||
fields.LcmOperationType.CHANGE_EXT_CONN,
|
||||
fields.LcmOperationType.CHANGE_VNFPKG]:
|
||||
inst.update(context)
|
||||
# grant_req and grant are not necessary any more.
|
||||
if grant_req is not None:
|
||||
|
|
|
@ -64,10 +64,13 @@ class VnfLcmDriverV2(object):
|
|||
grant_req = objects.GrantRequestV1(
|
||||
vnfInstanceId=inst.id,
|
||||
vnfLcmOpOccId=lcmocc.id,
|
||||
vnfdId=inst.vnfdId,
|
||||
operation=lcmocc.operation,
|
||||
isAutomaticInvocation=lcmocc.isAutomaticInvocation
|
||||
)
|
||||
if lcmocc.operation == v2fields.LcmOperationType.CHANGE_VNFPKG:
|
||||
grant_req.vnfdId = lcmocc.operationParams.get('vnfdId')
|
||||
else:
|
||||
grant_req.vnfdId = inst.vnfdId
|
||||
grant_req._links = objects.GrantRequestV1_Links(
|
||||
vnfLcmOpOcc=objects.Link(
|
||||
href=lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)),
|
||||
|
@ -953,6 +956,78 @@ class VnfLcmDriverV2(object):
|
|||
# only support openstack at the moment
|
||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||
|
||||
def change_vnfpkg_grant(self, grant_req, req, inst, vnfd):
|
||||
inst_info = inst.instantiatedVnfInfo
|
||||
grant_req.flavourId = inst_info.flavourId
|
||||
target_vdu_ids = [
|
||||
vdu_param.get('vdu_id')
|
||||
for vdu_param in req.additionalParams.get('vdu_params', [])
|
||||
]
|
||||
|
||||
if req.additionalParams.get('upgrade_type') == 'RollingUpdate':
|
||||
update_reses = []
|
||||
add_reses = []
|
||||
remove_reses = []
|
||||
if inst_info.obj_attr_is_set('vnfcResourceInfo'):
|
||||
for inst_vnc in inst_info.vnfcResourceInfo:
|
||||
if inst_vnc.vduId in target_vdu_ids:
|
||||
vdu_res_id = uuidutils.generate_uuid()
|
||||
res_def = objects.ResourceDefinitionV1(
|
||||
id=vdu_res_id,
|
||||
type='COMPUTE',
|
||||
resourceTemplateId=inst_vnc.vduId)
|
||||
update_reses.append(res_def)
|
||||
nodes = vnfd.get_vdu_nodes(inst_info.flavourId)
|
||||
vdu_storage_names = vnfd.get_vdu_storages(
|
||||
nodes[inst_vnc.vduId])
|
||||
for vdu_storage_name in vdu_storage_names:
|
||||
res_def = objects.ResourceDefinitionV1(
|
||||
id=_make_combination_id(
|
||||
vdu_storage_name, vdu_res_id),
|
||||
type='STORAGE',
|
||||
resourceTemplateId=vdu_storage_name)
|
||||
add_reses.append(res_def)
|
||||
if inst_vnc.obj_attr_is_set('storageResourceIds'):
|
||||
inst_stor_info = (
|
||||
inst_info.virtualStorageResourceInfo)
|
||||
for str_info in inst_stor_info:
|
||||
if str_info.id in inst_vnc.storageResourceIds:
|
||||
res_def = objects.ResourceDefinitionV1(
|
||||
id=uuidutils.generate_uuid(),
|
||||
type='STORAGE',
|
||||
resourceTemplateId=(
|
||||
str_info.virtualStorageDescId),
|
||||
resource=str_info.storageResource)
|
||||
remove_reses.append(res_def)
|
||||
if update_reses:
|
||||
grant_req.updateResources = update_reses
|
||||
|
||||
if add_reses:
|
||||
grant_req.addResources = add_reses
|
||||
|
||||
if remove_reses:
|
||||
grant_req.removeResources = remove_reses
|
||||
else:
|
||||
# TODO(YiFeng): Blue-Green type will be supported in Zed release.
|
||||
# not reach here at the moment
|
||||
pass
|
||||
|
||||
def change_vnfpkg_process(
|
||||
self, context, lcmocc, inst, grant_req, grant, vnfd):
|
||||
inst_saved = inst.obj_clone()
|
||||
req = lcmocc.operationParams
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||
driver = openstack.Openstack()
|
||||
try:
|
||||
driver.change_vnfpkg(req, inst, grant_req, grant, vnfd)
|
||||
except Exception as ex:
|
||||
lcmocc_utils.update_lcmocc(lcmocc, inst_saved, inst)
|
||||
raise Exception from ex
|
||||
else:
|
||||
# only support openstack at the moment
|
||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||
|
||||
def change_ext_conn_rollback(self, context, lcmocc, inst, grant_req,
|
||||
grant, vnfd):
|
||||
req = lcmocc.operationParams
|
||||
|
@ -963,3 +1038,15 @@ class VnfLcmDriverV2(object):
|
|||
else:
|
||||
# only support openstack at the moment
|
||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||
|
||||
def change_vnfpkg_rollback(
|
||||
self, context, lcmocc, inst, grant_req, grant, vnfd):
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
req = lcmocc.operationParams
|
||||
driver = openstack.Openstack()
|
||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||
driver.change_vnfpkg_rollback(
|
||||
req, inst, grant_req, grant, vnfd, lcmocc)
|
||||
else:
|
||||
# only support openstack at the moment
|
||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||
|
|
|
@ -379,6 +379,61 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||
|
||||
return sol_wsgi.SolResponse(202, None, location=location)
|
||||
|
||||
@validator.schema(schema.ChangeCurrentVnfPkgRequest_V200, '2.0.0')
|
||||
@coordinate.lock_vnf_instance('{id}')
|
||||
def change_vnfpkg(self, request, id, body):
|
||||
context = request.context
|
||||
inst = inst_utils.get_inst(context, id)
|
||||
vnfd_id = body['vnfdId']
|
||||
|
||||
if inst.instantiationState != 'INSTANTIATED':
|
||||
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=id)
|
||||
|
||||
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
||||
|
||||
pkg_info = self.nfvo_client.get_vnf_package_info_vnfd(
|
||||
context, vnfd_id)
|
||||
if pkg_info.operationalState != "ENABLED":
|
||||
raise sol_ex.VnfdIdNotEnabled(vnfd_id=vnfd_id)
|
||||
|
||||
additional_params = body.get('additionalParams')
|
||||
if additional_params is None:
|
||||
raise sol_ex.SolValidationError(
|
||||
detail="Change Current VNF Package "
|
||||
"operation must have 'additionalParams'")
|
||||
upgrade_type = additional_params.get('upgrade_type', '')
|
||||
if upgrade_type != 'RollingUpdate':
|
||||
raise sol_ex.NotSupportUpgradeType(upgrade_type=upgrade_type)
|
||||
if additional_params.get('vdu_params'):
|
||||
vdu_ids = [vdu.get('vdu_id') for vdu in
|
||||
additional_params.get('vdu_params')]
|
||||
if None in vdu_ids:
|
||||
raise sol_ex.SolValidationError(
|
||||
detail="If you set vdu_params in additionalParams, you"
|
||||
"must set vduId for each element.")
|
||||
for vdu in additional_params.get('vdu_params'):
|
||||
for attr in ['old_vnfc_param', 'new_vnfc_param']:
|
||||
if vdu.get(attr):
|
||||
vdu_keys = vdu.get(attr).keys()
|
||||
if set(vdu_keys).difference(set(
|
||||
['cp_name', 'username', 'password'])):
|
||||
raise sol_ex.SolValidationError(
|
||||
detail=f"If you set {attr} in "
|
||||
f"additionalParams, you must set"
|
||||
f" 'cp_name', 'username' and"
|
||||
f" 'password'")
|
||||
|
||||
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.CHANGE_VNFPKG,
|
||||
body)
|
||||
|
||||
lcmocc.create(context)
|
||||
|
||||
self.conductor_rpc.start_lcm_op(context, lcmocc.id)
|
||||
|
||||
location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)
|
||||
|
||||
return sol_wsgi.SolResponse(202, None, location=location)
|
||||
|
||||
@validator.schema(schema.LccnSubscriptionRequest_V200, '2.0.0')
|
||||
def subscription_create(self, request, body):
|
||||
context = request.context
|
||||
|
|
|
@ -49,7 +49,7 @@ class HeatClient(object):
|
|||
self.wait_stack_create(fields["stack_name"])
|
||||
|
||||
def update_stack(self, stack_name, fields, wait=True):
|
||||
path = "stacks/{}".format(stack_name)
|
||||
path = f"stacks/{stack_name}"
|
||||
resp, body = self.client.do_request(path, "PATCH",
|
||||
expected_status=[202], body=fields)
|
||||
|
||||
|
@ -57,7 +57,7 @@ class HeatClient(object):
|
|||
self.wait_stack_update(stack_name)
|
||||
|
||||
def delete_stack(self, stack_name, wait=True):
|
||||
path = "stacks/{}".format(stack_name)
|
||||
path = f"stacks/{stack_name}"
|
||||
resp, body = self.client.do_request(path, "DELETE",
|
||||
expected_status=[204, 404])
|
||||
|
||||
|
@ -65,7 +65,7 @@ class HeatClient(object):
|
|||
self.wait_stack_delete(stack_name)
|
||||
|
||||
def get_status(self, stack_name):
|
||||
path = "stacks/{}".format(stack_name)
|
||||
path = f"stacks/{stack_name}"
|
||||
resp, body = self.client.do_request(path, "GET",
|
||||
expected_status=[200, 404])
|
||||
|
||||
|
@ -78,7 +78,7 @@ class HeatClient(object):
|
|||
def get_resources(self, stack_name):
|
||||
# NOTE: Because it is necessary to get nested stack info, it is
|
||||
# necessary to specify 'nested_depth=2'.
|
||||
path = "stacks/{}/resources?nested_depth=2".format(stack_name)
|
||||
path = f"stacks/{stack_name}/resources?nested_depth=2"
|
||||
resp, body = self.client.do_request(path, "GET",
|
||||
expected_status=[200])
|
||||
|
||||
|
@ -134,23 +134,29 @@ class HeatClient(object):
|
|||
raise sol_ex.StackOperationFailed
|
||||
return body
|
||||
|
||||
def get_resource_info(self, stack_name, stack_id, resource_name):
|
||||
path = f"stacks/{stack_name}/{stack_id}/resources/{resource_name}"
|
||||
def get_resource_info(self, nested_stack_id, resource_name):
|
||||
path = f"stacks/{nested_stack_id}/resources/{resource_name}"
|
||||
resp, body = self.client.do_request(path, "GET",
|
||||
expected_status=[200, 404])
|
||||
if resp.status_code == 404:
|
||||
return resp, None
|
||||
return resp, body['resource']
|
||||
return None
|
||||
return body['resource']
|
||||
|
||||
def get_resource_list(self, stack_id):
|
||||
path = f"stacks/{stack_id}/resources"
|
||||
resp, body = self.client.do_request(path, "GET",
|
||||
expected_status=[200, 404])
|
||||
return body
|
||||
|
||||
def get_parameters(self, stack_name):
|
||||
path = "stacks/{}".format(stack_name)
|
||||
path = f"stacks/{stack_name}"
|
||||
resp, body = self.client.do_request(path, "GET",
|
||||
expected_status=[200])
|
||||
|
||||
return body["stack"]["parameters"]
|
||||
|
||||
def mark_unhealthy(self, stack_id, resource_name):
|
||||
path = "stacks/{}/resources/{}".format(stack_id, resource_name)
|
||||
path = f"stacks/{stack_id}/resources/{resource_name}"
|
||||
fields = {
|
||||
"mark_unhealthy": True,
|
||||
"resource_status_reason": "marked by tacker"
|
||||
|
@ -159,14 +165,14 @@ class HeatClient(object):
|
|||
expected_status=[200], body=fields)
|
||||
|
||||
def get_template(self, stack_name):
|
||||
path = "stacks/{}/template".format(stack_name)
|
||||
path = f"stacks/{stack_name}/template"
|
||||
resp, body = self.client.do_request(path, "GET",
|
||||
expected_status=[200])
|
||||
|
||||
return body
|
||||
|
||||
def get_files(self, stack_name):
|
||||
path = "stacks/{}/files".format(stack_name)
|
||||
path = f"stacks/{stack_name}/files"
|
||||
resp, body = self.client.do_request(path, "GET",
|
||||
expected_status=[200])
|
||||
|
||||
|
@ -205,9 +211,34 @@ def get_resource_stack_id(heat_res):
|
|||
return "{}/{}".format(items[-2], items[-1])
|
||||
|
||||
|
||||
def get_parent_nested_id(res):
|
||||
for link in res.get('links', []):
|
||||
if link['rel'] == 'nested':
|
||||
items = link['href'].split('/')
|
||||
return "{}/{}".format(items[-2], items[-1])
|
||||
|
||||
|
||||
def get_parent_resource(heat_res, heat_reses):
|
||||
parent = heat_res.get('parent_resource')
|
||||
if parent:
|
||||
for res in heat_reses:
|
||||
if res['resource_name'] == parent:
|
||||
return res
|
||||
|
||||
|
||||
def get_group_stack_id(heat_reses, vdu_id):
|
||||
parent_resources = [heat_res for heat_res in heat_reses
|
||||
if heat_res.get('resource_name') == vdu_id]
|
||||
if parent_resources:
|
||||
parent_resource = parent_resources[0].get('parent_resource')
|
||||
else:
|
||||
raise sol_ex.VduIdNotFound(vdu_id=vdu_id)
|
||||
group_resource_name = [heat_res for heat_res in
|
||||
heat_reses if
|
||||
heat_res.get('resource_name') ==
|
||||
parent_resource][0].get('parent_resource')
|
||||
group_stack_id = [heat_res for heat_res in
|
||||
heat_reses if
|
||||
heat_res.get('resource_name') ==
|
||||
group_resource_name][0].get('physical_resource_id')
|
||||
return group_stack_id
|
||||
|
|
|
@ -14,21 +14,25 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
from dateutil import parser
|
||||
import eventlet
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
import subprocess
|
||||
|
||||
from dateutil import parser
|
||||
import eventlet
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import yaml
|
||||
|
||||
from tacker.sol_refactored.common import cinder_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.infra_drivers.openstack import heat_utils
|
||||
from tacker.sol_refactored.infra_drivers.openstack import userdata_default
|
||||
from tacker.sol_refactored.nfvo import glance_utils
|
||||
from tacker.sol_refactored import objects
|
||||
from tacker.sol_refactored.objects.v2 import fields as v2fields
|
||||
|
||||
|
@ -292,6 +296,261 @@ class Openstack(object):
|
|||
self._make_instantiated_vnf_info(req, inst, grant_req, grant, vnfd,
|
||||
heat_reses)
|
||||
|
||||
def change_vnfpkg(self, req, inst, grant_req, grant, vnfd):
|
||||
group_vdu_ids = []
|
||||
if req.additionalParams.get('upgrade_type') == 'RollingUpdate':
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
stack_id = heat_client.get_stack_resource(
|
||||
stack_name)['stack']["id"]
|
||||
vdu_infos = self._get_vdu_info(vnfd, grant, inst)
|
||||
new_res_ids = {}
|
||||
for vdu_param in req.additionalParams.get('vdu_params'):
|
||||
vdu_id = vdu_param.get('vdu_id')
|
||||
new_res_ids[vdu_id] = []
|
||||
body = heat_client.get_resource_info(
|
||||
f"{stack_name}/{stack_id}", vdu_id)
|
||||
if uuidutils.is_uuid_like(
|
||||
vdu_infos[vdu_id]['image']):
|
||||
vdu_image_id_flag = True
|
||||
else:
|
||||
vdu_image_id_flag = False
|
||||
# The previous processing is to obtain the VM information under
|
||||
# the current stack according to vduId, excluding the resources
|
||||
# under nested. When the status_code is 404, it means that
|
||||
# there is no VM under the stack, and the VM is created by
|
||||
# `OS::Heat::AutoScalingGroup` by default.
|
||||
if body is None:
|
||||
# handle VM under `OS::Heat::AutoScalingGroup`
|
||||
# In this case, the VM does not support changing from
|
||||
# image creation to volume creation, or changing from
|
||||
# volume creation to image creation. Because it cannot
|
||||
# change VDU.yaml under nested directory.
|
||||
heat_reses = heat_client.get_resources(stack_name)
|
||||
group_stack_id = heat_utils.get_group_stack_id(
|
||||
heat_reses, vdu_id)
|
||||
templates = heat_client.get_template(
|
||||
group_stack_id)
|
||||
reses = heat_client.get_resource_list(
|
||||
group_stack_id)['resources']
|
||||
|
||||
# update VM one by one
|
||||
for res in reses:
|
||||
templates['resources'][res.get('resource_name')][
|
||||
'properties']['image'] = vdu_infos[
|
||||
vdu_id].get('image')
|
||||
templates['resources'][res.get('resource_name')][
|
||||
'properties']['flavor'] = vdu_infos[
|
||||
vdu_id].get('flavor')
|
||||
fields = {
|
||||
"stack_id": group_stack_id,
|
||||
"template": templates
|
||||
}
|
||||
try:
|
||||
heat_client.update_stack(
|
||||
group_stack_id, fields)
|
||||
except sol_ex.StackOperationFailed:
|
||||
self._handle_exception(
|
||||
res, new_res_ids, vdu_infos,
|
||||
vdu_id, heat_client, req, inst, vnfd,
|
||||
stack_name, get_res_flag=True)
|
||||
self._get_new_res_info(
|
||||
res.get('resource_name'),
|
||||
new_res_ids[vdu_id],
|
||||
heat_utils.get_parent_nested_id(res),
|
||||
vdu_infos[vdu_id], vdu_id,
|
||||
heat_client)
|
||||
|
||||
# execute coordinate_vnf_script
|
||||
try:
|
||||
self._execute_coordinate_vnf_script(
|
||||
req, inst, grant_req, grant, vnfd,
|
||||
heat_utils.get_parent_nested_id(res),
|
||||
heat_client, vdu_param, vim_info,
|
||||
vdu_image_id_flag)
|
||||
except (sol_ex.SshIpNotFoundException,
|
||||
sol_ex.CoordinateVNFExecutionFailed) as ex:
|
||||
self._handle_exception(
|
||||
res, new_res_ids, vdu_infos,
|
||||
vdu_id, heat_client, req, inst, vnfd,
|
||||
stack_name, ex=ex)
|
||||
group_vdu_ids.append(vdu_id)
|
||||
else:
|
||||
# handle single VM
|
||||
res = {
|
||||
"resource_name": stack_name,
|
||||
"physical_resource_id": stack_id,
|
||||
}
|
||||
new_template = vnfd.get_base_hot(
|
||||
inst.instantiatedVnfInfo.flavourId)['template']
|
||||
heat_parameter = self._update_vnf_template_and_parameter(
|
||||
stack_id, vdu_infos, vdu_id, heat_client, new_template)
|
||||
fields = {
|
||||
"stack_id": stack_id,
|
||||
"parameters": {"nfv": heat_parameter.get('nfv')},
|
||||
"template": yaml.safe_dump(
|
||||
heat_parameter.get('templates')),
|
||||
}
|
||||
try:
|
||||
heat_client.update_stack(stack_id, fields, wait=True)
|
||||
except sol_ex.StackOperationFailed:
|
||||
self._handle_exception(
|
||||
res, new_res_ids, vdu_infos,
|
||||
vdu_id, heat_client, req, inst, vnfd,
|
||||
stack_name, get_res_flag=True)
|
||||
self._get_new_res_info(
|
||||
stack_name,
|
||||
new_res_ids[vdu_id],
|
||||
f'{stack_name}/{stack_id}',
|
||||
vdu_infos[vdu_id], vdu_id,
|
||||
heat_client)
|
||||
|
||||
# execute coordinate_vnf_script
|
||||
try:
|
||||
self._execute_coordinate_vnf_script(
|
||||
req, inst, grant_req, grant, vnfd,
|
||||
f"{stack_name}/{stack_id}",
|
||||
heat_client, vdu_param, vim_info,
|
||||
vdu_image_id_flag,
|
||||
operation='change_vnfpkg')
|
||||
except (sol_ex.SshIpNotFoundException,
|
||||
sol_ex.CoordinateVNFExecutionFailed) as ex:
|
||||
self._handle_exception(
|
||||
res, new_res_ids, vdu_infos,
|
||||
vdu_id, heat_client, req, inst, vnfd,
|
||||
stack_name, ex=ex)
|
||||
|
||||
# Because external parameters are not updated after the image
|
||||
# of the nested-VM is updated, scale will create the original
|
||||
# VM again, so the overall update needs to be performed
|
||||
# at the end.
|
||||
fields = self._get_entire_stack_fields(
|
||||
heat_client, stack_id, group_vdu_ids, vdu_infos)
|
||||
try:
|
||||
heat_client.update_stack(stack_id, fields)
|
||||
except sol_ex.StackOperationFailed:
|
||||
self._handle_exception(
|
||||
res, new_res_ids, vdu_infos,
|
||||
vdu_id, heat_client, req, inst, vnfd,
|
||||
stack_name)
|
||||
self._update_vnf_instantiated_info(
|
||||
req, new_res_ids, inst, vnfd,
|
||||
heat_client, stack_name)
|
||||
inst.vnfdId = req.vnfdId
|
||||
else:
|
||||
# TODO(YiFeng): Blue-Green type will be supported in Zed release.
|
||||
raise sol_ex.NotSupportUpgradeType(
|
||||
upgrade_type=req.additionalParams.get('upgrade_type'))
|
||||
|
||||
def change_vnfpkg_rollback(
|
||||
self, req, inst, grant_req, grant, vnfd, lcmocc):
|
||||
group_vdu_ids = []
|
||||
if req.additionalParams.get('upgrade_type') == 'RollingUpdate':
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
stack_id = heat_client.get_stack_resource(
|
||||
stack_name)['stack']["id"]
|
||||
vdu_infos = self._get_vdu_info(vnfd, grant, inst)
|
||||
new_res_ids = {}
|
||||
templates = {}
|
||||
affected_vnfcs = [affected_vnfc for affected_vnfc in
|
||||
lcmocc.resourceChanges.affectedVnfcs if
|
||||
affected_vnfc.changeType in {'ADDED', 'MODIFIED'}]
|
||||
for lcmocc_vnf in affected_vnfcs:
|
||||
if new_res_ids.get(lcmocc_vnf.vduId) is None:
|
||||
new_res_ids[lcmocc_vnf.vduId] = []
|
||||
image = vdu_infos[lcmocc_vnf.vduId]['image']
|
||||
if uuidutils.is_uuid_like(image):
|
||||
vdu_image_id_flag = True
|
||||
else:
|
||||
vdu_image_id_flag = False
|
||||
if lcmocc_vnf.metadata.get(
|
||||
'current_vnfd_id') is not inst.vnfdId:
|
||||
parent_resource_name = lcmocc_vnf.metadata.get(
|
||||
'parent_resource_name')
|
||||
if parent_resource_name:
|
||||
vdu_stack_id = lcmocc_vnf.metadata.get(
|
||||
'stack_id')
|
||||
if not templates.get(lcmocc_vnf.vduId):
|
||||
templates[lcmocc_vnf.vduId] = {}
|
||||
heat_reses = heat_client.get_resources(stack_name)
|
||||
group_stack_id = heat_utils.get_group_stack_id(
|
||||
heat_reses, lcmocc_vnf.vduId)
|
||||
template = heat_client.get_template(
|
||||
group_stack_id)
|
||||
templates[lcmocc_vnf.vduId] = template
|
||||
template['resources'][parent_resource_name][
|
||||
'properties']['image'] = vdu_infos[
|
||||
lcmocc_vnf.vduId].get('image')
|
||||
template['resources'][parent_resource_name][
|
||||
'properties']['flavor'] = vdu_infos[
|
||||
lcmocc_vnf.vduId].get('flavor')
|
||||
fields = {
|
||||
"stack_id": group_stack_id,
|
||||
"template": template
|
||||
}
|
||||
heat_client.update_stack(
|
||||
group_stack_id, fields, wait=True)
|
||||
self._get_new_res_info(
|
||||
parent_resource_name,
|
||||
new_res_ids[lcmocc_vnf.vduId], vdu_stack_id,
|
||||
vdu_infos[lcmocc_vnf.vduId],
|
||||
lcmocc_vnf.vduId, heat_client)
|
||||
vdu_param = [vdu_param for vdu_param in
|
||||
req.get('additionalParams').get(
|
||||
'vdu_params')
|
||||
if vdu_param.get('vdu_id')
|
||||
== lcmocc_vnf.vduId][0]
|
||||
self._execute_coordinate_vnf_script(
|
||||
req, inst, grant_req, grant, vnfd,
|
||||
vdu_stack_id,
|
||||
heat_client, vdu_param, vim_info,
|
||||
vdu_image_id_flag,
|
||||
operation="change_vnfpkg_rollback")
|
||||
group_vdu_ids.append(lcmocc_vnf.vduId)
|
||||
else:
|
||||
vdu_stack_id = lcmocc_vnf.metadata.get('stack_id')
|
||||
new_template = vnfd.get_base_hot(
|
||||
inst.instantiatedVnfInfo.flavourId)['template']
|
||||
heat_parameter = (
|
||||
self._update_vnf_template_and_parameter(
|
||||
stack_id, vdu_infos,
|
||||
lcmocc_vnf.vduId, heat_client, new_template))
|
||||
fields = {
|
||||
"stack_id": stack_id,
|
||||
"parameters": {"nfv": heat_parameter.get('nfv')},
|
||||
"template": yaml.safe_dump(
|
||||
heat_parameter.get('templates')),
|
||||
}
|
||||
heat_client.update_stack(stack_id,
|
||||
fields, wait=True)
|
||||
self._get_new_res_info(
|
||||
stack_name, new_res_ids[lcmocc_vnf.vduId],
|
||||
vdu_stack_id, vdu_infos[lcmocc_vnf.vduId],
|
||||
lcmocc_vnf.vduId, heat_client)
|
||||
vdu_param = [vdu_param for vdu_param in
|
||||
req.get('additionalParams').get(
|
||||
'vdu_params')
|
||||
if vdu_param.get('vdu_id')
|
||||
== lcmocc_vnf.vduId][0]
|
||||
self._execute_coordinate_vnf_script(
|
||||
req, inst, grant_req, grant, vnfd, vdu_stack_id,
|
||||
heat_client, vdu_param, vim_info,
|
||||
vdu_image_id_flag,
|
||||
operation="change_vnfpkg_rollback")
|
||||
fields = self._get_entire_stack_fields(
|
||||
heat_client, stack_id, group_vdu_ids, vdu_infos)
|
||||
heat_client.update_stack(stack_id, fields, wait=True)
|
||||
self._update_vnf_instantiated_info(
|
||||
req, new_res_ids, inst, vnfd,
|
||||
heat_client, stack_name, operation='change_vnfpkg_rollback')
|
||||
else:
|
||||
# TODO(YiFeng): Blue-Green type will be supported in Zed release.
|
||||
raise sol_ex.NotSupportUpgradeType(
|
||||
upgrade_type=req.additionalParams.get('upgrade_type'))
|
||||
|
||||
def _make_hot(self, req, inst, grant_req, grant, vnfd):
|
||||
if grant_req.operation == v2fields.LcmOperationType.INSTANTIATE:
|
||||
flavour_id = req.flavourId
|
||||
|
@ -372,6 +631,60 @@ class Openstack(object):
|
|||
obj.maxAddress = range_data.maxAddress
|
||||
return obj
|
||||
|
||||
def _execute_coordinate_vnf_script(
|
||||
self, req, inst, grant_req, grant,
|
||||
vnfd, nested_stack_id, heat_client, vdu_param,
|
||||
vim_info, vdu_image_id_flag, operation='change_vnfpkg'):
|
||||
coordinate_vnf = None
|
||||
coordinate_vnf_class = None
|
||||
if req.obj_attr_is_set('additionalParams'):
|
||||
if operation == 'change_vnfpkg':
|
||||
coordinate_vnf = req.additionalParams.get(
|
||||
'lcm-operation-coordinate-new-vnf')
|
||||
coordinate_vnf_class = req.additionalParams.get(
|
||||
'lcm-operation-coordinate-new-vnf-class')
|
||||
else:
|
||||
coordinate_vnf = req.additionalParams.get(
|
||||
'lcm-operation-coordinate-old-vnf')
|
||||
coordinate_vnf_class = req.additionalParams.get(
|
||||
'lcm-operation-coordinate-old-vnf-class')
|
||||
|
||||
if coordinate_vnf and coordinate_vnf_class:
|
||||
if operation == 'change_vnfpkg':
|
||||
ssh_ip = self._get_ssh_ip(nested_stack_id,
|
||||
vdu_param.get('new_vnfc_param'),
|
||||
heat_client)
|
||||
else:
|
||||
ssh_ip = self._get_ssh_ip(nested_stack_id,
|
||||
vdu_param.get('old_vnfc_param'),
|
||||
heat_client)
|
||||
if not ssh_ip:
|
||||
raise sol_ex.SshIpNotFoundException
|
||||
image, flavor = self._get_current_vdu_image_and_flavor(
|
||||
nested_stack_id, vdu_param.get('vdu_id'),
|
||||
heat_client, vim_info, vdu_image_id_flag)
|
||||
tmp_csar_dir = vnfd.make_tmp_csar_dir()
|
||||
script_dict = {
|
||||
"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,
|
||||
"vdu_info": {
|
||||
"ssh_ip": ssh_ip,
|
||||
"new_image": image,
|
||||
"new_flavor": flavor,
|
||||
"vdu_param": vdu_param
|
||||
}
|
||||
}
|
||||
script_path = os.path.join(tmp_csar_dir, coordinate_vnf)
|
||||
out = subprocess.run(["python3", script_path],
|
||||
input=pickle.dumps(script_dict),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if out.returncode != 0:
|
||||
LOG.error(out)
|
||||
raise sol_ex.CoordinateVNFExecutionFailed
|
||||
|
||||
def _proto_data_to_info(self, proto_data):
|
||||
# make CpProtocolInfo (5.5.3.9b) from CpProtocolData (4.4.1.10b)
|
||||
proto_info = objects.CpProtocolInfoV2(
|
||||
|
@ -463,6 +776,285 @@ class Openstack(object):
|
|||
|
||||
return ext_vl
|
||||
|
||||
def _get_vdu_info(self, vnfd, grant, inst):
|
||||
flavour_id = inst.instantiatedVnfInfo.flavourId
|
||||
vdu_nodes = vnfd.get_vdu_nodes(flavour_id)
|
||||
storage_nodes = vnfd.get_storage_nodes(flavour_id)
|
||||
vdu_info_dict = {}
|
||||
for name, node in vdu_nodes.items():
|
||||
flavor = self._get_param_flavor(vnfd, name, flavour_id, grant)
|
||||
image = self._get_param_image(vnfd, name, flavour_id, grant)
|
||||
vdu_storage_names = vnfd.get_vdu_storages(node)
|
||||
volume_name = ''
|
||||
volume_size = ''
|
||||
for vdu_storage_name in vdu_storage_names:
|
||||
if storage_nodes[vdu_storage_name].get(
|
||||
'properties').get('sw_image_data'):
|
||||
image = self._get_param_image(
|
||||
vnfd, vdu_storage_name, flavour_id, grant)
|
||||
volume_name = vdu_storage_name
|
||||
volume_size = storage_nodes[vdu_storage_name].get(
|
||||
'properties', {}).get(
|
||||
'virtual_block_storage_data', '').get(
|
||||
'size_of_storage', ''
|
||||
)
|
||||
volume_size = volume_size.rstrip(' GB')
|
||||
if not volume_size.isdigit():
|
||||
raise sol_ex.InvalidVolumeSize
|
||||
break
|
||||
|
||||
vdu_info_dict[name] = {
|
||||
"flavor": flavor,
|
||||
"image": image
|
||||
}
|
||||
|
||||
if volume_name:
|
||||
vdu_info_dict[name]['volume_info'] = {
|
||||
"volume_name": volume_name,
|
||||
"volume_size": volume_size
|
||||
}
|
||||
return vdu_info_dict
|
||||
|
||||
def _get_param_flavor(self, vnfd, vdu_name, flavour_id, grant):
|
||||
# try to get from grant
|
||||
if grant.obj_attr_is_set('vimAssets'):
|
||||
assets = grant.vimAssets
|
||||
if assets.obj_attr_is_set('computeResourceFlavours'):
|
||||
flavours = assets.computeResourceFlavours
|
||||
for flavour in flavours:
|
||||
if flavour.vnfdVirtualComputeDescId == vdu_name:
|
||||
return flavour.vimFlavourId
|
||||
# if specified in VNFD, use it
|
||||
# NOTE: if not found. parameter is set to None.
|
||||
# may be error when stack create
|
||||
return vnfd.get_compute_flavor(flavour_id, vdu_name)
|
||||
|
||||
def _get_param_image(self, vnfd, vdu_name, flavour_id, grant):
|
||||
# try to get from grant
|
||||
if grant.obj_attr_is_set('vimAssets'):
|
||||
assets = grant.vimAssets
|
||||
if assets.obj_attr_is_set('softwareImages'):
|
||||
images = assets.softwareImages
|
||||
for image in images:
|
||||
if image.vnfdSoftwareImageId == vdu_name:
|
||||
return image.vimSoftwareImageId
|
||||
|
||||
# if specified in VNFD, use it
|
||||
# NOTE: if not found. parameter is set to None.
|
||||
# may be error when stack create
|
||||
sw_images = vnfd.get_sw_image(flavour_id)
|
||||
for name, image in sw_images.items():
|
||||
if name == vdu_name:
|
||||
return image
|
||||
|
||||
return None
|
||||
|
||||
def _get_current_vdu_image_and_flavor(
|
||||
self, nested_stack_id, resource_name,
|
||||
heat_client, vim_info, vdu_image_id_flag):
|
||||
vdu_info = heat_client.get_resource_info(
|
||||
nested_stack_id, resource_name)
|
||||
if vdu_info.get('attributes').get('image'):
|
||||
image = vdu_info.get('attributes').get('image').get('id')
|
||||
if not vdu_image_id_flag:
|
||||
glance_client = glance_utils.GlanceClient(vim_info)
|
||||
image = glance_client.get_image(image).name
|
||||
|
||||
else:
|
||||
volume_ids = [volume.get('id') for volume in vdu_info.get(
|
||||
'attributes').get('os-extended-volumes:volumes_attached')]
|
||||
cinder_client = cinder_utils.CinderClient(vim_info)
|
||||
if vdu_image_id_flag:
|
||||
image = [cinder_client.get_volume(
|
||||
volume_id).volume_image_metadata.get('image_id')
|
||||
for volume_id in volume_ids if cinder_client.get_volume(
|
||||
volume_id).volume_image_metadata][0]
|
||||
else:
|
||||
image = [cinder_client.get_volume(
|
||||
volume_id).volume_image_metadata.get('image_name')
|
||||
for volume_id in volume_ids if cinder_client.get_volume(
|
||||
volume_id).volume_image_metadata][0]
|
||||
flavor_name = vdu_info.get('attributes').get('flavor').get(
|
||||
'original_name')
|
||||
|
||||
return image, flavor_name
|
||||
|
||||
def _get_new_res_info(self, parent_resource_name, vdu_infos,
|
||||
stack_id, vnfd_info, vdu_id, heat_client):
|
||||
new_res_infos = {
|
||||
"stack_id": stack_id,
|
||||
"parent_resource_name": parent_resource_name
|
||||
}
|
||||
nested_reses = heat_client.get_resource_list(
|
||||
stack_id.split('/')[1])['resources']
|
||||
for nested_res in nested_reses:
|
||||
if nested_res.get(
|
||||
'resource_type') == 'OS::Nova::Server' and nested_res.get(
|
||||
'resource_name') == vdu_id:
|
||||
new_res_infos["vdu_id"] = nested_res.get(
|
||||
'physical_resource_id')
|
||||
elif nested_res.get(
|
||||
'resource_type'
|
||||
) == 'OS::Cinder::Volume' and nested_res.get(
|
||||
'resource_name') == vnfd_info.get(
|
||||
'volume_info').get('volume_name'):
|
||||
new_res_infos['volume_info'] = {
|
||||
"volume_name": nested_res.get('resource_name'),
|
||||
"volume_id": nested_res.get('physical_resource_id')
|
||||
}
|
||||
vdu_infos.append(new_res_infos)
|
||||
|
||||
def _get_ssh_ip(self, nested_stack_id, vnfc_param, heat_client):
|
||||
cp_name = vnfc_param.get('cp_name')
|
||||
cp_info = heat_client.get_resource_info(nested_stack_id, cp_name)
|
||||
if cp_info.get('attributes').get('floating_ip_address'):
|
||||
ssh_ip = cp_info.get('attributes').get('floating_ip_address')
|
||||
else:
|
||||
ssh_ip = cp_info.get('attributes').get('fixed_ips')[0].get(
|
||||
'ip_address')
|
||||
return ssh_ip
|
||||
|
||||
def _update_vnf_instantiated_info(
|
||||
self, req, new_res_ids, inst, vnfd, heat_client, stack_name,
|
||||
operation='change_vnfpkg'):
|
||||
instantiated_vnf_info = inst.instantiatedVnfInfo
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_reses = heat_client.get_resources(stack_name)
|
||||
storage_reses = self._get_checked_reses(
|
||||
vnfd.get_storage_nodes(inst.instantiatedVnfInfo.flavourId),
|
||||
heat_utils.get_storage_reses(heat_reses))
|
||||
|
||||
# handle storage_info
|
||||
def _res_to_handle(res):
|
||||
return objects.ResourceHandle(
|
||||
resourceId=res['physical_resource_id'],
|
||||
vimLevelResourceType=res['resource_type'],
|
||||
vimConnectionId=vim_info.vimId)
|
||||
|
||||
storage_infos = [
|
||||
objects.VirtualStorageResourceInfoV2(
|
||||
id=res_id,
|
||||
virtualStorageDescId=res['resource_name'],
|
||||
storageResource=_res_to_handle(res)
|
||||
)
|
||||
for res_id, res in storage_reses.items()
|
||||
]
|
||||
|
||||
# handle vnfc_resource_info
|
||||
for vnfc_res in instantiated_vnf_info.vnfcResourceInfo:
|
||||
if new_res_ids.get(vnfc_res.vduId):
|
||||
new_res_info = [vnfc_info for vnfc_info
|
||||
in new_res_ids.get(vnfc_res.vduId)
|
||||
if vnfc_info['stack_id']
|
||||
== vnfc_res.metadata['stack_id']]
|
||||
if not new_res_info:
|
||||
continue
|
||||
new_res_info = new_res_info[0]
|
||||
current_vnfc = []
|
||||
if instantiated_vnf_info.obj_attr_is_set('vnfcInfo'):
|
||||
current_vnfc = [
|
||||
vnfc for vnfc in instantiated_vnf_info.vnfcInfo
|
||||
if vnfc.id == _make_combination_id(
|
||||
vnfc_res.vduId, vnfc_res.id)][0]
|
||||
vnfc_res.id = new_res_info.get('vdu_id')
|
||||
vnfc_res.computeResource.resourceId = new_res_info.get(
|
||||
'vdu_id')
|
||||
if current_vnfc:
|
||||
current_vnfc.id = _make_combination_id(
|
||||
vnfc_res.vduId, vnfc_res.id)
|
||||
current_vnfc.vnfcResourceInfoId = vnfc_res.id
|
||||
if operation == 'change_vnfpkg':
|
||||
vnfc_res.metadata['current_vnfd_id'] = req.vnfdId
|
||||
else:
|
||||
vnfc_res.metadata['current_vnfd_id'] = inst.vnfdId
|
||||
storage_ids = [
|
||||
storage_id for storage_id, storage_res in
|
||||
storage_reses.items()
|
||||
if (vnfc_res.vduId in storage_res.get(
|
||||
'required_by', []))]
|
||||
if vnfc_res.metadata.get('parent_resource_name') != stack_name:
|
||||
storage_ids = [
|
||||
storage_id for storage_id, storage_res in
|
||||
storage_reses.items()
|
||||
if (vnfc_res.vduId in storage_res.get(
|
||||
'required_by', []) and vnfc_res.metadata.get(
|
||||
'parent_resource_name') == storage_res.get(
|
||||
'parent_resource'))]
|
||||
if storage_ids:
|
||||
vnfc_res.storageResourceIds = storage_ids
|
||||
else:
|
||||
if vnfc_res.obj_attr_is_set('storageResourceIds'):
|
||||
del vnfc_res.storageResourceIds
|
||||
|
||||
if storage_infos:
|
||||
instantiated_vnf_info.virtualStorageResourceInfo = storage_infos
|
||||
else:
|
||||
if instantiated_vnf_info.obj_attr_is_set(
|
||||
'virtualStorageResourceInfo'):
|
||||
del instantiated_vnf_info.virtualStorageResourceInfo
|
||||
|
||||
def _update_vnf_template_and_parameter(
|
||||
self, stack_id, vdu_infos, vdu_id, heat_client, new_template):
|
||||
vdu_info = vdu_infos[vdu_id]
|
||||
volume_info = vdu_info.get('volume_info', {})
|
||||
base_templates = heat_client.get_template(stack_id)
|
||||
old_parameter = heat_client.get_parameters(stack_id)['nfv']
|
||||
new_parameter = json.loads(copy.deepcopy(old_parameter))
|
||||
|
||||
# old VM(created by volume) -> new VM(created by volume)
|
||||
if volume_info and 'image' not in base_templates[
|
||||
'resources'][vdu_id]['properties']:
|
||||
new_parameter['VDU'][vdu_id]['computeFlavourId'] = vdu_info.get(
|
||||
'flavor')
|
||||
new_parameter['VDU'][volume_info.get('volume_name')][
|
||||
'vcImageId'] = vdu_info.get('image')
|
||||
|
||||
# old VM(created by volume) -> new VM(created by image)
|
||||
elif vdu_info.get(
|
||||
'volume_info') is None and 'image' not in base_templates[
|
||||
'resources'][vdu_id]['properties']:
|
||||
# delete vdu's volume definition info
|
||||
if len(base_templates['resources'][vdu_id]['properties'][
|
||||
'block_device_mapping_v2']) > 1:
|
||||
old_volumes = [name for name, value in
|
||||
base_templates['resources'].items()
|
||||
if value['type'] == 'OS::Cinder::Volume'
|
||||
and value['properties']['image']]
|
||||
for volume in base_templates['resources'][
|
||||
vdu_id]['properties']['block_device_mapping_v2']:
|
||||
if volume['volume_id']['get_resource'] in old_volumes:
|
||||
target_volume_name = volume['volume_id'][
|
||||
'get_resource']
|
||||
else:
|
||||
target_volume_name = base_templates['resources'][
|
||||
vdu_id]['properties']['block_device_mapping_v2'][0][
|
||||
'volume_id']['get_resource']
|
||||
del new_parameter['VDU'][target_volume_name]
|
||||
new_parameter['VDU'][vdu_id]['computeFlavourId'] = vdu_info.get(
|
||||
'flavor')
|
||||
new_parameter['VDU'][vdu_id]['vcImageId'] = vdu_info.get('image')
|
||||
|
||||
# old VM(created by image) -> new VM(created by volume)
|
||||
elif volume_info and 'image' in base_templates[
|
||||
'resources'][vdu_id]['properties']:
|
||||
del new_parameter['VDU'][vdu_id]['vcImageId']
|
||||
new_parameter['VDU'][vdu_id]['computeFlavourId'] = vdu_info.get(
|
||||
'flavor')
|
||||
new_parameter['VDU'][volume_info.get('volume_name')] = {
|
||||
"vcImageId": vdu_infos[vdu_id].get('image')
|
||||
}
|
||||
# old VM(created by image) -> new VM(created by image)
|
||||
else:
|
||||
new_parameter['VDU'][vdu_id]['computeFlavourId'] = vdu_info.get(
|
||||
'flavor')
|
||||
new_parameter['VDU'][vdu_id]['vcImageId'] = vdu_info.get('image')
|
||||
|
||||
heat_parameter = {
|
||||
"templates": new_template,
|
||||
"nfv": new_parameter
|
||||
}
|
||||
return heat_parameter
|
||||
|
||||
def _make_ext_vl_info_from_req(self, req, grant, ext_cp_infos):
|
||||
# make extVirtualLinkInfo
|
||||
req_ext_vls = []
|
||||
|
@ -947,3 +1539,47 @@ class Openstack(object):
|
|||
inst_vnf_info.vnfcInfo = vnfc_infos
|
||||
|
||||
inst.instantiatedVnfInfo = inst_vnf_info
|
||||
|
||||
def _handle_exception(
|
||||
self, res, new_res_ids, vdu_infos,
|
||||
vdu_id, heat_client, req, inst, vnfd,
|
||||
stack_name, ex=None, get_res_flag=False):
|
||||
if get_res_flag:
|
||||
if len(res.keys()) == 2:
|
||||
par_stack_id = '{}/{}'.format(
|
||||
res.get('resource_name'),
|
||||
res.get('physical_resource_id'))
|
||||
else:
|
||||
par_stack_id = heat_utils.get_parent_nested_id(res)
|
||||
self._get_new_res_info(
|
||||
res.get('resource_name'),
|
||||
new_res_ids[vdu_id],
|
||||
par_stack_id,
|
||||
vdu_infos[vdu_id], vdu_id,
|
||||
heat_client)
|
||||
self._update_vnf_instantiated_info(
|
||||
req, new_res_ids, inst,
|
||||
vnfd, heat_client, stack_name)
|
||||
if ex:
|
||||
raise ex
|
||||
raise sol_ex.StackOperationFailed
|
||||
|
||||
def _get_entire_stack_fields(self, heat_client, stack_id,
|
||||
group_vdu_ids, vdu_infos):
|
||||
parameter = json.loads(heat_client.get_parameters(stack_id)['nfv'])
|
||||
for group_vdu_id in group_vdu_ids:
|
||||
if not parameter['VDU'][group_vdu_id].get('vcImageId'):
|
||||
volume_info = vdu_infos[
|
||||
group_vdu_id].get('volume_info')
|
||||
parameter['VDU'][volume_info.get('volume_name')][
|
||||
'vcImageId'] = vdu_infos[group_vdu_id].get(
|
||||
'image')
|
||||
else:
|
||||
parameter['VDU'][group_vdu_id][
|
||||
'vcImageId'] = vdu_infos[group_vdu_id].get(
|
||||
'image')
|
||||
fields = {
|
||||
"stack_id": stack_id,
|
||||
"parameters": {"nfv": parameter}
|
||||
}
|
||||
return fields
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import yaml
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.infra_drivers.openstack import userdata_utils
|
||||
|
||||
|
@ -23,39 +24,39 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
|
||||
@staticmethod
|
||||
def instantiate(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = req['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
if 'computeFlavourId' in vdu_value:
|
||||
vdu_value['computeFlavourId'] = (
|
||||
userdata_utils.get_param_flavor(
|
||||
common_script_utils.get_param_flavor(
|
||||
vdu_name, flavour_id, vnfd, grant))
|
||||
if 'vcImageId' in vdu_value:
|
||||
vdu_value['vcImageId'] = userdata_utils.get_param_image(
|
||||
vdu_value['vcImageId'] = common_script_utils.get_param_image(
|
||||
vdu_name, flavour_id, vnfd, grant)
|
||||
if 'locationConstraints' in vdu_value:
|
||||
vdu_value['locationConstraints'] = (
|
||||
userdata_utils.get_param_zone(
|
||||
common_script_utils.get_param_zone(
|
||||
vdu_name, grant_req, grant))
|
||||
if 'desired_capacity' in vdu_value:
|
||||
vdu_value['desired_capacity'] = (
|
||||
userdata_utils.get_param_capacity(
|
||||
common_script_utils.get_param_capacity(
|
||||
vdu_name, inst, grant_req))
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
for cp_name, cp_value in cps.items():
|
||||
if 'network' in cp_value:
|
||||
cp_value['network'] = userdata_utils.get_param_network(
|
||||
cp_value['network'] = common_script_utils.get_param_network(
|
||||
cp_name, grant, req)
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = userdata_utils.get_param_fixed_ips(
|
||||
ext_fixed_ips = common_script_utils.get_param_fixed_ips(
|
||||
cp_name, grant, req)
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
|
@ -70,7 +71,7 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
fixed_ips.append(ips_i)
|
||||
cp_value['fixed_ips'] = fixed_ips
|
||||
|
||||
userdata_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
common_script_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
|
||||
if 'nfv' in req.get('additionalParams', {}):
|
||||
nfv_dict = inst_utils.json_merge_patch(nfv_dict,
|
||||
|
@ -98,19 +99,19 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
# NOTE: complete 'nfv' dict can not be made at the moment
|
||||
# since InstantiateVnfRequest is necessary to make it.
|
||||
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
new_vdus = {}
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
if 'desired_capacity' in vdu_value:
|
||||
capacity = userdata_utils.get_param_capacity(
|
||||
capacity = common_script_utils.get_param_capacity(
|
||||
vdu_name, inst, grant_req)
|
||||
new_vdus[vdu_name] = {'desired_capacity': capacity}
|
||||
|
||||
|
@ -125,19 +126,19 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
# It is thought that it is suitable that this method defines
|
||||
# here since it is very likely to scale method above.
|
||||
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
new_vdus = {}
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
if 'desired_capacity' in vdu_value:
|
||||
capacity = userdata_utils.get_current_capacity(
|
||||
capacity = common_script_utils.get_current_capacity(
|
||||
vdu_name, inst)
|
||||
new_vdus[vdu_name] = {'desired_capacity': capacity}
|
||||
|
||||
|
@ -155,25 +156,26 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
# NOTE: complete 'nfv' dict can not be made at the moment
|
||||
# since InstantiateVnfRequest is necessary to make it.
|
||||
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
new_cps = {}
|
||||
for cp_name, cp_value in cps.items():
|
||||
if 'network' in cp_value:
|
||||
network = userdata_utils.get_param_network(cp_name, grant, req)
|
||||
network = common_script_utils.get_param_network(
|
||||
cp_name, grant, req)
|
||||
if network is None:
|
||||
continue
|
||||
new_cps.setdefault(cp_name, {})
|
||||
new_cps[cp_name]['network'] = network
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = userdata_utils.get_param_fixed_ips(
|
||||
ext_fixed_ips = common_script_utils.get_param_fixed_ips(
|
||||
cp_name, grant, req)
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
|
@ -200,27 +202,28 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
# It is thought that it is suitable that this method defines
|
||||
# here since it is very likely to scale method above.
|
||||
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
new_cps = {}
|
||||
for cp_name, cp_value in cps.items():
|
||||
if 'network' in cp_value:
|
||||
network = userdata_utils.get_param_network_from_inst(
|
||||
network = common_script_utils.get_param_network_from_inst(
|
||||
cp_name, inst)
|
||||
if network is None:
|
||||
continue
|
||||
new_cps.setdefault(cp_name, {})
|
||||
new_cps[cp_name]['network'] = network
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = userdata_utils.get_param_fixed_ips_from_inst(
|
||||
cp_name, inst)
|
||||
ext_fixed_ips = (
|
||||
common_script_utils.get_param_fixed_ips_from_inst(
|
||||
cp_name, inst))
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
if i not in cp_value['fixed_ips']:
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
import abc
|
||||
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
|
||||
|
||||
class AbstractUserData(metaclass=abc.ABCMeta):
|
||||
|
@ -54,224 +53,3 @@ class AbstractUserData(metaclass=abc.ABCMeta):
|
|||
@abc.abstractmethod
|
||||
def heal(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
raise sol_ex.UserDataClassNotImplemented()
|
||||
|
||||
|
||||
def get_vnfd(vnfd_id, csar_dir):
|
||||
vnfd = vnfd_utils.Vnfd(vnfd_id)
|
||||
vnfd.init_from_csar_dir(csar_dir)
|
||||
return vnfd
|
||||
|
||||
|
||||
def init_nfv_dict(hot_template):
|
||||
get_params = []
|
||||
|
||||
def _get_get_param(prop):
|
||||
if isinstance(prop, dict):
|
||||
for key, value in prop.items():
|
||||
if key == 'get_param':
|
||||
get_params.append(value)
|
||||
else:
|
||||
_get_get_param(value)
|
||||
elif isinstance(prop, list):
|
||||
for value in prop:
|
||||
_get_get_param(value)
|
||||
|
||||
for res in hot_template.get('resources', {}).values():
|
||||
_get_get_param(res.get('properties', {}))
|
||||
|
||||
nfv = {}
|
||||
|
||||
for param in get_params:
|
||||
if (not isinstance(param, list) or len(param) < 4 or
|
||||
param[0] != 'nfv'):
|
||||
continue
|
||||
parent = nfv
|
||||
for item in param[1:-1]:
|
||||
parent.setdefault(item, {})
|
||||
parent = parent[item]
|
||||
parent[param[-1]] = None
|
||||
|
||||
# TODO(oda-g): enhance to handle list
|
||||
# NOTE: List is not considered here and only 'fixed_ips' is treated as
|
||||
# list in userdata_default.py at the moment.
|
||||
# Note that if handling list is enhanced, userdata_default.py is
|
||||
# necessary to modify.
|
||||
return nfv
|
||||
|
||||
|
||||
def get_param_flavor(vdu_name, flavour_id, vnfd, grant):
|
||||
# try to get from grant
|
||||
if 'vimAssets' in grant:
|
||||
assets = grant['vimAssets']
|
||||
if 'computeResourceFlavours' in assets:
|
||||
flavours = assets['computeResourceFlavours']
|
||||
for flavour in flavours:
|
||||
if flavour['vnfdVirtualComputeDescId'] == vdu_name:
|
||||
return flavour['vimFlavourId']
|
||||
|
||||
# if specified in VNFD, use it
|
||||
# NOTE: if not found. parameter is set to None.
|
||||
# may be error when stack create
|
||||
return vnfd.get_compute_flavor(flavour_id, vdu_name)
|
||||
|
||||
|
||||
def get_param_image(vdu_name, flavour_id, vnfd, grant):
|
||||
# try to get from grant
|
||||
if 'vimAssets' in grant:
|
||||
assets = grant['vimAssets']
|
||||
if 'softwareImages' in assets:
|
||||
images = assets['softwareImages']
|
||||
for image in images:
|
||||
if image['vnfdSoftwareImageId'] == vdu_name:
|
||||
return image['vimSoftwareImageId']
|
||||
|
||||
# if specified in VNFD, use it
|
||||
# NOTE: if not found. parameter is set to None.
|
||||
# may be error when stack create
|
||||
sw_images = vnfd.get_sw_image(flavour_id)
|
||||
for name, image in sw_images.items():
|
||||
if name == vdu_name:
|
||||
return image
|
||||
|
||||
|
||||
def get_param_zone(vdu_name, grant_req, grant):
|
||||
if 'zones' not in grant or 'addResources' not in grant:
|
||||
return
|
||||
|
||||
for res in grant['addResources']:
|
||||
if 'zoneId' not in res:
|
||||
continue
|
||||
for req_res in grant_req['addResources']:
|
||||
if req_res['id'] == res['resourceDefinitionId']:
|
||||
if req_res.get('resourceTemplateId') == vdu_name:
|
||||
for zone in grant['zones']:
|
||||
if zone['id'] == res['zoneId']: # must be found
|
||||
return zone['zoneId']
|
||||
|
||||
|
||||
def get_current_capacity(vdu_name, inst):
|
||||
count = 0
|
||||
inst_vnfcs = (inst.get('instantiatedVnfInfo', {})
|
||||
.get('vnfcResourceInfo', []))
|
||||
for inst_vnfc in inst_vnfcs:
|
||||
if inst_vnfc['vduId'] == vdu_name:
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
|
||||
def get_param_capacity(vdu_name, inst, grant_req):
|
||||
# NOTE: refer grant_req here since interpretation of VNFD was done when
|
||||
# making grant_req.
|
||||
count = get_current_capacity(vdu_name, inst)
|
||||
|
||||
add_reses = grant_req.get('addResources', [])
|
||||
for res_def in add_reses:
|
||||
if (res_def['type'] == 'COMPUTE' and
|
||||
res_def['resourceTemplateId'] == vdu_name):
|
||||
count += 1
|
||||
|
||||
rm_reses = grant_req.get('removeResources', [])
|
||||
for res_def in rm_reses:
|
||||
if (res_def['type'] == 'COMPUTE' and
|
||||
res_def['resourceTemplateId'] == vdu_name):
|
||||
count -= 1
|
||||
|
||||
return count
|
||||
|
||||
|
||||
def _get_fixed_ips_from_extcp(extcp):
|
||||
fixed_ips = []
|
||||
for cp_conf in extcp['cpConfig'].values():
|
||||
if 'cpProtocolData' not in cp_conf:
|
||||
continue
|
||||
for prot_data in cp_conf['cpProtocolData']:
|
||||
if 'ipOverEthernet' not in prot_data:
|
||||
continue
|
||||
if 'ipAddresses' not in prot_data['ipOverEthernet']:
|
||||
continue
|
||||
for ip in prot_data['ipOverEthernet']['ipAddresses']:
|
||||
data = {}
|
||||
if 'fixedAddresses' in ip:
|
||||
# pick up only one ip address
|
||||
data['ip_address'] = str(ip['fixedAddresses'][0])
|
||||
if 'subnetId' in ip:
|
||||
data['subnet'] = ip['subnetId']
|
||||
if data:
|
||||
fixed_ips.append(data)
|
||||
return fixed_ips
|
||||
|
||||
|
||||
def get_param_network(cp_name, grant, req):
|
||||
# see grant first then instantiateVnfRequest
|
||||
vls = grant.get('extVirtualLinks', []) + req.get('extVirtualLinks', [])
|
||||
for vl in vls:
|
||||
for extcp in vl['extCps']:
|
||||
if extcp['cpdId'] == cp_name:
|
||||
return vl['resourceId']
|
||||
|
||||
|
||||
def get_param_fixed_ips(cp_name, grant, req):
|
||||
# see grant first then instantiateVnfRequest
|
||||
vls = grant.get('extVirtualLinks', []) + req.get('extVirtualLinks', [])
|
||||
for vl in vls:
|
||||
for extcp in vl['extCps']:
|
||||
if extcp['cpdId'] == cp_name:
|
||||
return _get_fixed_ips_from_extcp(extcp)
|
||||
|
||||
|
||||
def get_param_network_from_inst(cp_name, inst):
|
||||
for vl in inst['instantiatedVnfInfo'].get('extVirtualLinkInfo', []):
|
||||
for extcp in vl.get('currentVnfExtCpData', []):
|
||||
if extcp['cpdId'] == cp_name:
|
||||
return vl['resourceHandle']['resourceId']
|
||||
|
||||
|
||||
def get_param_fixed_ips_from_inst(cp_name, inst):
|
||||
for vl in inst['instantiatedVnfInfo'].get('extVirtualLinkInfo', []):
|
||||
for extcp in vl.get('currentVnfExtCpData', []):
|
||||
if extcp['cpdId'] == cp_name:
|
||||
return _get_fixed_ips_from_extcp(extcp)
|
||||
|
||||
|
||||
def apply_ext_managed_vls(hot_dict, req, grant):
|
||||
# see grant first then instantiateVnfRequest
|
||||
mgd_vls = (grant.get('extManagedVirtualLinks', []) +
|
||||
req.get('extManagedVirtualLinks', []))
|
||||
|
||||
# NOTE: refer HOT only here, not refer VNFD.
|
||||
# HOT and VNFD must be consistent.
|
||||
|
||||
for mgd_vl in mgd_vls:
|
||||
vl_name = mgd_vl['vnfVirtualLinkDescId']
|
||||
network_id = mgd_vl['resourceId']
|
||||
get_res = {'get_resource': vl_name}
|
||||
|
||||
def _change(item):
|
||||
if not isinstance(item, dict):
|
||||
return
|
||||
for key, value in item.items():
|
||||
if value == get_res:
|
||||
item[key] = network_id
|
||||
else:
|
||||
_change(value)
|
||||
|
||||
del_reses = []
|
||||
for res_name, res_data in hot_dict.get('resources', {}).items():
|
||||
# delete network definition
|
||||
if res_name == vl_name:
|
||||
del_reses.append(res_name)
|
||||
|
||||
# delete subnet definition
|
||||
if res_data['type'] == 'OS::Neutron::Subnet':
|
||||
net = (res_data.get('properties', {})
|
||||
.get('network', {})
|
||||
.get('get_resource'))
|
||||
if net == vl_name:
|
||||
del_reses.append(res_name)
|
||||
|
||||
# change '{get_resource: vl_name}' to network_id
|
||||
_change(res_data)
|
||||
|
||||
for res_name in del_reses:
|
||||
hot_dict['resources'].pop(res_name)
|
||||
|
|
|
@ -210,6 +210,52 @@ class LocalNfvo(object):
|
|||
softwareImages=vim_sw_images
|
||||
)
|
||||
|
||||
def change_vnfpkg_grant(self, context, grant_req, grant_res):
|
||||
attr_list = ['updateResources', 'addResources', 'removeResources']
|
||||
for attr in attr_list:
|
||||
if grant_req.obj_attr_is_set(attr):
|
||||
res_list = []
|
||||
for res_def in grant_req[attr]:
|
||||
g_info = objects.GrantInfoV1(res_def.id)
|
||||
res_list.append(g_info)
|
||||
grant_res[attr] = res_list
|
||||
vnfd = self.get_vnfd(context, grant_req.vnfdId)
|
||||
sw_image_data = vnfd.get_sw_image_data(grant_req.flavourId)
|
||||
target_vdu_ids = [res['resourceTemplateId'] for
|
||||
res in grant_req['updateResources']]
|
||||
vdu_nodes = {key: value for key, value in vnfd.get_vdu_nodes(
|
||||
grant_req.flavourId).items() if key in target_vdu_ids}
|
||||
target_storage_nodes = []
|
||||
for key, value in vdu_nodes.items():
|
||||
target_storage_nodes.extend(vnfd.get_vdu_storages(value))
|
||||
|
||||
vim_sw_images = []
|
||||
for res_id, sw_data in sw_image_data.items():
|
||||
if 'file' in sw_data and (res_id in target_storage_nodes or
|
||||
res_id in target_vdu_ids):
|
||||
vim_info = self._get_vim_info(context, grant_req)
|
||||
if vim_info is None:
|
||||
msg = "No VimConnectionInfo to create glance image"
|
||||
LOG.exception(msg)
|
||||
raise sol_ex.LocalNfvoGrantFailed(sol_detail=msg)
|
||||
try:
|
||||
image = self._glance_create_image(
|
||||
vim_info, vnfd, sw_data, grant_req.vnfInstanceId)
|
||||
except Exception:
|
||||
msg = "glance image create failed"
|
||||
LOG.exception(msg)
|
||||
raise sol_ex.LocalNfvoGrantFailed(sol_detail=msg)
|
||||
else:
|
||||
image = sw_data['name']
|
||||
vim_sw_image = objects.VimSoftwareImageV1(
|
||||
vnfdSoftwareImageId=res_id,
|
||||
vimSoftwareImageId=image)
|
||||
vim_sw_images.append(vim_sw_image)
|
||||
if vim_sw_images:
|
||||
grant_res.vimAssets = objects.GrantV1_VimAssets(
|
||||
softwareImages=vim_sw_images
|
||||
)
|
||||
|
||||
def grant(self, context, grant_req):
|
||||
grant_res = objects.GrantV1(
|
||||
id=uuidutils.generate_uuid(),
|
||||
|
@ -222,6 +268,9 @@ class LocalNfvo(object):
|
|||
if grant_req.operation == v2_fields.LcmOperationType.INSTANTIATE:
|
||||
self.instantiate_grant(context, grant_req, grant_res)
|
||||
|
||||
elif grant_req.operation == v2_fields.LcmOperationType.CHANGE_VNFPKG:
|
||||
self.change_vnfpkg_grant(context, grant_req, grant_res)
|
||||
|
||||
endpoint = config.CONF.v2_vnfm.endpoint
|
||||
grant_res._links = objects.GrantV1_Links(
|
||||
vnfLcmOpOcc=objects.Link(
|
||||
|
@ -312,8 +361,10 @@ class LocalNfvo(object):
|
|||
if vim_info is None:
|
||||
# never happen. just for code consistency.
|
||||
return
|
||||
self._glance_delete_images(vim_info, inst.id)
|
||||
elif lcmocc.operation == v2_fields.LcmOperationType.MODIFY_INFO:
|
||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||
self._glance_delete_images(vim_info, inst.id)
|
||||
elif lcmocc.operation == v2_fields.LcmOperationType.MODIFY_INFO or (
|
||||
lcmocc.operation == v2_fields.LcmOperationType.CHANGE_VNFPKG):
|
||||
if (lcmocc.operationState ==
|
||||
v2_fields.LcmOperationStateType.PROCESSING):
|
||||
# register vnfdId of vnf instance so that
|
||||
|
|
|
@ -28,13 +28,23 @@ class ChangeCurrentVnfPkgRequest(base.TackerObject,
|
|||
|
||||
fields = {
|
||||
'vnfdId': fields.StringField(nullable=False),
|
||||
# NOTE: 'extVirtualLinks' is not supported.
|
||||
# It can be specified but make no effect at all.
|
||||
'extVirtualLinks': fields.ListOfObjectsField(
|
||||
'ExtVirtualLinkData', nullable=True),
|
||||
# NOTE: 'extManagedVirtualLinks' is not supported.
|
||||
# It can be specified but make no effect at all.
|
||||
'extManagedVirtualLinks': fields.ListOfObjectsField(
|
||||
'ExtManagedVirtualLinkData', nullable=True),
|
||||
# NOTE: 'vimConnectionInfo' is not supported.
|
||||
# It can be specified but make no effect at all.
|
||||
'vimConnectionInfo': fields.DictOfObjectsField(
|
||||
'VimConnectionInfo', nullable=True),
|
||||
'additionalParams': fields.KeyValuePairsField(nullable=True),
|
||||
# NOTE: 'extensions' is not supported.
|
||||
# It can be specified but make no effect at all.
|
||||
'extensions': fields.KeyValuePairsField(nullable=True),
|
||||
# NOTE: 'vnfConfigurableProperties' is not supported.
|
||||
# It can be specified but make no effect at all.
|
||||
'vnfConfigurableProperties': fields.KeyValuePairsField(nullable=True),
|
||||
}
|
||||
|
|
|
@ -112,6 +112,12 @@ class Client(object):
|
|||
path, "POST", body=req_body, version="2.0.0")
|
||||
self.print(resp, body)
|
||||
|
||||
def change_vnfpkg(self, id, req_body):
|
||||
path = self.path + '/' + id + '/change_vnfpkg'
|
||||
resp, body = self.client.do_request(
|
||||
path, "POST", body=req_body, version="2.0.0")
|
||||
self.print(resp, body)
|
||||
|
||||
def retry(self, id):
|
||||
path = self.path + '/' + id + '/retry'
|
||||
resp, body = self.client.do_request(path, "POST", version="2.0.0")
|
||||
|
@ -140,6 +146,7 @@ def usage():
|
|||
print(" inst scale {id} body(path of content)")
|
||||
print(" inst heal {id} body(path of content)")
|
||||
print(" inst chg_ext_conn {id} body(path of content)")
|
||||
print(" inst change_vnfpkg {id} body(path of content)")
|
||||
print(" subsc create body(path of content)")
|
||||
print(" subsc list [body(path of content)]")
|
||||
print(" subsc show {id}")
|
||||
|
@ -166,7 +173,8 @@ if __name__ == '__main__':
|
|||
|
||||
if resource == "inst":
|
||||
if action not in ["create", "list", "show", "delete", "update",
|
||||
"inst", "term", "scale", "heal", "chg_ext_conn"]:
|
||||
"inst", "term", "scale", "heal", "chg_ext_conn",
|
||||
"change_vnfpkg"]:
|
||||
usage()
|
||||
client = Client("/vnflcm/v2/vnf_instances")
|
||||
elif resource == "subsc":
|
||||
|
@ -224,6 +232,10 @@ if __name__ == '__main__':
|
|||
if len(sys.argv) != 5:
|
||||
usage()
|
||||
client.chg_ext_conn(sys.argv[3], get_body(sys.argv[4]))
|
||||
elif action == "change_vnfpkg":
|
||||
if len(sys.argv) != 5:
|
||||
usage()
|
||||
client.change_vnfpkg(sys.argv[3], get_body(sys.argv[4]))
|
||||
elif action == "retry":
|
||||
if len(sys.argv) != 4:
|
||||
usage()
|
||||
|
|
|
@ -76,6 +76,9 @@ class BaseSolV2Test(base.BaseTestCase):
|
|||
service_type='compute')
|
||||
cls.heat_client = heat_utils.HeatClient(vim_info)
|
||||
|
||||
cls.cinder_client = http_client.HttpClient(
|
||||
auth, service_type='block-storage')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(BaseSolV2Test, cls).tearDownClass()
|
||||
|
@ -328,11 +331,21 @@ class BaseSolV2Test(base.BaseTestCase):
|
|||
return self.tacker_client.do_request(
|
||||
path, "POST", body=req_body, version="2.0.0")
|
||||
|
||||
def change_vnfpkg(self, inst_id, req_body):
|
||||
path = "/vnflcm/v2/vnf_instances/{}/change_vnfpkg".format(inst_id)
|
||||
return self.tacker_client.do_request(
|
||||
path, "POST", body=req_body, version="2.0.0")
|
||||
|
||||
def terminate_vnf_instance(self, inst_id, req_body):
|
||||
path = "/vnflcm/v2/vnf_instances/{}/terminate".format(inst_id)
|
||||
return self.tacker_client.do_request(
|
||||
path, "POST", body=req_body, version="2.0.0")
|
||||
|
||||
def rollback(self, lcmocc_id):
|
||||
path = "/vnflcm/v2/vnf_lcm_op_occs/{}/rollback".format(lcmocc_id)
|
||||
return self.tacker_client.do_request(
|
||||
path, "POST", version="2.0.0")
|
||||
|
||||
def wait_lcmocc_complete(self, lcmocc_id):
|
||||
# NOTE: It is not necessary to set timeout because the operation
|
||||
# itself set timeout and the state will become 'FAILED_TEMP'.
|
||||
|
@ -476,3 +489,21 @@ class BaseSolV2Test(base.BaseTestCase):
|
|||
callback_url)
|
||||
self.assertEqual(1, len(notify_mock_responses))
|
||||
self.assertEqual(204, notify_mock_responses[0].status_code)
|
||||
|
||||
def get_current_vdu_image(
|
||||
self, stack_id, stack_name, resource_name):
|
||||
vdu_info = self.heat_client.get_resource_info(
|
||||
f"{stack_name}/{stack_id}", resource_name)
|
||||
if vdu_info.get('attributes').get('image'):
|
||||
image_id = vdu_info.get('attributes').get('image').get('id')
|
||||
else:
|
||||
volume_ids = [volume.get('id') for volume in vdu_info.get(
|
||||
'attributes').get('os-extended-volumes:volumes_attached')]
|
||||
for volume_id in volume_ids:
|
||||
path = f"/volumes/{volume_id}"
|
||||
resp, resp_body = self.cinder_client.do_request(path, "GET")
|
||||
if resp_body['volume']['volume_image_metadata']:
|
||||
image_id = resp_body['volume'][
|
||||
'volume_image_metadata'].get('image_id')
|
||||
|
||||
return image_id
|
||||
|
|
|
@ -829,3 +829,109 @@ def change_ext_conn_min(net_ids, subnets):
|
|||
ext_vl_1
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def change_vnfpkg_create(vnfd_id):
|
||||
return {
|
||||
"vnfdId": vnfd_id,
|
||||
"vnfInstanceName": "vnf_change_vnfpkg",
|
||||
"vnfInstanceDescription": "test_change_vnfpkg_from_image_to_image",
|
||||
"metadata": {"dummy-key": "dummy-val"}
|
||||
}
|
||||
|
||||
|
||||
def change_vnfpkg_instantiate(net_ids, subnet_ids, auth_url,
|
||||
flavor_id='simple'):
|
||||
ext_vl_1 = {
|
||||
"id": uuidutils.generate_uuid(),
|
||||
"resourceId": net_ids['net0'],
|
||||
"extCps": [
|
||||
{
|
||||
"cpdId": "VDU1_CP1",
|
||||
"cpConfig": {
|
||||
"VDU1_CP1_1": {
|
||||
"cpProtocolData": [{
|
||||
"layerProtocol": "IP_OVER_ETHERNET",
|
||||
"ipOverEthernet": {
|
||||
"ipAddresses": [{
|
||||
"type": "IPV4",
|
||||
"numDynamicAddresses": 1}]}}]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cpdId": "VDU2_CP1",
|
||||
"cpConfig": {
|
||||
"VDU2_CP1_1": {
|
||||
"cpProtocolData": [{
|
||||
"layerProtocol": "IP_OVER_ETHERNET",
|
||||
"ipOverEthernet": {
|
||||
"ipAddresses": [{
|
||||
"type": "IPV4",
|
||||
"fixedAddresses": ["10.10.0.101"]}]}}]}
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
return {
|
||||
"flavourId": flavor_id,
|
||||
"instantiationLevelId": "instantiation_level_1",
|
||||
"extVirtualLinks": [
|
||||
ext_vl_1
|
||||
],
|
||||
"vimConnectionInfo": {
|
||||
"vim1": {
|
||||
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
|
||||
"vimId": uuidutils.generate_uuid(),
|
||||
"interfaceInfo": {"endpoint": auth_url},
|
||||
"accessInfo": {
|
||||
"username": "nfv_user",
|
||||
"region": "RegionOne",
|
||||
"password": "devstack",
|
||||
"project": "nfv",
|
||||
"projectDomain": "Default",
|
||||
"userDomain": "Default"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def change_vnfpkg(vnfd_id):
|
||||
return {
|
||||
"vnfdId": vnfd_id,
|
||||
"additionalParams": {
|
||||
"upgrade_type": "RollingUpdate",
|
||||
"lcm-operation-coordinate-old-vnf":
|
||||
"./Scripts/coordinate_old_vnf.py",
|
||||
"lcm-operation-coordinate-old-vnf-class": "CoordinateOldVnf",
|
||||
"lcm-operation-coordinate-new-vnf":
|
||||
"./Scripts/coordinate_new_vnf.py",
|
||||
"lcm-operation-coordinate-new-vnf-class": "CoordinateNewVnf",
|
||||
"vdu_params": [{
|
||||
"vdu_id": "VDU1",
|
||||
"old_vnfc_param": {
|
||||
"cp_name": "VDU1_CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"
|
||||
},
|
||||
"new_vnfc_param": {
|
||||
"cp_name": "VDU1_CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"
|
||||
}
|
||||
}, {
|
||||
"vdu_id": "VDU2",
|
||||
"old_vnfc_param": {
|
||||
"cp_name": "VDU2_CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"
|
||||
},
|
||||
"new_vnfc_param": {
|
||||
"cp_name": "VDU2_CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import yaml
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.infra_drivers.openstack import userdata_utils
|
||||
|
||||
|
@ -47,39 +48,39 @@ class UserData(userdata_utils.AbstractUserData):
|
|||
link_port_ids.append(cp_conf['linkPortId'])
|
||||
return link_port_ids
|
||||
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = req['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
if 'computeFlavourId' in vdu_value:
|
||||
vdu_value['computeFlavourId'] = (
|
||||
userdata_utils.get_param_flavor(
|
||||
common_script_utils.get_param_flavor(
|
||||
vdu_name, flavour_id, vnfd, grant))
|
||||
if 'vcImageId' in vdu_value:
|
||||
vdu_value['vcImageId'] = userdata_utils.get_param_image(
|
||||
vdu_value['vcImageId'] = common_script_utils.get_param_image(
|
||||
vdu_name, flavour_id, vnfd, grant)
|
||||
if 'locationConstraints' in vdu_value:
|
||||
vdu_value['locationConstraints'] = (
|
||||
userdata_utils.get_param_zone(
|
||||
common_script_utils.get_param_zone(
|
||||
vdu_name, grant_req, grant))
|
||||
if 'desired_capacity' in vdu_value:
|
||||
vdu_value['desired_capacity'] = (
|
||||
userdata_utils.get_param_capacity(
|
||||
common_script_utils.get_param_capacity(
|
||||
vdu_name, inst, grant_req))
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
for cp_name, cp_value in cps.items():
|
||||
if 'network' in cp_value:
|
||||
cp_value['network'] = userdata_utils.get_param_network(
|
||||
cp_value['network'] = common_script_utils.get_param_network(
|
||||
cp_name, grant, req)
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = userdata_utils.get_param_fixed_ips(
|
||||
ext_fixed_ips = common_script_utils.get_param_fixed_ips(
|
||||
cp_name, grant, req)
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
|
@ -104,7 +105,7 @@ class UserData(userdata_utils.AbstractUserData):
|
|||
cp_value['port'] = _get_param_port(
|
||||
cp_name, grant, req).pop()
|
||||
|
||||
userdata_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
common_script_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
|
||||
if 'nfv' in req.get('additionalParams', {}):
|
||||
nfv_dict = inst_utils.json_merge_patch(nfv_dict,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
|
||||
# Copyright (C) 2022 Fujitsu
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -15,6 +15,7 @@
|
|||
|
||||
import yaml
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.infra_drivers.openstack import userdata_utils
|
||||
|
||||
|
@ -47,39 +48,39 @@ class UserData(userdata_utils.AbstractUserData):
|
|||
link_port_ids.append(cp_conf['linkPortId'])
|
||||
return link_port_ids
|
||||
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = req['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
if 'computeFlavourId' in vdu_value:
|
||||
vdu_value['computeFlavourId'] = (
|
||||
userdata_utils.get_param_flavor(
|
||||
common_script_utils.get_param_flavor(
|
||||
vdu_name, flavour_id, vnfd, grant))
|
||||
if 'vcImageId' in vdu_value:
|
||||
vdu_value['vcImageId'] = userdata_utils.get_param_image(
|
||||
vdu_value['vcImageId'] = common_script_utils.get_param_image(
|
||||
vdu_name, flavour_id, vnfd, grant)
|
||||
if 'locationConstraints' in vdu_value:
|
||||
vdu_value['locationConstraints'] = (
|
||||
userdata_utils.get_param_zone(
|
||||
common_script_utils.get_param_zone(
|
||||
vdu_name, grant_req, grant))
|
||||
if 'desired_capacity' in vdu_value:
|
||||
vdu_value['desired_capacity'] = (
|
||||
userdata_utils.get_param_capacity(
|
||||
common_script_utils.get_param_capacity(
|
||||
vdu_name, inst, grant_req))
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
for cp_name, cp_value in cps.items():
|
||||
if 'network' in cp_value:
|
||||
cp_value['network'] = userdata_utils.get_param_network(
|
||||
cp_value['network'] = common_script_utils.get_param_network(
|
||||
cp_name, grant, req)
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = userdata_utils.get_param_fixed_ips(
|
||||
ext_fixed_ips = common_script_utils.get_param_fixed_ips(
|
||||
cp_name, grant, req)
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
|
@ -104,7 +105,7 @@ class UserData(userdata_utils.AbstractUserData):
|
|||
cp_value['port'] = _get_param_port(
|
||||
cp_name, grant, req).pop()
|
||||
|
||||
userdata_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
common_script_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
|
||||
if 'nfv' in req.get('additionalParams', {}):
|
||||
nfv_dict = inst_utils.json_merge_patch(nfv_dict,
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1_scale:
|
||||
type: OS::Heat::AutoScalingGroup
|
||||
properties:
|
||||
min_size: 1
|
||||
max_size: 3
|
||||
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
|
||||
resource:
|
||||
type: base_hot_nested_VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU1, vcImageId] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
|
||||
|
||||
VDU1_scale_out:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: 1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
VDU1_scale_in:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: -1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
fixed_ips:
|
||||
- ip_address: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, ip_address]}
|
||||
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,27 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
image: { get_param: image }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1_scale:
|
||||
type: OS::Heat::AutoScalingGroup
|
||||
properties:
|
||||
min_size: 1
|
||||
max_size: 3
|
||||
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
|
||||
resource:
|
||||
type: base_hot_nested_VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU1-VirtualStorage, vcImageId] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
|
||||
|
||||
VDU1_scale_out:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: 1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
VDU1_scale_in:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: -1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
fixed_ips:
|
||||
- ip_address: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, ip_address]}
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,39 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
block_device_mapping_v2: [{"volume_id": { get_resource: VDU1-VirtualStorage }}]
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
|
||||
VDU1-VirtualStorage:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
image: { get_param: image }
|
||||
size: 4
|
||||
volume_type: { get_resource: multi }
|
||||
multi:
|
||||
type: OS::Cinder::VolumeType
|
||||
properties:
|
||||
name: { get_resource: VDU1_CP1 }
|
||||
metadata: { multiattach: "<is> True" }
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Simple deployment flavour for Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: simple
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate: []
|
||||
instantiate_start: []
|
||||
instantiate_end: []
|
||||
terminate: []
|
||||
terminate_start: []
|
||||
terminate_end: []
|
||||
modify_information: []
|
||||
modify_information_start: []
|
||||
modify_information_end: []
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
sw_image_data:
|
||||
name: VDU1-image
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
sw_image_data:
|
||||
name: VDU2-image
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU1_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
|
||||
VDU2_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 2
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 3
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
|
@ -0,0 +1,224 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Simple deployment flavour for Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: volume
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate: []
|
||||
instantiate_start: []
|
||||
instantiate_end: []
|
||||
terminate: []
|
||||
terminate_start: []
|
||||
terminate_end: []
|
||||
modify_information: []
|
||||
modify_information_start: []
|
||||
modify_information_end: []
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
requirements:
|
||||
- virtual_storage: VDU1-VirtualStorage
|
||||
|
||||
VDU1-VirtualStorage:
|
||||
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
|
||||
properties:
|
||||
virtual_block_storage_data:
|
||||
size_of_storage: 4 GB
|
||||
rdma_enabled: true
|
||||
sw_image_data:
|
||||
name: cirros-0.5.2-x86_64-disk
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
sw_image_data:
|
||||
name: VDU2-image
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU1_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
|
||||
VDU2_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 2
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 3
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
|
@ -0,0 +1,31 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
- change_vnf_pkg_new_image_df_simple.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
selected_flavour:
|
||||
type: string
|
||||
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: { get_input: selected_flavour }
|
||||
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
provider: Company
|
||||
product_name: Sample VNF
|
||||
software_version: '1.0'
|
||||
descriptor_version: '1.0'
|
||||
vnfm_info:
|
||||
- Tacker
|
||||
requirements:
|
||||
#- virtual_link_external # mapped in lower-level templates
|
||||
#- virtual_link_internal # mapped in lower-level templates
|
|
@ -0,0 +1,53 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: VNF type definition
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
|
||||
node_types:
|
||||
company.provider.VNF:
|
||||
derived_from: tosca.nodes.nfv.VNF
|
||||
properties:
|
||||
descriptor_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ]
|
||||
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
descriptor_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
provider:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Company' ] ]
|
||||
default: 'Company'
|
||||
product_name:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||
default: 'Sample VNF'
|
||||
software_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
constraints: [ valid_values: [ Tacker ] ]
|
||||
default: [ Tacker ]
|
||||
flavour_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ simple,volume ] ]
|
||||
default: simple
|
||||
flavour_description:
|
||||
type: string
|
||||
default: "flavour"
|
||||
requirements:
|
||||
- virtual_link_external1:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
- virtual_link_external2:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
type: tosca.interfaces.nfv.Vnflcm
|
|
@ -0,0 +1,145 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 sys
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
import paramiko
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CMD_TIMEOUT = 30
|
||||
SERVER_WAIT_COMPLETE_TIME = 60
|
||||
SSH_CONNECT_RETRY_COUNT = 4
|
||||
|
||||
|
||||
class SampleNewCoordinateVNFScript(object):
|
||||
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir, vdu_info):
|
||||
self.req = req
|
||||
self.inst = inst
|
||||
self.grant_req = grant_req
|
||||
self.grant = grant
|
||||
self.csar_dir = csar_dir
|
||||
self.vdu_info = vdu_info
|
||||
|
||||
def coordinate_vnf(self):
|
||||
# check ssh connect and os version
|
||||
"""(YiFeng) Add comment to check ssh access
|
||||
|
||||
The next part of code is to check connect VM via ssh.
|
||||
Since the zuul's network cannot check this content, so
|
||||
we comment this part of code. If you want to check them
|
||||
in your local environment, please uncomment.
|
||||
# user = self.vdu_info.get('vdu_param').get(
|
||||
# 'new_vnfc_param').get('username')
|
||||
# password = self.vdu_info.get('vdu_param').get(
|
||||
# 'new_vnfc_param').get('password')
|
||||
# host = self.vdu_info.get("ssh_ip")
|
||||
# commander = self._init_commander(
|
||||
# user, password, host, retry=SSH_CONNECT_RETRY_COUNT)
|
||||
# ssh_command = 'cat /etc/os-release | grep PRETTY_NAME'
|
||||
# result = self._execute_command(commander, host, ssh_command)
|
||||
# os_version = result[0].replace('\n', '').split('=')[1]
|
||||
# LOG.info('The os version of this new VM is %s', os_version)
|
||||
"""
|
||||
# check image and flavour updated successfully
|
||||
vnfd = vnfd_utils.Vnfd(self.req.get('vnfdId'))
|
||||
vnfd.init_from_csar_dir(self.csar_dir)
|
||||
vdu_infos = common_script_utils.get_vdu_info(
|
||||
self.grant, self.inst, vnfd)
|
||||
|
||||
vdu_id = self.vdu_info.get('vdu_param').get('vdu_id')
|
||||
image = vdu_infos.get(vdu_id, {}).get('image')
|
||||
flavor = vdu_infos.get(vdu_id, {}).get('flavor')
|
||||
if self.vdu_info.get('new_image') != image or self.vdu_info.get(
|
||||
'new_flavor') != flavor:
|
||||
error = "The VM's image or flavour update failed'"
|
||||
LOG.error(error)
|
||||
raise sol_ex.VMRunningFailed(error)
|
||||
|
||||
def _init_commander(self, user, password, host, retry):
|
||||
while retry > 0:
|
||||
try:
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(
|
||||
host, username=user, password=password)
|
||||
LOG.info("Connected to %s", host)
|
||||
return ssh
|
||||
except paramiko.AuthenticationException as e:
|
||||
LOG.error("Authentication failed when connecting to %s",
|
||||
host)
|
||||
raise sol_ex.VMRunningFailed(e)
|
||||
except (paramiko.SSHException,
|
||||
paramiko.ssh_exception.NoValidConnectionsError) as e:
|
||||
LOG.debug(e)
|
||||
retry -= 1
|
||||
if retry == 0:
|
||||
LOG.error(e)
|
||||
raise sol_ex.VMRunningFailed(e)
|
||||
time.sleep(SERVER_WAIT_COMPLETE_TIME)
|
||||
|
||||
def _execute_command(self, commander, host, command):
|
||||
try:
|
||||
stdin, stdout, stderr = commander.exec_command(command)
|
||||
cmd_out = stdout.readlines()
|
||||
cmd_err = stderr.readlines()
|
||||
return_code = stdout.channel.recv_exit_status()
|
||||
except paramiko.SSHException as e:
|
||||
LOG.error("Command execution failed at %s. Giving up", host)
|
||||
raise e
|
||||
finally:
|
||||
commander.close()
|
||||
if return_code != 0:
|
||||
error = cmd_err
|
||||
raise sol_ex.VMRunningFailed(error_info=error)
|
||||
result = "cmd: %s, stdout: %s, stderr: %s, return code: %s" % (
|
||||
command, cmd_out, cmd_err, return_code)
|
||||
LOG.debug("Remote command execution result: %s", result)
|
||||
return cmd_out
|
||||
|
||||
|
||||
def main():
|
||||
operation = "coordinate_vnf"
|
||||
script_dict = pickle.load(sys.stdin.buffer)
|
||||
req = script_dict['request']
|
||||
inst = script_dict['vnf_instance']
|
||||
grant_req = script_dict['grant_request']
|
||||
grant = script_dict['grant_response']
|
||||
csar_dir = script_dict['tmp_csar_dir']
|
||||
vdu_info = script_dict['vdu_info']
|
||||
script = SampleNewCoordinateVNFScript(
|
||||
req, inst, grant_req, grant,
|
||||
csar_dir, vdu_info)
|
||||
try:
|
||||
getattr(script, operation)()
|
||||
except Exception:
|
||||
raise Exception
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,4 @@
|
|||
TOSCA-Meta-File-Version: 1.0
|
||||
CSAR-Version: 1.1
|
||||
Created-by: Onboarding portal
|
||||
Entry-Definitions: Definitions/change_vnf_pkg_top.vnfd.yaml
|
|
@ -0,0 +1,51 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.tests.functional.sol_v2 import paramgen
|
||||
from tacker.tests.functional.sol_v2 import utils
|
||||
|
||||
|
||||
zip_file_name = os.path.basename(os.path.abspath(".")) + '.zip'
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
|
||||
image_dir = "../../../../etc/samples/etsi/nfv/common/Files/images/"
|
||||
image_file = "cirros-0.5.2-x86_64-disk.img"
|
||||
image_path = os.path.abspath(image_dir + image_file)
|
||||
|
||||
utils.make_zip(".", tmp_dir, vnfd_id, image_path)
|
||||
|
||||
shutil.move(os.path.join(tmp_dir, zip_file_name), ".")
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
# if your sample is change VM from image to image
|
||||
change_vnfpkg_req_from_image_to_image = paramgen.change_vnfpkg(vnfd_id)
|
||||
|
||||
with open("change_vnfpkg_req_from_image_to_image", "w", encoding='utf-8') as f:
|
||||
f.write(json.dumps(change_vnfpkg_req_from_image_to_image, indent=2))
|
||||
|
||||
# if your sample is change VM from volume to image
|
||||
change_vnfpkg_req_from_volume_to_image = paramgen.change_vnfpkg(vnfd_id)
|
||||
del change_vnfpkg_req_from_volume_to_image['additionalParams']['vdu_params'][0]
|
||||
|
||||
with open("change_vnfpkg_req_from_volume", "w", encoding='utf-8') as f:
|
||||
f.write(json.dumps(change_vnfpkg_req_from_volume_to_image, indent=2))
|
|
@ -0,0 +1,65 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1_scale:
|
||||
type: OS::Heat::AutoScalingGroup
|
||||
properties:
|
||||
min_size: 1
|
||||
max_size: 3
|
||||
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
|
||||
resource:
|
||||
type: base_hot_nested_VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU1, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
|
||||
|
||||
VDU1_scale_out:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: 1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
VDU1_scale_in:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: -1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
block_device_mapping_v2: [{"volume_id": { get_resource: VDU2-VirtualStorage }}]
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
fixed_ips:
|
||||
- ip_address: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, ip_address]}
|
||||
|
||||
VDU2-VirtualStorage:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
image: { get_param: [ nfv, VDU, VDU2-VirtualStorage, vcImageId ] }
|
||||
size: 4
|
||||
volume_type: { get_resource: multi }
|
||||
multi:
|
||||
type: OS::Cinder::VolumeType
|
||||
properties:
|
||||
name: VDU2-multi
|
||||
metadata: { multiattach: "<is> True" }
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,27 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
image: { get_param: image }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1_scale:
|
||||
type: OS::Heat::AutoScalingGroup
|
||||
properties:
|
||||
min_size: 1
|
||||
max_size: 3
|
||||
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
|
||||
resource:
|
||||
type: base_hot_nested_VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU1-VirtualStorage, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
|
||||
|
||||
VDU1_scale_out:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: 1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
VDU1_scale_in:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: -1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
block_device_mapping_v2: [{"volume_id": { get_resource: VDU2-VirtualStorage }}]
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
fixed_ips:
|
||||
- ip_address: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, ip_address]}
|
||||
|
||||
VDU2-VirtualStorage:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
image: { get_param: [ nfv, VDU, VDU2-VirtualStorage, vcImageId ] }
|
||||
size: 4
|
||||
volume_type: { get_resource: multi }
|
||||
multi:
|
||||
type: OS::Cinder::VolumeType
|
||||
properties:
|
||||
name: VDU2-multi
|
||||
metadata: { multiattach: "<is> True" }
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,39 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
block_device_mapping_v2: [{"volume_id": { get_resource: VDU1-VirtualStorage }}]
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
|
||||
VDU1-VirtualStorage:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
image: { get_param: image }
|
||||
size: 4
|
||||
volume_type: { get_resource: multi }
|
||||
multi:
|
||||
type: OS::Cinder::VolumeType
|
||||
properties:
|
||||
name: { get_resource: VDU1_CP1 }
|
||||
metadata: { multiattach: "<is> True" }
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Simple deployment flavour for Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: simple
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate: []
|
||||
instantiate_start: []
|
||||
instantiate_end: []
|
||||
terminate: []
|
||||
terminate_start: []
|
||||
terminate_end: []
|
||||
modify_information: []
|
||||
modify_information_start: []
|
||||
modify_information_end: []
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
sw_image_data:
|
||||
name: cirros-0.5.2-x86_64-disk
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: ds2G
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 2 GB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 2
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 20 GB
|
||||
requirements:
|
||||
- virtual_storage: VDU2-VirtualStorage
|
||||
|
||||
VDU2-VirtualStorage:
|
||||
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
|
||||
properties:
|
||||
virtual_block_storage_data:
|
||||
size_of_storage: 1 GB
|
||||
rdma_enabled: true
|
||||
sw_image_data:
|
||||
name: VDU2-VirtualStorage-image
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 12 GB
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU1_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
|
||||
VDU2_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 2
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 3
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
|
@ -0,0 +1,237 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Simple deployment flavour for Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: volume
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate: []
|
||||
instantiate_start: []
|
||||
instantiate_end: []
|
||||
terminate: []
|
||||
terminate_start: []
|
||||
terminate_end: []
|
||||
modify_information: []
|
||||
modify_information_start: []
|
||||
modify_information_end: []
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
requirements:
|
||||
- virtual_storage: VDU1-VirtualStorage
|
||||
|
||||
VDU1-VirtualStorage:
|
||||
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
|
||||
properties:
|
||||
virtual_block_storage_data:
|
||||
size_of_storage: 1 GB
|
||||
rdma_enabled: true
|
||||
sw_image_data:
|
||||
name: VDU1-VirtualStorage-image
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 12 GB
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
requirements:
|
||||
- virtual_storage: VDU2-VirtualStorage
|
||||
|
||||
VDU2-VirtualStorage:
|
||||
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
|
||||
properties:
|
||||
virtual_block_storage_data:
|
||||
size_of_storage: 1 GB
|
||||
rdma_enabled: true
|
||||
sw_image_data:
|
||||
name: VDU2-VirtualStorage-image
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 12 GB
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU1_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
|
||||
VDU2_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 2
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 3
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
|
@ -0,0 +1,31 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
- change_vnf_pkg_new_volume_df_simple.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
selected_flavour:
|
||||
type: string
|
||||
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: { get_input: selected_flavour }
|
||||
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
provider: Company
|
||||
product_name: Sample VNF
|
||||
software_version: '1.0'
|
||||
descriptor_version: '1.0'
|
||||
vnfm_info:
|
||||
- Tacker
|
||||
requirements:
|
||||
#- virtual_link_external # mapped in lower-level templates
|
||||
#- virtual_link_internal # mapped in lower-level templates
|
|
@ -0,0 +1,53 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: VNF type definition
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
|
||||
node_types:
|
||||
company.provider.VNF:
|
||||
derived_from: tosca.nodes.nfv.VNF
|
||||
properties:
|
||||
descriptor_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ]
|
||||
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
descriptor_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
provider:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Company' ] ]
|
||||
default: 'Company'
|
||||
product_name:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||
default: 'Sample VNF'
|
||||
software_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
constraints: [ valid_values: [ Tacker ] ]
|
||||
default: [ Tacker ]
|
||||
flavour_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ simple,volume ] ]
|
||||
default: simple
|
||||
flavour_description:
|
||||
type: string
|
||||
default: "flavour"
|
||||
requirements:
|
||||
- virtual_link_external1:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
- virtual_link_external2:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
type: tosca.interfaces.nfv.Vnflcm
|
|
@ -0,0 +1,145 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 sys
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
import paramiko
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CMD_TIMEOUT = 30
|
||||
SERVER_WAIT_COMPLETE_TIME = 60
|
||||
SSH_CONNECT_RETRY_COUNT = 4
|
||||
|
||||
|
||||
class SampleNewCoordinateVNFScript(object):
|
||||
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir, vdu_info):
|
||||
self.req = req
|
||||
self.inst = inst
|
||||
self.grant_req = grant_req
|
||||
self.grant = grant
|
||||
self.csar_dir = csar_dir
|
||||
self.vdu_info = vdu_info
|
||||
|
||||
def coordinate_vnf(self):
|
||||
# check ssh connect and os version
|
||||
"""(YiFeng) Add comment to check ssh access
|
||||
|
||||
The next part of code is to check connect VM via ssh.
|
||||
Since the zuul's network cannot check this content, so
|
||||
we comment this part of code. If you want to check them
|
||||
in your local environment, please uncomment.
|
||||
user = self.vdu_info.get('vdu_param').get(
|
||||
'new_vnfc_param').get('username')
|
||||
password = self.vdu_info.get('vdu_param').get(
|
||||
'new_vnfc_param').get('password')
|
||||
host = self.vdu_info.get("ssh_ip")
|
||||
commander = self._init_commander(
|
||||
user, password, host, retry=SSH_CONNECT_RETRY_COUNT)
|
||||
ssh_command = 'cat /etc/os-release | grep PRETTY_NAME'
|
||||
result = self._execute_command(commander, host, ssh_command)
|
||||
os_version = result[0].replace('\n', '').split('=')[1]
|
||||
LOG.info('The os version of this new VM is %s', os_version)
|
||||
"""
|
||||
# check image and flavour updated successfully
|
||||
vnfd = vnfd_utils.Vnfd(self.req.get('vnfdId'))
|
||||
vnfd.init_from_csar_dir(self.csar_dir)
|
||||
vdu_infos = common_script_utils.get_vdu_info(
|
||||
self.grant, self.inst, vnfd)
|
||||
|
||||
vdu_id = self.vdu_info.get('vdu_param').get('vdu_id')
|
||||
image = vdu_infos.get(vdu_id, {}).get('image')
|
||||
flavor = vdu_infos.get(vdu_id, {}).get('flavor')
|
||||
if self.vdu_info.get('new_image') != image or self.vdu_info.get(
|
||||
'new_flavor') != flavor:
|
||||
error = "The VM's image or flavour update failed'"
|
||||
LOG.error(error)
|
||||
raise sol_ex.VMRunningFailed(error)
|
||||
|
||||
def _init_commander(self, user, password, host, retry):
|
||||
while retry > 0:
|
||||
try:
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(
|
||||
host, username=user, password=password)
|
||||
LOG.info("Connected to %s", host)
|
||||
return ssh
|
||||
except paramiko.AuthenticationException as e:
|
||||
LOG.error("Authentication failed when connecting to %s",
|
||||
host)
|
||||
raise sol_ex.VMRunningFailed(e)
|
||||
except (paramiko.SSHException,
|
||||
paramiko.ssh_exception.NoValidConnectionsError) as e:
|
||||
LOG.debug(e)
|
||||
retry -= 1
|
||||
if retry == 0:
|
||||
LOG.error(e)
|
||||
raise sol_ex.VMRunningFailed(e)
|
||||
time.sleep(SERVER_WAIT_COMPLETE_TIME)
|
||||
|
||||
def _execute_command(self, commander, host, command):
|
||||
try:
|
||||
stdin, stdout, stderr = commander.exec_command(command)
|
||||
cmd_out = stdout.readlines()
|
||||
cmd_err = stderr.readlines()
|
||||
return_code = stdout.channel.recv_exit_status()
|
||||
except paramiko.SSHException as e:
|
||||
LOG.error("Command execution failed at %s. Giving up", host)
|
||||
raise e
|
||||
finally:
|
||||
commander.close()
|
||||
if return_code != 0:
|
||||
error = cmd_err
|
||||
raise sol_ex.VMRunningFailed(error_info=error)
|
||||
result = "cmd: %s, stdout: %s, stderr: %s, return code: %s" % (
|
||||
command, cmd_out, cmd_err, return_code)
|
||||
LOG.debug("Remote command execution result: %s", result)
|
||||
return cmd_out
|
||||
|
||||
|
||||
def main():
|
||||
operation = "coordinate_vnf"
|
||||
script_dict = pickle.load(sys.stdin.buffer)
|
||||
req = script_dict['request']
|
||||
inst = script_dict['vnf_instance']
|
||||
grant_req = script_dict['grant_request']
|
||||
grant = script_dict['grant_response']
|
||||
csar_dir = script_dict['tmp_csar_dir']
|
||||
vdu_info = script_dict['vdu_info']
|
||||
script = SampleNewCoordinateVNFScript(
|
||||
req, inst, grant_req, grant,
|
||||
csar_dir, vdu_info)
|
||||
try:
|
||||
getattr(script, operation)()
|
||||
except Exception:
|
||||
raise Exception
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,67 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 sys
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CMD_TIMEOUT = 30
|
||||
SERVER_WAIT_COMPLETE_TIME = 60
|
||||
SSH_CONNECT_RETRY_COUNT = 4
|
||||
|
||||
|
||||
class SampleErrorNewCoordinateVNFScript(object):
|
||||
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir, vdu_info):
|
||||
self.req = req
|
||||
self.inst = inst
|
||||
self.grant_req = grant_req
|
||||
self.grant = grant
|
||||
self.csar_dir = csar_dir
|
||||
self.vdu_info = vdu_info
|
||||
|
||||
def coordinate_vnf(self):
|
||||
raise Exception("ErrorNewCoordinateVNFScript")
|
||||
|
||||
|
||||
def main():
|
||||
operation = "coordinate_vnf"
|
||||
script_dict = pickle.load(sys.stdin.buffer)
|
||||
req = script_dict['request']
|
||||
inst = script_dict['vnf_instance']
|
||||
grant_req = script_dict['grant_request']
|
||||
grant = script_dict['grant_response']
|
||||
csar_dir = script_dict['tmp_csar_dir']
|
||||
vdu_info = script_dict['vdu_info']
|
||||
script = SampleErrorNewCoordinateVNFScript(
|
||||
req, inst, grant_req, grant,
|
||||
csar_dir, vdu_info)
|
||||
try:
|
||||
getattr(script, operation)()
|
||||
except Exception:
|
||||
raise Exception
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,4 @@
|
|||
TOSCA-Meta-File-Version: 1.0
|
||||
CSAR-Version: 1.1
|
||||
Created-by: Onboarding portal
|
||||
Entry-Definitions: Definitions/change_vnf_pkg_top.vnfd.yaml
|
|
@ -0,0 +1,54 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.tests.functional.sol_v2 import paramgen
|
||||
from tacker.tests.functional.sol_v2 import utils
|
||||
|
||||
|
||||
zip_file_name = os.path.basename(os.path.abspath(".")) + '.zip'
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
|
||||
image_dir = "../../../../etc/samples/etsi/nfv/common/Files/images/"
|
||||
image_file = "cirros-0.5.2-x86_64-disk.img"
|
||||
image_path = os.path.abspath(image_dir + image_file)
|
||||
|
||||
utils.make_zip(".", tmp_dir, vnfd_id, image_path)
|
||||
|
||||
shutil.move(os.path.join(tmp_dir, zip_file_name), ".")
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
# if your sample is change VM from image to volume
|
||||
change_vnfpkg_req_from_image_to_volume = paramgen.change_vnfpkg(vnfd_id)
|
||||
del change_vnfpkg_req_from_image_to_volume['additionalParams']['vdu_params'][0]
|
||||
|
||||
with open("change_vnfpkg_req_from_image_to_volume", "w",
|
||||
encoding='utf-8') as f:
|
||||
f.write(json.dumps(change_vnfpkg_req_from_image_to_volume, indent=2))
|
||||
|
||||
# if your sample is change VM from volume to volume
|
||||
change_vnfpkg_req_from_volume_to_volume = paramgen.change_vnfpkg(vnfd_id)
|
||||
del change_vnfpkg_req_from_volume_to_volume[
|
||||
'additionalParams']['vdu_params'][0]
|
||||
|
||||
with open("change_vnfpkg_req_from_volume", "w", encoding='utf-8') as f:
|
||||
f.write(json.dumps(change_vnfpkg_req_from_volume_to_volume, indent=2))
|
|
@ -0,0 +1,53 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1_scale:
|
||||
type: OS::Heat::AutoScalingGroup
|
||||
properties:
|
||||
min_size: 1
|
||||
max_size: 3
|
||||
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
|
||||
resource:
|
||||
type: base_hot_nested_VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU1, vcImageId] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
|
||||
|
||||
VDU1_scale_out:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: 1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
VDU1_scale_in:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: -1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
fixed_ips:
|
||||
- ip_address: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, ip_address]}
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,26 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
image: { get_param: image }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
|
@ -0,0 +1,219 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Simple deployment flavour for Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: simple
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate: []
|
||||
instantiate_start: []
|
||||
instantiate_end: []
|
||||
terminate: []
|
||||
terminate_start: []
|
||||
terminate_end: []
|
||||
modify_information: []
|
||||
modify_information_start: []
|
||||
modify_information_end: []
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
sw_image_data:
|
||||
name: VDU1-image
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.error
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
sw_image_data:
|
||||
name: VDU2-image
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU1_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
|
||||
VDU2_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 2
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 3
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
|
@ -0,0 +1,31 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
- change_vnf_pkg_error_image_df_simple.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
selected_flavour:
|
||||
type: string
|
||||
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: { get_input: selected_flavour }
|
||||
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
provider: Company
|
||||
product_name: Sample VNF
|
||||
software_version: '1.0'
|
||||
descriptor_version: '1.0'
|
||||
vnfm_info:
|
||||
- Tacker
|
||||
requirements:
|
||||
#- virtual_link_external # mapped in lower-level templates
|
||||
#- virtual_link_internal # mapped in lower-level templates
|
|
@ -0,0 +1,53 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: VNF type definition
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
|
||||
node_types:
|
||||
company.provider.VNF:
|
||||
derived_from: tosca.nodes.nfv.VNF
|
||||
properties:
|
||||
descriptor_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ]
|
||||
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
descriptor_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
provider:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Company' ] ]
|
||||
default: 'Company'
|
||||
product_name:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||
default: 'Sample VNF'
|
||||
software_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
constraints: [ valid_values: [ Tacker ] ]
|
||||
default: [ Tacker ]
|
||||
flavour_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ simple ] ]
|
||||
default: simple
|
||||
flavour_description:
|
||||
type: string
|
||||
default: "flavour"
|
||||
requirements:
|
||||
- virtual_link_external1:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
- virtual_link_external2:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
type: tosca.interfaces.nfv.Vnflcm
|
|
@ -0,0 +1,145 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 sys
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
import paramiko
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CMD_TIMEOUT = 30
|
||||
SERVER_WAIT_COMPLETE_TIME = 60
|
||||
SSH_CONNECT_RETRY_COUNT = 4
|
||||
|
||||
|
||||
class SampleNewCoordinateVNFScript(object):
|
||||
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir, vdu_info):
|
||||
self.req = req
|
||||
self.inst = inst
|
||||
self.grant_req = grant_req
|
||||
self.grant = grant
|
||||
self.csar_dir = csar_dir
|
||||
self.vdu_info = vdu_info
|
||||
|
||||
def coordinate_vnf(self):
|
||||
# check ssh connect and os version
|
||||
"""(YiFeng) Add comment to check ssh access
|
||||
|
||||
The next part of code is to check connect VM via ssh.
|
||||
Since the zuul's network cannot check this content, so
|
||||
we comment this part of code. If you want to check them
|
||||
in your local environment, please uncomment.
|
||||
# user = self.vdu_info.get('vdu_param').get(
|
||||
# 'new_vnfc_param').get('username')
|
||||
# password = self.vdu_info.get('vdu_param').get(
|
||||
# 'new_vnfc_param').get('password')
|
||||
# host = self.vdu_info.get("ssh_ip"),
|
||||
# commander = self._init_commander(
|
||||
# user, password, host, retry=SSH_CONNECT_RETRY_COUNT)
|
||||
# ssh_command = 'cat /etc/os-release | grep PRETTY_NAME'
|
||||
# result = self._execute_command(commander, host, ssh_command)
|
||||
# os_version = result.get_stdout()[0].replace('\n', '').split('=')
|
||||
# LOG.info('The os version of this new VM is %s', os_version)
|
||||
"""
|
||||
# check image and flavour updated successfully
|
||||
vnfd = vnfd_utils.Vnfd(self.req.get('vnfdId'))
|
||||
vnfd.init_from_csar_dir(self.csar_dir)
|
||||
vdu_infos = common_script_utils.get_vdu_info(
|
||||
self.grant, self.inst, vnfd)
|
||||
|
||||
vdu_id = self.vdu_info.get('vdu_param').get('vdu_id')
|
||||
image = vdu_infos.get(vdu_id, {}).get('image')
|
||||
flavor = vdu_infos.get(vdu_id, {}).get('flavor')
|
||||
if self.vdu_info.get('new_image') != image or self.vdu_info.get(
|
||||
'new_flavor') != flavor:
|
||||
error = "The VM's image or flavour update failed'"
|
||||
LOG.error(error)
|
||||
raise sol_ex.VMRunningFailed(error)
|
||||
|
||||
def _init_commander(self, user, password, host, retry):
|
||||
while retry > 0:
|
||||
try:
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(
|
||||
host, username=user, password=password)
|
||||
LOG.info("Connected to %s", host)
|
||||
return ssh
|
||||
except paramiko.AuthenticationException as e:
|
||||
LOG.error("Authentication failed when connecting to %s",
|
||||
host)
|
||||
raise sol_ex.VMRunningFailed(e)
|
||||
except (paramiko.SSHException,
|
||||
paramiko.ssh_exception.NoValidConnectionsError) as e:
|
||||
LOG.debug(e)
|
||||
retry -= 1
|
||||
if retry == 0:
|
||||
LOG.error(e)
|
||||
raise sol_ex.VMRunningFailed(e)
|
||||
time.sleep(SERVER_WAIT_COMPLETE_TIME)
|
||||
|
||||
def _execute_command(self, commander, host, command):
|
||||
try:
|
||||
stdin, stdout, stderr = commander.exec_command(command)
|
||||
cmd_out = stdout.readlines()
|
||||
cmd_err = stderr.readlines()
|
||||
return_code = stdout.channel.recv_exit_status()
|
||||
except paramiko.SSHException as e:
|
||||
LOG.error("Command execution failed at %s. Giving up", host)
|
||||
raise e
|
||||
finally:
|
||||
commander.close()
|
||||
if return_code != 0:
|
||||
error = cmd_err
|
||||
raise sol_ex.VMRunningFailed(error_info=error)
|
||||
result = "cmd: %s, stdout: %s, stderr: %s, return code: %s" % (
|
||||
command, cmd_out, cmd_err, return_code)
|
||||
LOG.debug("Remote command execution result: %s", result)
|
||||
return cmd_out
|
||||
|
||||
|
||||
def main():
|
||||
operation = "coordinate_vnf"
|
||||
script_dict = pickle.load(sys.stdin.buffer)
|
||||
req = script_dict['request']
|
||||
inst = script_dict['vnf_instance']
|
||||
grant_req = script_dict['grant_request']
|
||||
grant = script_dict['grant_response']
|
||||
csar_dir = script_dict['tmp_csar_dir']
|
||||
vdu_info = script_dict['vdu_info']
|
||||
script = SampleNewCoordinateVNFScript(
|
||||
req, inst, grant_req, grant,
|
||||
csar_dir, vdu_info)
|
||||
try:
|
||||
getattr(script, operation)()
|
||||
except Exception:
|
||||
raise Exception
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,4 @@
|
|||
TOSCA-Meta-File-Version: 1.0
|
||||
CSAR-Version: 1.1
|
||||
Created-by: Onboarding portal
|
||||
Entry-Definitions: Definitions/change_vnf_pkg_top.vnfd.yaml
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.tests.functional.sol_v2 import paramgen
|
||||
from tacker.tests.functional.sol_v2 import utils
|
||||
|
||||
|
||||
zip_file_name = os.path.basename(os.path.abspath(".")) + '.zip'
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
|
||||
image_dir = "../../../../etc/samples/etsi/nfv/common/Files/images/"
|
||||
image_file = "cirros-0.5.2-x86_64-disk.img"
|
||||
image_path = os.path.abspath(image_dir + image_file)
|
||||
|
||||
utils.make_zip(".", tmp_dir, vnfd_id, image_path)
|
||||
|
||||
shutil.move(os.path.join(tmp_dir, zip_file_name), ".")
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
# if your sample is change VM from image to image update failed
|
||||
change_vnfpkg_req_update_failed = paramgen.change_vnfpkg(vnfd_id)
|
||||
del change_vnfpkg_req_update_failed['additionalParams']['vdu_params'][1]
|
||||
|
||||
with open("change_vnfpkg_req_update_failed", "w",
|
||||
encoding='utf-8') as f:
|
||||
f.write(json.dumps(change_vnfpkg_req_update_failed, indent=2))
|
|
@ -0,0 +1,53 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1_scale:
|
||||
type: OS::Heat::AutoScalingGroup
|
||||
properties:
|
||||
min_size: 1
|
||||
max_size: 3
|
||||
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
|
||||
resource:
|
||||
type: base_hot_nested_VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU1, vcImageId] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
|
||||
|
||||
VDU1_scale_out:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: 1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
VDU1_scale_in:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: -1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
fixed_ips:
|
||||
- ip_address: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, ip_address]}
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,27 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
image: { get_param: image }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1_scale:
|
||||
type: OS::Heat::AutoScalingGroup
|
||||
properties:
|
||||
min_size: 1
|
||||
max_size: 3
|
||||
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
|
||||
resource:
|
||||
type: base_hot_nested_VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image: { get_param: [ nfv, VDU, VDU1-VirtualStorage, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
|
||||
|
||||
VDU1_scale_out:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: 1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
VDU1_scale_in:
|
||||
type: OS::Heat::ScalingPolicy
|
||||
properties:
|
||||
scaling_adjustment: -1
|
||||
auto_scaling_group_id:
|
||||
get_resource: VDU1_scale
|
||||
adjustment_type: change_in_capacity
|
||||
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
block_device_mapping_v2: [{"volume_id": { get_resource: VDU2-VirtualStorage }}]
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
fixed_ips:
|
||||
- ip_address: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, ip_address]}
|
||||
|
||||
VDU2-VirtualStorage:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
image: { get_param: [ nfv, VDU, VDU2-VirtualStorage, vcImageId ] }
|
||||
size: 4
|
||||
volume_type: { get_resource: multi }
|
||||
multi:
|
||||
type: OS::Cinder::VolumeType
|
||||
properties:
|
||||
name: VDU2-multi
|
||||
metadata: { multiattach: "<is> True" }
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,39 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
block_device_mapping_v2: [{"volume_id": { get_resource: VDU1-VirtualStorage }}]
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
|
||||
VDU1-VirtualStorage:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
image: { get_param: image }
|
||||
size: 4
|
||||
volume_type: { get_resource: multi }
|
||||
multi:
|
||||
type: OS::Cinder::VolumeType
|
||||
properties:
|
||||
name: { get_resource: VDU1_CP1 }
|
||||
metadata: { multiattach: "<is> True" }
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Simple deployment flavour for Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: simple
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate: []
|
||||
instantiate_start: []
|
||||
instantiate_end: []
|
||||
terminate: []
|
||||
terminate_start: []
|
||||
terminate_end: []
|
||||
modify_information: []
|
||||
modify_information_start: []
|
||||
modify_information_end: []
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
sw_image_data:
|
||||
name: cirros-0.5.2-x86_64-disk
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
sw_image_data:
|
||||
name: cirros-0.5.2-x86_64-disk
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
|
||||
VDU1_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
|
||||
VDU2_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 2
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 3
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
|
@ -0,0 +1,229 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Simple deployment flavour for Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: volume
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate: []
|
||||
instantiate_start: []
|
||||
instantiate_end: []
|
||||
terminate: []
|
||||
terminate_start: []
|
||||
terminate_end: []
|
||||
modify_information: []
|
||||
modify_information_start: []
|
||||
modify_information_end: []
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
requirements:
|
||||
- virtual_storage: VDU1-VirtualStorage
|
||||
|
||||
VDU1-VirtualStorage:
|
||||
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
|
||||
properties:
|
||||
virtual_block_storage_data:
|
||||
size_of_storage: 4 GB
|
||||
rdma_enabled: true
|
||||
sw_image_data:
|
||||
name: cirros-0.5.2-x86_64-disk
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
capabilities:
|
||||
virtual_compute:
|
||||
properties:
|
||||
requested_additional_capabilities:
|
||||
properties:
|
||||
requested_additional_capability_name: m1.tiny
|
||||
support_mandatory: true
|
||||
target_performance_parameters:
|
||||
entry_schema: test
|
||||
virtual_memory:
|
||||
virtual_mem_size: 512 MB
|
||||
virtual_cpu:
|
||||
num_virtual_cpu: 1
|
||||
virtual_local_storage:
|
||||
- size_of_storage: 3 GB
|
||||
requirements:
|
||||
- virtual_storage: VDU2-VirtualStorage
|
||||
|
||||
VDU2-VirtualStorage:
|
||||
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
|
||||
properties:
|
||||
virtual_block_storage_data:
|
||||
size_of_storage: 4 GB
|
||||
rdma_enabled: true
|
||||
sw_image_data:
|
||||
name: cirros-0.5.2-x86_64-disk
|
||||
version: '0.5.2'
|
||||
checksum:
|
||||
algorithm: sha-256
|
||||
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
min_disk: 0 GB
|
||||
min_ram: 256 MB
|
||||
size: 2 GB
|
||||
|
||||
VDU1_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
|
||||
VDU2_CP1:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 0
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 2
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 3
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
|
@ -0,0 +1,32 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- change_vnf_pkg_types.yaml
|
||||
- change_vnf_pkg_old_image_df_simple.yaml
|
||||
- change_vnf_pkg_old_volume_df_simple.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
selected_flavour:
|
||||
type: string
|
||||
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: { get_input: selected_flavour }
|
||||
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
provider: Company
|
||||
product_name: Sample VNF
|
||||
software_version: '1.0'
|
||||
descriptor_version: '1.0'
|
||||
vnfm_info:
|
||||
- Tacker
|
||||
requirements:
|
||||
#- virtual_link_external # mapped in lower-level templates
|
||||
#- virtual_link_internal # mapped in lower-level templates
|
|
@ -0,0 +1,53 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: VNF type definition
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
|
||||
node_types:
|
||||
company.provider.VNF:
|
||||
derived_from: tosca.nodes.nfv.VNF
|
||||
properties:
|
||||
descriptor_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ]
|
||||
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
descriptor_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
provider:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Company' ] ]
|
||||
default: 'Company'
|
||||
product_name:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||
default: 'Sample VNF'
|
||||
software_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
constraints: [ valid_values: [ Tacker ] ]
|
||||
default: [ Tacker ]
|
||||
flavour_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ simple, volume ] ]
|
||||
default: simple
|
||||
flavour_description:
|
||||
type: string
|
||||
default: "flavour"
|
||||
requirements:
|
||||
- virtual_link_external1:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
- virtual_link_external2:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
type: tosca.interfaces.nfv.Vnflcm
|
|
@ -0,0 +1,145 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 sys
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
import paramiko
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CMD_TIMEOUT = 30
|
||||
SERVER_WAIT_COMPLETE_TIME = 60
|
||||
SSH_CONNECT_RETRY_COUNT = 4
|
||||
|
||||
|
||||
class SampleOldCoordinateVNFScript(object):
|
||||
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir, vdu_info):
|
||||
self.req = req
|
||||
self.inst = inst
|
||||
self.grant_req = grant_req
|
||||
self.grant = grant
|
||||
self.csar_dir = csar_dir
|
||||
self.vdu_info = vdu_info
|
||||
|
||||
def coordinate_vnf(self):
|
||||
# check ssh connect and os version
|
||||
"""(YiFeng) Add comment to check ssh access
|
||||
|
||||
The next part of code is to check connect VM via ssh.
|
||||
Since the zuul's network cannot check this content, so
|
||||
we comment this part of code. If you want to check them
|
||||
in your local environment, please uncomment.
|
||||
# user = self.vdu_info.get('vdu_param').get(
|
||||
# 'old_vnfc_param').get('username')
|
||||
# password = self.vdu_info.get('vdu_param').get(
|
||||
# 'old_vnfc_param').get('password')
|
||||
# host = self.vdu_info.get("ssh_ip"),
|
||||
# commander = self._init_commander(
|
||||
# user, password, host, retry=SSH_CONNECT_RETRY_COUNT)
|
||||
# ssh_command = 'cat /etc/os-release | grep PRETTY_NAME'
|
||||
# result = self._execute_command(commander, host, ssh_command)
|
||||
# os_version = result.get_stdout()[0].replace('\n', '').split('=')
|
||||
# LOG.info('The os version of this new VM is %s', os_version)
|
||||
"""
|
||||
# check image and flavour updated successfully
|
||||
vnfd = vnfd_utils.Vnfd(self.req.get('vnfdId'))
|
||||
vnfd.init_from_csar_dir(self.csar_dir)
|
||||
vdu_infos = common_script_utils.get_vdu_info(
|
||||
self.grant, self.inst, vnfd)
|
||||
|
||||
vdu_id = self.vdu_info.get('vdu_param').get('vdu_id')
|
||||
image = vdu_infos.get(vdu_id, {}).get('image')
|
||||
flavor = vdu_infos.get(vdu_id, {}).get('flavor')
|
||||
if self.vdu_info.get('new_image') != image or self.vdu_info.get(
|
||||
'new_flavor') != flavor:
|
||||
error = "The VM's image or flavour update failed'"
|
||||
LOG.error(error)
|
||||
raise sol_ex.VMRunningFailed(error)
|
||||
|
||||
def _init_commander(self, user, password, host, retry):
|
||||
while retry > 0:
|
||||
try:
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(
|
||||
host, username=user, password=password)
|
||||
LOG.info("Connected to %s", host)
|
||||
return ssh
|
||||
except paramiko.AuthenticationException as e:
|
||||
LOG.error("Authentication failed when connecting to %s",
|
||||
host)
|
||||
raise sol_ex.VMRunningFailed(e)
|
||||
except (paramiko.SSHException,
|
||||
paramiko.ssh_exception.NoValidConnectionsError) as e:
|
||||
LOG.debug(e)
|
||||
retry -= 1
|
||||
if retry == 0:
|
||||
LOG.error(e)
|
||||
raise sol_ex.VMRunningFailed(e)
|
||||
time.sleep(SERVER_WAIT_COMPLETE_TIME)
|
||||
|
||||
def _execute_command(self, commander, host, command):
|
||||
try:
|
||||
stdin, stdout, stderr = commander.exec_command(command)
|
||||
cmd_out = stdout.readlines()
|
||||
cmd_err = stderr.readlines()
|
||||
return_code = stdout.channel.recv_exit_status()
|
||||
except paramiko.SSHException as e:
|
||||
LOG.error("Command execution failed at %s. Giving up", host)
|
||||
raise e
|
||||
finally:
|
||||
commander.close()
|
||||
if return_code != 0:
|
||||
error = cmd_err
|
||||
raise sol_ex.VMRunningFailed(error_info=error)
|
||||
result = "cmd: %s, stdout: %s, stderr: %s, return code: %s" % (
|
||||
command, cmd_out, cmd_err, return_code)
|
||||
LOG.debug("Remote command execution result: %s", result)
|
||||
return cmd_out
|
||||
|
||||
|
||||
def main():
|
||||
operation = "coordinate_vnf"
|
||||
script_dict = pickle.load(sys.stdin.buffer)
|
||||
req = script_dict['request']
|
||||
inst = script_dict['vnf_instance']
|
||||
grant_req = script_dict['grant_request']
|
||||
grant = script_dict['grant_response']
|
||||
csar_dir = script_dict['tmp_csar_dir']
|
||||
vdu_info = script_dict['vdu_info']
|
||||
script = SampleOldCoordinateVNFScript(
|
||||
req, inst, grant_req, grant,
|
||||
csar_dir, vdu_info)
|
||||
try:
|
||||
getattr(script, operation)()
|
||||
except Exception:
|
||||
raise Exception
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,4 @@
|
|||
TOSCA-Meta-File-Version: 1.0
|
||||
CSAR-Version: 1.1
|
||||
Created-by: Onboarding portal
|
||||
Entry-Definitions: Definitions/change_vnf_pkg_top.vnfd.yaml
|
|
@ -0,0 +1,85 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.tests.functional.sol_v2 import paramgen
|
||||
from tacker.tests.functional.sol_v2 import utils
|
||||
|
||||
|
||||
zip_file_name = os.path.basename(os.path.abspath(".")) + '.zip'
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
|
||||
image_dir = "../../../../etc/samples/etsi/nfv/common/Files/images/"
|
||||
image_file = "cirros-0.5.2-x86_64-disk.img"
|
||||
image_path = os.path.abspath(image_dir + image_file)
|
||||
|
||||
utils.make_zip(".", tmp_dir, vnfd_id, image_path)
|
||||
|
||||
shutil.move(os.path.join(tmp_dir, zip_file_name), ".")
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
create_req = paramgen.change_vnfpkg_create(vnfd_id)
|
||||
terminate_req = paramgen.terminate_vnf_min()
|
||||
|
||||
net_ids = utils.get_network_ids(['net0'])
|
||||
subnet_ids = utils.get_subnet_ids(['subnet0'])
|
||||
|
||||
# if your sample is change VM from image to image
|
||||
instantiate_req_from_image_to_image = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, "http://localhost/identity/v3")
|
||||
|
||||
# if your sample is change VM from volume to image
|
||||
instantiate_req_from_volume_to_image = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, "http://localhost/identity/v3", flavor_id='volume')
|
||||
|
||||
# if your sample is change VM from image to volume
|
||||
instantiate_req_from_image_to_volume = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, "http://localhost/identity/v3")
|
||||
|
||||
# if your sample is change VM from volume to volume
|
||||
instantiate_req_from_volume_to_volume = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, "http://localhost/identity/v3", flavor_id='volume')
|
||||
|
||||
# if your sample is change VM from image to image update failed
|
||||
instantiate_req_update_failed = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, "http://localhost/identity/v3")
|
||||
|
||||
with open("create_req", "w") as f:
|
||||
f.write(json.dumps(create_req, indent=2))
|
||||
|
||||
with open("terminate_req", "w") as f:
|
||||
f.write(json.dumps(terminate_req, indent=2))
|
||||
|
||||
with open("instantiate_req_from_image_to_image", "w") as f:
|
||||
f.write(json.dumps(instantiate_req_from_image_to_image, indent=2))
|
||||
|
||||
with open("instantiate_req_from_volume_to_image", "w") as f:
|
||||
f.write(json.dumps(instantiate_req_from_volume_to_image, indent=2))
|
||||
|
||||
with open("instantiate_req_from_image_to_volume", "w") as f:
|
||||
f.write(json.dumps(instantiate_req_from_image_to_volume, indent=2))
|
||||
|
||||
with open("instantiate_req_from_volume_to_volume", "w") as f:
|
||||
f.write(json.dumps(instantiate_req_from_volume_to_volume, indent=2))
|
||||
|
||||
with open("instantiate_req_update_failed", "w") as f:
|
||||
f.write(json.dumps(instantiate_req_update_failed, indent=2))
|
|
@ -0,0 +1,644 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# 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 ddt
|
||||
import os
|
||||
import time
|
||||
|
||||
from tacker.tests.functional.sol_v2 import base_v2
|
||||
from tacker.tests.functional.sol_v2 import paramgen
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ChangeVnfPkgVnfLcmTest(base_v2.BaseSolV2Test):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(ChangeVnfPkgVnfLcmTest, cls).setUpClass()
|
||||
|
||||
cur_dir = os.path.dirname(__file__)
|
||||
# tacker/tests/etc...
|
||||
# /functional/sol_v2
|
||||
image_dir = os.path.join(
|
||||
cur_dir, "../../etc/samples/etsi/nfv/common/Files/images")
|
||||
image_file = "cirros-0.5.2-x86_64-disk.img"
|
||||
image_path = os.path.abspath(os.path.join(image_dir, image_file))
|
||||
|
||||
change_vnfpkg_from_image_to_image_path = os.path.join(
|
||||
cur_dir, "samples/test_instantiate_vnf_with_old_image_or_volume")
|
||||
cls.vnf_pkg_1, cls.vnfd_id_1 = cls.create_vnf_package(
|
||||
change_vnfpkg_from_image_to_image_path)
|
||||
|
||||
change_vnfpkg_from_image_to_image_path_2 = os.path.join(
|
||||
cur_dir, "samples/test_change_vnf_pkg_with_new_image")
|
||||
cls.vnf_pkg_2, cls.vnfd_id_2 = cls.create_vnf_package(
|
||||
change_vnfpkg_from_image_to_image_path_2, image_path=image_path)
|
||||
|
||||
change_vnfpkg_from_image_to_volume_path = os.path.join(
|
||||
cur_dir, "samples/test_change_vnf_pkg_with_new_volume")
|
||||
cls.vnf_pkg_3, cls.vnfd_id_3 = cls.create_vnf_package(
|
||||
change_vnfpkg_from_image_to_volume_path, image_path=image_path)
|
||||
|
||||
change_vnfpkg_failed_in_update_path = os.path.join(
|
||||
cur_dir, "samples/test_change_vnf_pkg_with_update_failed")
|
||||
cls.vnf_pkg_4, cls.vnfd_id_4 = cls.create_vnf_package(
|
||||
change_vnfpkg_failed_in_update_path, image_path=image_path)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(ChangeVnfPkgVnfLcmTest, cls).tearDownClass()
|
||||
|
||||
cls.delete_vnf_package(cls.vnf_pkg_1)
|
||||
cls.delete_vnf_package(cls.vnf_pkg_2)
|
||||
cls.delete_vnf_package(cls.vnf_pkg_3)
|
||||
cls.delete_vnf_package(cls.vnf_pkg_4)
|
||||
|
||||
def setUp(self):
|
||||
super(ChangeVnfPkgVnfLcmTest, self).setUp()
|
||||
|
||||
def test_change_vnfpkg_from_image_to_image(self):
|
||||
create_req = paramgen.change_vnfpkg_create(self.vnfd_id_1)
|
||||
resp, body = self.create_vnf_instance(create_req)
|
||||
expected_inst_attrs = [
|
||||
'id',
|
||||
'vnfInstanceName',
|
||||
'vnfInstanceDescription',
|
||||
'vnfdId',
|
||||
'vnfProvider',
|
||||
'vnfProductName',
|
||||
'vnfSoftwareVersion',
|
||||
'vnfdVersion',
|
||||
# 'vnfConfigurableProperties', # omitted
|
||||
# 'vimConnectionInfo', # omitted
|
||||
'instantiationState',
|
||||
# 'instantiatedVnfInfo', # omitted
|
||||
'metadata',
|
||||
# 'extensions', # omitted
|
||||
'_links'
|
||||
]
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, expected_inst_attrs)
|
||||
inst_id = body['id']
|
||||
|
||||
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
instantiate_req = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, self.auth_url)
|
||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
additional_inst_attrs = [
|
||||
'vimConnectionInfo',
|
||||
'instantiatedVnfInfo'
|
||||
]
|
||||
expected_inst_attrs.extend(additional_inst_attrs)
|
||||
resp_1, body_1 = self.show_vnf_instance(inst_id)
|
||||
stack_name = "vnf-{}".format(inst_id)
|
||||
stack_id = self.heat_client.get_stack_resource(stack_name)['stack'][
|
||||
'id']
|
||||
image_id_1 = self.get_current_vdu_image(stack_id, stack_name, 'VDU2')
|
||||
old_vnfd_id = body_1['vnfdId']
|
||||
|
||||
self.assertEqual(200, resp_1.status_code)
|
||||
self.check_resp_headers_in_get(resp_1)
|
||||
self.check_resp_body(body_1, expected_inst_attrs)
|
||||
|
||||
change_vnfpkg_req = paramgen.change_vnfpkg(self.vnfd_id_2)
|
||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
resp_2, body_2 = self.show_vnf_instance(inst_id)
|
||||
image_id_2 = self.get_current_vdu_image(stack_id, stack_name, 'VDU2')
|
||||
self.assertNotEqual(image_id_1, image_id_2)
|
||||
|
||||
self.assertEqual(200, resp_2.status_code)
|
||||
self.check_resp_headers_in_get(resp_2)
|
||||
self.check_resp_body(body_2, expected_inst_attrs)
|
||||
new_vnfd_id = [obj['metadata']['current_vnfd_id'] for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo']][0]
|
||||
|
||||
self.assertNotEqual(old_vnfd_id, new_vnfd_id)
|
||||
|
||||
terminate_req = paramgen.terminate_vnf_min()
|
||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(10)
|
||||
|
||||
resp, body = self.delete_vnf_instance(inst_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
def test_change_vnfpkg_from_image_to_volume(self):
|
||||
create_req = paramgen.change_vnfpkg_create(self.vnfd_id_1)
|
||||
resp, body = self.create_vnf_instance(create_req)
|
||||
expected_inst_attrs = [
|
||||
'id',
|
||||
'vnfInstanceName',
|
||||
'vnfInstanceDescription',
|
||||
'vnfdId',
|
||||
'vnfProvider',
|
||||
'vnfProductName',
|
||||
'vnfSoftwareVersion',
|
||||
'vnfdVersion',
|
||||
# 'vnfConfigurableProperties', # omitted
|
||||
# 'vimConnectionInfo', # omitted
|
||||
'instantiationState',
|
||||
# 'instantiatedVnfInfo', # omitted
|
||||
'metadata',
|
||||
# 'extensions', # omitted
|
||||
'_links'
|
||||
]
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, expected_inst_attrs)
|
||||
inst_id = body['id']
|
||||
|
||||
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
instantiate_req = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, self.auth_url)
|
||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
additional_inst_attrs = [
|
||||
'vimConnectionInfo',
|
||||
'instantiatedVnfInfo'
|
||||
]
|
||||
expected_inst_attrs.extend(additional_inst_attrs)
|
||||
resp_1, body_1 = self.show_vnf_instance(inst_id)
|
||||
stack_name = "vnf-{}".format(inst_id)
|
||||
stack_id = self.heat_client.get_stack_resource(stack_name)['stack'][
|
||||
'id']
|
||||
image_id_1 = self.get_current_vdu_image(stack_id, stack_name, 'VDU2')
|
||||
|
||||
self.assertEqual(200, resp_1.status_code)
|
||||
self.check_resp_headers_in_get(resp_1)
|
||||
self.check_resp_body(body_1, expected_inst_attrs)
|
||||
resource_ids_1 = [obj['id'] for obj in body_1[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2'][0]
|
||||
|
||||
change_vnfpkg_req = paramgen.change_vnfpkg(self.vnfd_id_3)
|
||||
del change_vnfpkg_req['additionalParams']['vdu_params'][0]
|
||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
resp_2, body_2 = self.show_vnf_instance(inst_id)
|
||||
image_id_2 = self.get_current_vdu_image(stack_id, stack_name, 'VDU2')
|
||||
storageResourceIds = [obj.get('storageResourceIds') for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2']
|
||||
self.assertIsNotNone(storageResourceIds)
|
||||
resource_ids_2 = [obj['id'] for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2'][0]
|
||||
self.assertNotEqual(resource_ids_1, resource_ids_2)
|
||||
self.assertNotEqual(image_id_1, image_id_2)
|
||||
|
||||
self.assertEqual(200, resp_2.status_code)
|
||||
self.check_resp_headers_in_get(resp_2)
|
||||
self.check_resp_body(body_2, expected_inst_attrs)
|
||||
|
||||
terminate_req = paramgen.terminate_vnf_min()
|
||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(10)
|
||||
|
||||
resp, body = self.delete_vnf_instance(inst_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
def test_change_vnfpkg_from_volume_to_image(self):
|
||||
create_req = paramgen.change_vnfpkg_create(self.vnfd_id_1)
|
||||
resp, body = self.create_vnf_instance(create_req)
|
||||
expected_inst_attrs = [
|
||||
'id',
|
||||
'vnfInstanceName',
|
||||
'vnfInstanceDescription',
|
||||
'vnfdId',
|
||||
'vnfProvider',
|
||||
'vnfProductName',
|
||||
'vnfSoftwareVersion',
|
||||
'vnfdVersion',
|
||||
# 'vnfConfigurableProperties', # omitted
|
||||
# 'vimConnectionInfo', # omitted
|
||||
'instantiationState',
|
||||
# 'instantiatedVnfInfo', # omitted
|
||||
'metadata',
|
||||
# 'extensions', # omitted
|
||||
'_links'
|
||||
]
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, expected_inst_attrs)
|
||||
inst_id = body['id']
|
||||
|
||||
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
instantiate_req = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, self.auth_url, flavor_id='volume')
|
||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
additional_inst_attrs = [
|
||||
'vimConnectionInfo',
|
||||
'instantiatedVnfInfo'
|
||||
]
|
||||
expected_inst_attrs.extend(additional_inst_attrs)
|
||||
resp_1, body_1 = self.show_vnf_instance(inst_id)
|
||||
stack_name = "vnf-{}".format(inst_id)
|
||||
stack_id = self.heat_client.get_stack_resource(stack_name)['stack'][
|
||||
'id']
|
||||
image_id_1 = self.get_current_vdu_image(stack_id, stack_name, 'VDU2')
|
||||
storageResourceIds_1 = [
|
||||
obj.get('storageResourceIds') for obj in body_1[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
if obj['vduId'] == 'VDU2']
|
||||
resource_ids_1 = [obj['id'] for obj in body_1[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2'][0]
|
||||
self.assertIsNotNone(storageResourceIds_1)
|
||||
|
||||
self.assertEqual(200, resp_1.status_code)
|
||||
self.check_resp_headers_in_get(resp_1)
|
||||
self.check_resp_body(body_1, expected_inst_attrs)
|
||||
|
||||
change_vnfpkg_req = paramgen.change_vnfpkg(self.vnfd_id_2)
|
||||
del change_vnfpkg_req['additionalParams']['vdu_params'][0]
|
||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
resp_2, body_2 = self.show_vnf_instance(inst_id)
|
||||
image_id_2 = self.get_current_vdu_image(stack_id, stack_name, 'VDU2')
|
||||
storageResourceIds_2 = [
|
||||
obj.get('storageResourceIds') for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
if obj['vduId'] == 'VDU2']
|
||||
resource_ids_2 = [obj['id'] for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2'][0]
|
||||
self.assertIsNone(storageResourceIds_2[0])
|
||||
self.assertNotEqual(image_id_1, image_id_2)
|
||||
self.assertNotEqual(resource_ids_1, resource_ids_2)
|
||||
|
||||
self.assertEqual(200, resp_2.status_code)
|
||||
self.check_resp_headers_in_get(resp_2)
|
||||
self.check_resp_body(body_2, expected_inst_attrs)
|
||||
|
||||
terminate_req = paramgen.terminate_vnf_min()
|
||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(10)
|
||||
|
||||
resp, body = self.delete_vnf_instance(inst_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
def test_change_vnfpkg_from_volume_to_volume(self):
|
||||
create_req = paramgen.change_vnfpkg_create(self.vnfd_id_1)
|
||||
resp, body = self.create_vnf_instance(create_req)
|
||||
expected_inst_attrs = [
|
||||
'id',
|
||||
'vnfInstanceName',
|
||||
'vnfInstanceDescription',
|
||||
'vnfdId',
|
||||
'vnfProvider',
|
||||
'vnfProductName',
|
||||
'vnfSoftwareVersion',
|
||||
'vnfdVersion',
|
||||
# 'vnfConfigurableProperties', # omitted
|
||||
# 'vimConnectionInfo', # omitted
|
||||
'instantiationState',
|
||||
# 'instantiatedVnfInfo', # omitted
|
||||
'metadata',
|
||||
# 'extensions', # omitted
|
||||
'_links'
|
||||
]
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, expected_inst_attrs)
|
||||
inst_id = body['id']
|
||||
|
||||
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
instantiate_req = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, self.auth_url, flavor_id='volume')
|
||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
additional_inst_attrs = [
|
||||
'vimConnectionInfo',
|
||||
'instantiatedVnfInfo'
|
||||
]
|
||||
expected_inst_attrs.extend(additional_inst_attrs)
|
||||
resp_1, body_1 = self.show_vnf_instance(inst_id)
|
||||
stack_name = "vnf-{}".format(inst_id)
|
||||
stack_id = self.heat_client.get_stack_resource(stack_name)['stack'][
|
||||
'id']
|
||||
image_id_1 = self.get_current_vdu_image(stack_id, stack_name, 'VDU2')
|
||||
storageResourceId_1 = [
|
||||
obj.get('storageResourceIds') for obj in body_1[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
if obj['vduId'] == 'VDU2']
|
||||
resource_ids_1 = [obj['id'] for obj in body_1[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2'][0]
|
||||
|
||||
self.assertEqual(200, resp_1.status_code)
|
||||
self.check_resp_headers_in_get(resp_1)
|
||||
self.check_resp_body(body_1, expected_inst_attrs)
|
||||
|
||||
change_vnfpkg_req = paramgen.change_vnfpkg(self.vnfd_id_3)
|
||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
resp_2, body_2 = self.show_vnf_instance(inst_id)
|
||||
image_id_2 = self.get_current_vdu_image(stack_id, stack_name, 'VDU2')
|
||||
storageResourceId_2 = [
|
||||
obj.get('storageResourceIds') for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
if obj['vduId'] == 'VDU2']
|
||||
resource_ids_2 = [obj['id'] for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2'][0]
|
||||
self.assertNotEqual(image_id_1, image_id_2)
|
||||
self.assertNotEqual(storageResourceId_1, storageResourceId_2)
|
||||
self.assertNotEqual(resource_ids_1, resource_ids_2)
|
||||
|
||||
self.assertEqual(200, resp_2.status_code)
|
||||
self.check_resp_headers_in_get(resp_2)
|
||||
self.check_resp_body(body_2, expected_inst_attrs)
|
||||
|
||||
terminate_req = paramgen.terminate_vnf_min()
|
||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(10)
|
||||
|
||||
resp, body = self.delete_vnf_instance(inst_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
def test_change_vnfpkg_failed_in_update(self):
|
||||
create_req = paramgen.change_vnfpkg_create(self.vnfd_id_1)
|
||||
resp, body = self.create_vnf_instance(create_req)
|
||||
expected_inst_attrs = [
|
||||
'id',
|
||||
'vnfInstanceName',
|
||||
'vnfInstanceDescription',
|
||||
'vnfdId',
|
||||
'vnfProvider',
|
||||
'vnfProductName',
|
||||
'vnfSoftwareVersion',
|
||||
'vnfdVersion',
|
||||
# 'vnfConfigurableProperties', # omitted
|
||||
# 'vimConnectionInfo', # omitted
|
||||
'instantiationState',
|
||||
# 'instantiatedVnfInfo', # omitted
|
||||
'metadata',
|
||||
# 'extensions', # omitted
|
||||
'_links'
|
||||
]
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, expected_inst_attrs)
|
||||
inst_id = body['id']
|
||||
|
||||
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
instantiate_req = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, self.auth_url)
|
||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
additional_inst_attrs = [
|
||||
'vimConnectionInfo',
|
||||
'instantiatedVnfInfo'
|
||||
]
|
||||
expected_inst_attrs.extend(additional_inst_attrs)
|
||||
resp_1, body_1 = self.show_vnf_instance(inst_id)
|
||||
old_vnfd_id = body_1['vnfdId']
|
||||
|
||||
self.assertEqual(200, resp_1.status_code)
|
||||
self.check_resp_headers_in_get(resp_1)
|
||||
self.check_resp_body(body_1, expected_inst_attrs)
|
||||
|
||||
change_vnfpkg_req = paramgen.change_vnfpkg(self.vnfd_id_4)
|
||||
del change_vnfpkg_req['additionalParams']['vdu_params'][1]
|
||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
||||
|
||||
resp, body = self.rollback(lcmocc_id)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.wait_lcmocc_rolled_back(lcmocc_id)
|
||||
|
||||
resp_2, body_2 = self.show_vnf_instance(inst_id)
|
||||
new_vnfd_id = [
|
||||
obj['metadata'].get('current_vnfd_id') for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
if obj['metadata'].get('current_vnfd_id')][0]
|
||||
self.assertEqual(old_vnfd_id, new_vnfd_id)
|
||||
|
||||
self.assertEqual(200, resp_2.status_code)
|
||||
self.check_resp_headers_in_get(resp_2)
|
||||
self.check_resp_body(body_2, expected_inst_attrs)
|
||||
|
||||
terminate_req = paramgen.terminate_vnf_min()
|
||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(10)
|
||||
|
||||
resp, body = self.delete_vnf_instance(inst_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
def test_change_vnfpkg_failed_with_error_coordinate_vnf(self):
|
||||
create_req = paramgen.change_vnfpkg_create(self.vnfd_id_1)
|
||||
resp, body = self.create_vnf_instance(create_req)
|
||||
expected_inst_attrs = [
|
||||
'id',
|
||||
'vnfInstanceName',
|
||||
'vnfInstanceDescription',
|
||||
'vnfdId',
|
||||
'vnfProvider',
|
||||
'vnfProductName',
|
||||
'vnfSoftwareVersion',
|
||||
'vnfdVersion',
|
||||
# 'vnfConfigurableProperties', # omitted
|
||||
# 'vimConnectionInfo', # omitted
|
||||
'instantiationState',
|
||||
# 'instantiatedVnfInfo', # omitted
|
||||
'metadata',
|
||||
# 'extensions', # omitted
|
||||
'_links'
|
||||
]
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, expected_inst_attrs)
|
||||
inst_id = body['id']
|
||||
|
||||
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
instantiate_req = paramgen.change_vnfpkg_instantiate(
|
||||
net_ids, subnet_ids, self.auth_url, flavor_id='volume')
|
||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
additional_inst_attrs = [
|
||||
'vimConnectionInfo',
|
||||
'instantiatedVnfInfo'
|
||||
]
|
||||
expected_inst_attrs.extend(additional_inst_attrs)
|
||||
resp_1, body_1 = self.show_vnf_instance(inst_id)
|
||||
storageResourceId_1 = [
|
||||
obj.get('storageResourceIds') for obj in body_1[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
if obj['vduId'] == 'VDU2']
|
||||
resource_ids_1 = [obj['id'] for obj in body_1[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2'][0]
|
||||
|
||||
self.assertEqual(200, resp_1.status_code)
|
||||
self.check_resp_headers_in_get(resp_1)
|
||||
self.check_resp_body(body_1, expected_inst_attrs)
|
||||
|
||||
change_vnfpkg_req = paramgen.change_vnfpkg(self.vnfd_id_3)
|
||||
change_vnfpkg_req['additionalParams'][
|
||||
'lcm-operation-coordinate-new-vnf'
|
||||
] = "./Scripts/error_coordinate_new_vnf.py"
|
||||
change_vnfpkg_req['additionalParams'][
|
||||
'lcm-operation-coordinate-new-vnf-class'
|
||||
] = "ErrorCoordinateNewVnf"
|
||||
del change_vnfpkg_req['additionalParams']['vdu_params'][0]
|
||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
||||
|
||||
resp, body = self.rollback(lcmocc_id)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.wait_lcmocc_rolled_back(lcmocc_id)
|
||||
|
||||
resp_2, body_2 = self.show_vnf_instance(inst_id)
|
||||
storageResourceId_2 = [
|
||||
obj.get('storageResourceIds') for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
if obj['vduId'] == 'VDU2']
|
||||
resource_ids_2 = [obj['id'] for obj in body_2[
|
||||
'instantiatedVnfInfo']['vnfcResourceInfo'] if obj[
|
||||
'vduId'] == 'VDU2'][0]
|
||||
|
||||
self.assertEqual(200, resp_2.status_code)
|
||||
self.check_resp_headers_in_get(resp_2)
|
||||
self.check_resp_body(body_2, expected_inst_attrs)
|
||||
self.assertNotEqual(storageResourceId_1, storageResourceId_2)
|
||||
self.assertNotEqual(resource_ids_1, resource_ids_2)
|
||||
|
||||
terminate_req = paramgen.terminate_vnf_min()
|
||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(10)
|
||||
|
||||
resp, body = self.delete_vnf_instance(inst_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
|
@ -762,15 +762,15 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
|
|||
stack_name_1 = href.split("/")[7]
|
||||
break
|
||||
|
||||
_, port_info = self.heat_client.get_resource_info(
|
||||
stack_name_1, stack_id_1, 'VDU1_CP1')
|
||||
port_info = self.heat_client.get_resource_info(
|
||||
f"{stack_name_1}/{stack_id_1}", 'VDU1_CP1')
|
||||
before_physical_resource_id_1 = port_info['physical_resource_id']
|
||||
before_fixed_ips_1 = port_info['attributes']['fixed_ips']
|
||||
|
||||
stack_id_2 = self.heat_client.get_stack_resource(stack_name)['stack'][
|
||||
'id']
|
||||
_, port_info = self.heat_client.get_resource_info(
|
||||
stack_name, stack_id_2, 'VDU2_CP2')
|
||||
port_info = self.heat_client.get_resource_info(
|
||||
f"{stack_name}/{stack_id_2}", 'VDU2_CP2')
|
||||
before_physical_resource_id_2 = port_info['physical_resource_id']
|
||||
before_fixed_ips_2 = port_info['attributes']['fixed_ips']
|
||||
|
||||
|
@ -782,15 +782,15 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
|
|||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
_, port_info = self.heat_client.get_resource_info(
|
||||
stack_name_1, stack_id_1, 'VDU1_CP1')
|
||||
port_info = self.heat_client.get_resource_info(
|
||||
f"{stack_name_1}/{stack_id_1}", 'VDU1_CP1')
|
||||
after_physical_resource_id_1 = port_info['physical_resource_id']
|
||||
after_fixed_ips_1 = port_info['attributes']['fixed_ips']
|
||||
|
||||
stack_id_2 = self.heat_client.get_stack_resource(stack_name)['stack'][
|
||||
'id']
|
||||
_, port_info = self.heat_client.get_resource_info(
|
||||
stack_name, stack_id_2, 'VDU2_CP2')
|
||||
port_info = self.heat_client.get_resource_info(
|
||||
f"{stack_name}/{stack_id_2}", 'VDU2_CP2')
|
||||
after_physical_resource_id_2 = port_info['physical_resource_id']
|
||||
after_fixed_ips_2 = port_info['attributes']['fixed_ips']
|
||||
|
||||
|
@ -2859,8 +2859,8 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
|
|||
# 14. Change external connectivity
|
||||
stack_id = self.heat_client.get_stack_resource(stack_name)['stack'][
|
||||
'id']
|
||||
_, port_info = self.heat_client.get_resource_info(
|
||||
stack_name, stack_id, 'VDU2_CP2')
|
||||
port_info = self.heat_client.get_resource_info(
|
||||
f"{stack_name}/{stack_id}", 'VDU2_CP2')
|
||||
before_physical_resource_id = port_info['physical_resource_id']
|
||||
before_fixed_ips = port_info['attributes']['fixed_ips']
|
||||
|
||||
|
@ -2871,8 +2871,8 @@ class VnfLcmTest(base_v2.BaseSolV2Test):
|
|||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
_, port_info = self.heat_client.get_resource_info(
|
||||
stack_name, stack_id, 'VDU2_CP2')
|
||||
port_info = self.heat_client.get_resource_info(
|
||||
f"{stack_name}/{stack_id}", 'VDU2_CP2')
|
||||
after_physical_resource_id = port_info['physical_resource_id']
|
||||
after_fixed_ips = port_info['attributes']['fixed_ips']
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
import os
|
||||
|
||||
from tacker.sol_refactored.infra_drivers.openstack import userdata_utils
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.tests import base
|
||||
|
||||
|
||||
|
@ -23,14 +23,14 @@ SAMPLE_VNFD_ID = "b1bb0ce7-ebca-4fa7-95ed-4840d7000000"
|
|||
SAMPLE_FLAVOUR_ID = "simple"
|
||||
|
||||
|
||||
class TestUserDataUtils(base.BaseTestCase):
|
||||
class TestCommontScriptUtils(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestUserDataUtils, self).setUp()
|
||||
super(TestCommontScriptUtils, self).setUp()
|
||||
cur_dir = os.path.dirname(__file__)
|
||||
sample_dir = os.path.join(cur_dir, "../..", "samples")
|
||||
sample_dir = os.path.join(cur_dir, "..", "samples")
|
||||
|
||||
self.vnfd_1 = userdata_utils.get_vnfd(SAMPLE_VNFD_ID,
|
||||
self.vnfd_1 = common_script_utils.get_vnfd(SAMPLE_VNFD_ID,
|
||||
os.path.join(sample_dir, "sample1"))
|
||||
|
||||
def test_init_nfv_dict(self):
|
||||
|
@ -54,7 +54,7 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
'subnet': None}}}
|
||||
}
|
||||
}
|
||||
result = userdata_utils.init_nfv_dict(top_hot)
|
||||
result = common_script_utils.init_nfv_dict(top_hot)
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
def test_get_param_flavor(self):
|
||||
|
@ -68,12 +68,14 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
}
|
||||
}
|
||||
|
||||
result = userdata_utils.get_param_flavor('VDU1', SAMPLE_FLAVOUR_ID,
|
||||
result = common_script_utils.get_param_flavor(
|
||||
'VDU1', SAMPLE_FLAVOUR_ID,
|
||||
self.vnfd_1, grant)
|
||||
self.assertEqual(flavor, result)
|
||||
|
||||
# if not exist in grant, get from VNFD
|
||||
result = userdata_utils.get_param_flavor('VDU2', SAMPLE_FLAVOUR_ID,
|
||||
result = common_script_utils.get_param_flavor(
|
||||
'VDU2', SAMPLE_FLAVOUR_ID,
|
||||
self.vnfd_1, grant)
|
||||
self.assertEqual('m1.tiny', result)
|
||||
|
||||
|
@ -90,7 +92,7 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
}
|
||||
}
|
||||
|
||||
result = userdata_utils.get_param_image('VDU2', SAMPLE_FLAVOUR_ID,
|
||||
result = common_script_utils.get_param_image('VDU2', SAMPLE_FLAVOUR_ID,
|
||||
self.vnfd_1, grant)
|
||||
self.assertEqual(image_id, result)
|
||||
|
||||
|
@ -113,7 +115,7 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
]
|
||||
}
|
||||
|
||||
result = userdata_utils.get_param_zone('VDU1', grant_req, grant)
|
||||
result = common_script_utils.get_param_zone('VDU1', grant_req, grant)
|
||||
self.assertEqual('nova', result)
|
||||
|
||||
def test_get_param_capacity(self):
|
||||
|
@ -145,9 +147,11 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
}
|
||||
}
|
||||
|
||||
result = userdata_utils.get_param_capacity('VDU1', inst, grant_req)
|
||||
result = common_script_utils.get_param_capacity(
|
||||
'VDU1', inst, grant_req)
|
||||
self.assertEqual(2, result)
|
||||
result = userdata_utils.get_param_capacity('VDU2', inst, grant_req)
|
||||
result = common_script_utils.get_param_capacity(
|
||||
'VDU2', inst, grant_req)
|
||||
self.assertEqual(1, result)
|
||||
|
||||
def test_get_parama_network(self):
|
||||
|
@ -167,7 +171,7 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
]
|
||||
}
|
||||
|
||||
result = userdata_utils.get_param_network('VDU1_CP1', {}, req)
|
||||
result = common_script_utils.get_param_network('VDU1_CP1', {}, req)
|
||||
self.assertEqual(res_id, result)
|
||||
|
||||
def test_get_param_fixed_ips(self):
|
||||
|
@ -207,7 +211,7 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
}
|
||||
expected_result = [{'ip_address': ip_address, 'subnet': subnet_id}]
|
||||
|
||||
result = userdata_utils.get_param_fixed_ips('VDU2_CP2', {}, req)
|
||||
result = common_script_utils.get_param_fixed_ips('VDU2_CP2', {}, req)
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
def _inst_example_get_network_fixed_ips_from_inst(self):
|
||||
|
@ -252,7 +256,8 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
def test_get_parama_network_from_inst(self):
|
||||
inst = self._inst_example_get_network_fixed_ips_from_inst()
|
||||
|
||||
result = userdata_utils.get_param_network_from_inst('VDU2_CP2', inst)
|
||||
result = common_script_utils.get_param_network_from_inst(
|
||||
'VDU2_CP2', inst)
|
||||
self.assertEqual("ext_vl_res_id", result)
|
||||
|
||||
def test_get_param_fixed_ips_from_inst(self):
|
||||
|
@ -260,7 +265,8 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
|
||||
expected_result = [{'ip_address': 'ip_address', 'subnet': 'subnet_id'}]
|
||||
|
||||
result = userdata_utils.get_param_fixed_ips_from_inst('VDU2_CP2', inst)
|
||||
result = common_script_utils.get_param_fixed_ips_from_inst(
|
||||
'VDU2_CP2', inst)
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
def test_apply_ext_managed_vls(self):
|
||||
|
@ -289,7 +295,7 @@ class TestUserDataUtils(base.BaseTestCase):
|
|||
self.assertIn(vl, top_hot['resources'])
|
||||
self.assertIn(vl_subnet, top_hot['resources'])
|
||||
|
||||
userdata_utils.apply_ext_managed_vls(top_hot, req, {})
|
||||
common_script_utils.apply_ext_managed_vls(top_hot, req, {})
|
||||
|
||||
# check after
|
||||
# replaced to resource id
|
|
@ -914,6 +914,7 @@ _inst_info_example_4 = {
|
|||
"extVirtualLinkInfo": _inst_info_example_2["extVirtualLinkInfo"],
|
||||
# network resource is not changed but ports are re-receated.
|
||||
# this is for check of SOL003 all=True case.
|
||||
|
||||
"extManagedVirtualLinkInfo": [
|
||||
{
|
||||
"id": "res_id_internalVL1",
|
||||
|
@ -1099,6 +1100,257 @@ _inst_info_example_4 = {
|
|||
# "vnfcInfo": omitted
|
||||
}
|
||||
|
||||
# example_5 is update VDU1 and VDU2's info from example_1.
|
||||
_inst_info_example_5 = {
|
||||
# "flavourId", "vnfState", "scaleStatus", "maxScaleLevels" are omitted
|
||||
# "extCpInfo": omitted
|
||||
"extVirtualLinkInfo": [
|
||||
{
|
||||
"id": "bbf0932a-6142-4ea8-93cd-8059dba594a1",
|
||||
"resourceHandle": {
|
||||
"resourceId": "3529d333-dbcc-4d93-9b64-210647712569"
|
||||
},
|
||||
"extLinkPorts": [
|
||||
{
|
||||
"id": "res_id_VDU2_CP1",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU2_CP1",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "cp-res_id_VDU2_CP1"
|
||||
},
|
||||
{
|
||||
"id": "res_id_VDU1_CP1_1",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU1_CP1_1",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "cp-res_id_VDU1_CP1_1"
|
||||
}
|
||||
],
|
||||
# "currentVnfExtCpData": omitted
|
||||
},
|
||||
{
|
||||
"id": "790949df-c7b3-4926-a559-3895412f1dfe",
|
||||
"resourceHandle": {
|
||||
"resourceId": "367e5b3b-34dc-47f2-85b8-c39e3272893a"
|
||||
},
|
||||
"extLinkPorts": [
|
||||
{
|
||||
"id": "res_id_VDU2_CP2",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU2_CP2",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "cp-res_id_VDU2_CP2"
|
||||
},
|
||||
{
|
||||
"id": "res_id_VDU1_CP2_1",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU1_CP2_1",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "cp-res_id_VDU1_CP2_1"
|
||||
}
|
||||
],
|
||||
# "currentVnfExtCpData": omitted
|
||||
}
|
||||
],
|
||||
"extManagedVirtualLinkInfo": [
|
||||
{
|
||||
"id": "res_id_internalVL1",
|
||||
"vnfVirtualLinkDescId": "internalVL1",
|
||||
"networkResource": {
|
||||
"resourceId": "res_id_internalVL1"
|
||||
},
|
||||
"vnfLinkPorts": [
|
||||
{
|
||||
"id": "res_id_VDU2_CP3",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU2_CP3",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "VDU2_CP3-res_id_VDU2",
|
||||
"cpInstanceType": "VNFC_CP"
|
||||
},
|
||||
{
|
||||
"id": "res_id_VDU1_CP3_1",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU1_CP3_1",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "VDU1_CP3-res_id_VDU1_1",
|
||||
"cpInstanceType": "VNFC_CP"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"vnfcResourceInfo": [
|
||||
{
|
||||
"id": "res_id_VDU1_1_update",
|
||||
"vduId": "VDU1",
|
||||
"computeResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU1_1_update",
|
||||
"vimLevelResourceType": "OS::Nova::Server"
|
||||
},
|
||||
"metadata": {
|
||||
"current_vnfd_id": 'new_vnfd_id'
|
||||
},
|
||||
"storageResourceIds": [
|
||||
"new_res_id_VirtualStorage_1"
|
||||
],
|
||||
"vnfcCpInfo": [
|
||||
{
|
||||
"id": "VDU1_CP1-res_id_VDU1_1",
|
||||
"cpdId": "VDU1_CP1",
|
||||
"vnfExtCpId": "cp-res_id_VDU1_CP1_1"
|
||||
},
|
||||
{
|
||||
"id": "VDU1_CP2-res_id_VDU1_1",
|
||||
"cpdId": "VDU1_CP2",
|
||||
"vnfExtCpId": "cp-res_id_VDU1_CP2_1"
|
||||
},
|
||||
{
|
||||
"id": "VDU1_CP3-res_id_VDU1_1",
|
||||
"cpdId": "VDU1_CP3",
|
||||
"vnfLinkPortId": "res_id_VDU1_CP3_1"
|
||||
},
|
||||
{
|
||||
"id": "VDU1_CP4-res_id_VDU1_1",
|
||||
"cpdId": "VDU1_CP4",
|
||||
"vnfLinkPortId": "res_id_VDU1_CP4_1"
|
||||
},
|
||||
{
|
||||
"id": "VDU1_CP5-res_id_VDU1_1",
|
||||
"cpdId": "VDU1_CP5",
|
||||
"vnfLinkPortId": "res_id_VDU1_CP5_1"
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "res_id_VDU2",
|
||||
"vduId": "VDU2",
|
||||
"computeResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU2",
|
||||
"vimLevelResourceType": "OS::Nova::Server"
|
||||
},
|
||||
"metadata": {
|
||||
"current_vnfd_id": 'new_vnfd_id'
|
||||
},
|
||||
"vnfcCpInfo": [
|
||||
{
|
||||
"id": "VDU2_CP1-res_id_VDU2",
|
||||
"cpdId": "VDU2_CP1",
|
||||
"vnfExtCpId": "cp-res_id_VDU2_CP1"
|
||||
},
|
||||
{
|
||||
"id": "VDU2_CP2-res_id_VDU2",
|
||||
"cpdId": "VDU2_CP2",
|
||||
"vnfExtCpId": "cp-res_id_VDU2_CP2"
|
||||
},
|
||||
{
|
||||
"id": "VDU2_CP3-res_id_VDU2",
|
||||
"cpdId": "VDU2_CP3",
|
||||
"vnfLinkPortId": "res_id_VDU2_CP3"
|
||||
},
|
||||
{
|
||||
"id": "VDU2_CP4-res_id_VDU2",
|
||||
"cpdId": "VDU2_CP4",
|
||||
"vnfLinkPortId": "res_id_VDU2_CP4"
|
||||
},
|
||||
{
|
||||
"id": "VDU2_CP5-res_id_VDU2",
|
||||
"cpdId": "VDU2_CP5",
|
||||
"vnfLinkPortId": "res_id_VDU2_CP5"
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
"vnfVirtualLinkResourceInfo": [
|
||||
{
|
||||
"id": "res_id_internalVL3",
|
||||
"vnfVirtualLinkDescId": "internalVL3",
|
||||
"networkResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_internalVL3",
|
||||
"vimLevelResourceType": "OS::Neutron::Net"
|
||||
},
|
||||
"vnfLinkPorts": [
|
||||
{
|
||||
"id": "res_id_VDU2_CP5",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU2_CP5",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "VDU2_CP5-res_id_VDU2",
|
||||
"cpInstanceType": "VNFC_CP"
|
||||
},
|
||||
{
|
||||
"id": "res_id_VDU1_CP5_1",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU1_CP5_1",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "VDU1_CP5-res_id_VDU1_1",
|
||||
"cpInstanceType": "VNFC_CP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "res_id_internalVL2",
|
||||
"vnfVirtualLinkDescId": "internalVL2",
|
||||
"networkResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_internalVL2",
|
||||
"vimLevelResourceType": "OS::Neutron::Net"
|
||||
},
|
||||
"vnfLinkPorts": [
|
||||
{
|
||||
"id": "res_id_VDU2_CP4",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU2_CP4",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "VDU2_CP4-res_id_VDU2",
|
||||
"cpInstanceType": "VNFC_CP"
|
||||
},
|
||||
{
|
||||
"id": "res_id_VDU1_CP4_1",
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU1_CP4_1",
|
||||
"vimLevelResourceType": "OS::Neutron::Port"
|
||||
},
|
||||
"cpInstanceId": "VDU1_CP4-res_id_VDU1_1",
|
||||
"cpInstanceType": "VNFC_CP"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"virtualStorageResourceInfo": [
|
||||
{
|
||||
"id": "new_res_id_VirtualStorage_1",
|
||||
"virtualStorageDescId": "VirtualStorage",
|
||||
"storageResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "new_res_id_VirtualStorage_1",
|
||||
"vimLevelResourceType": "OS::Cinder::Volume"
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
# expected results
|
||||
_expected_resource_changes_instantiate = {
|
||||
"affectedVnfcs": [
|
||||
|
@ -1878,6 +2130,95 @@ _expected_resource_changes_heal = {
|
|||
}
|
||||
]
|
||||
}
|
||||
_expected_resource_changes_change_vnfpkg = {
|
||||
"affectedVnfcs": [
|
||||
{
|
||||
"id": "res_id_VDU1_1",
|
||||
"vduId": "VDU1",
|
||||
"changeType": "REMOVED",
|
||||
"computeResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU1_1",
|
||||
"vimLevelResourceType": "OS::Nova::Server"
|
||||
},
|
||||
"affectedVnfcCpIds": [
|
||||
"VDU1_CP1-res_id_VDU1_1",
|
||||
"VDU1_CP2-res_id_VDU1_1",
|
||||
"VDU1_CP3-res_id_VDU1_1",
|
||||
"VDU1_CP4-res_id_VDU1_1",
|
||||
"VDU1_CP5-res_id_VDU1_1"
|
||||
],
|
||||
"removedStorageResourceIds": [
|
||||
"res_id_VirtualStorage_1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "res_id_VDU1_1_update",
|
||||
"vduId": "VDU1",
|
||||
"changeType": "ADDED",
|
||||
"computeResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU1_1_update",
|
||||
"vimLevelResourceType": "OS::Nova::Server"
|
||||
},
|
||||
"affectedVnfcCpIds": [
|
||||
"VDU1_CP1-res_id_VDU1_1",
|
||||
"VDU1_CP2-res_id_VDU1_1",
|
||||
"VDU1_CP3-res_id_VDU1_1",
|
||||
"VDU1_CP4-res_id_VDU1_1",
|
||||
"VDU1_CP5-res_id_VDU1_1"
|
||||
],
|
||||
"addedStorageResourceIds": [
|
||||
"new_res_id_VirtualStorage_1"
|
||||
],
|
||||
'metadata': {
|
||||
'current_vnfd_id': 'new_vnfd_id'
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "res_id_VDU2",
|
||||
"vduId": "VDU2",
|
||||
"changeType": "MODIFIED",
|
||||
"computeResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VDU2",
|
||||
"vimLevelResourceType": "OS::Nova::Server"
|
||||
},
|
||||
"affectedVnfcCpIds": [
|
||||
"VDU2_CP1-res_id_VDU2",
|
||||
"VDU2_CP2-res_id_VDU2",
|
||||
"VDU2_CP3-res_id_VDU2",
|
||||
"VDU2_CP4-res_id_VDU2",
|
||||
"VDU2_CP5-res_id_VDU2"
|
||||
],
|
||||
'metadata': {
|
||||
'current_vnfd_id': 'new_vnfd_id'
|
||||
}
|
||||
}
|
||||
],
|
||||
"affectedVirtualStorages": [
|
||||
{
|
||||
"id": "new_res_id_VirtualStorage_1",
|
||||
"virtualStorageDescId": "VirtualStorage",
|
||||
"changeType": "ADDED",
|
||||
"storageResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "new_res_id_VirtualStorage_1",
|
||||
"vimLevelResourceType": "OS::Cinder::Volume"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "res_id_VirtualStorage_1",
|
||||
"virtualStorageDescId": "VirtualStorage",
|
||||
"changeType": "REMOVED",
|
||||
"storageResource": {
|
||||
"vimConnectionId": "vim_connection_id",
|
||||
"resourceId": "res_id_VirtualStorage_1",
|
||||
"vimLevelResourceType": "OS::Cinder::Volume"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class TestLcmOpOccUtils(base.BaseTestCase):
|
||||
|
@ -2061,3 +2402,25 @@ class TestLcmOpOccUtils(base.BaseTestCase):
|
|||
self.assertEqual(
|
||||
_expected_resource_changes_heal,
|
||||
self._sort_resource_changes(lcmocc['resourceChanges']))
|
||||
|
||||
def test_update_lcmocc_change_vnfpkg(self):
|
||||
# prepare
|
||||
inst_saved = objects.VnfInstanceV2()
|
||||
inst_saved.instantiatedVnfInfo = (
|
||||
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
|
||||
_inst_info_example_1))
|
||||
inst_saved.vnfdId = 'old_vnfd_id'
|
||||
inst = objects.VnfInstanceV2()
|
||||
inst.instantiatedVnfInfo = (
|
||||
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
|
||||
_inst_info_example_5))
|
||||
lcmocc = objects.VnfLcmOpOccV2(operation='CHANGE_VNFPKG')
|
||||
|
||||
# execute update_lcmocc
|
||||
lcmocc_utils.update_lcmocc(lcmocc, inst_saved, inst)
|
||||
|
||||
# check resourceChanges
|
||||
lcmocc = lcmocc.to_dict()
|
||||
self.assertEqual(
|
||||
_expected_resource_changes_change_vnfpkg,
|
||||
self._sort_resource_changes(lcmocc['resourceChanges']))
|
||||
|
|
|
@ -70,6 +70,36 @@ class TestConductorV2(db_base.SqlTestCase):
|
|||
|
||||
return lcmocc
|
||||
|
||||
def _change_vnfpkg_lcmocc(
|
||||
self, op_state=fields.LcmOperationStateType.STARTING):
|
||||
inst = objects.VnfInstanceV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfdId=uuidutils.generate_uuid(),
|
||||
vnfProvider='provider',
|
||||
vnfProductName='product name',
|
||||
vnfSoftwareVersion='software version',
|
||||
vnfdVersion='vnfd version',
|
||||
instantiationState='INSTANTIATED'
|
||||
)
|
||||
req = {"vnfdId": uuidutils.generate_uuid()}
|
||||
lcmocc = objects.VnfLcmOpOccV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
operationState=op_state,
|
||||
stateEnteredTime=datetime.utcnow(),
|
||||
startTime=datetime.utcnow(),
|
||||
vnfInstanceId=inst.id,
|
||||
operation=fields.LcmOperationType.CHANGE_VNFPKG,
|
||||
isAutomaticInvocation=False,
|
||||
isCancelPending=False,
|
||||
operationParams=req)
|
||||
|
||||
inst.create(self.context)
|
||||
lcmocc.create(self.context)
|
||||
|
||||
return lcmocc
|
||||
|
||||
def _make_grant_req_and_grant(self, lcmocc):
|
||||
grant_req = objects.GrantRequestV1(
|
||||
# required fields
|
||||
|
@ -129,6 +159,34 @@ class TestConductorV2(db_base.SqlTestCase):
|
|||
self.assertRaises(sol_ex.GrantRequestOrGrantNotFound,
|
||||
lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc)
|
||||
|
||||
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'process')
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'send_lcmocc_notification')
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnfd')
|
||||
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'grant')
|
||||
def test_start_lcm_op_change_vnfpkg_completed(
|
||||
self, mocked_grant, mocked_get_vnfd,
|
||||
mocked_send_lcmocc_notification, mocked_process):
|
||||
# prepare
|
||||
lcmocc = self._change_vnfpkg_lcmocc()
|
||||
mocked_get_vnfd.return_value = mock.Mock()
|
||||
mocked_grant.return_value = self._make_grant_req_and_grant(lcmocc)
|
||||
mocked_process.return_value = mock.Mock()
|
||||
op_state = []
|
||||
|
||||
def _store_state(context, lcmocc, inst, endpoint):
|
||||
op_state.append(lcmocc.operationState)
|
||||
|
||||
mocked_send_lcmocc_notification.side_effect = _store_state
|
||||
|
||||
# run start_lcm_op
|
||||
self.conductor.start_lcm_op(self.context, lcmocc.id)
|
||||
|
||||
# check operationState transition
|
||||
self.assertEqual(3, mocked_send_lcmocc_notification.call_count)
|
||||
self.assertEqual(fields.LcmOperationStateType.STARTING, op_state[0])
|
||||
self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[1])
|
||||
self.assertEqual(fields.LcmOperationStateType.COMPLETED, op_state[2])
|
||||
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'send_lcmocc_notification')
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnfd')
|
||||
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'grant')
|
||||
|
|
|
@ -627,6 +627,29 @@ _modify_vnfc_info_example = {
|
|||
]
|
||||
}
|
||||
|
||||
# change_vnfpkg example
|
||||
_change_vnfpkg_example = {
|
||||
"vnfdId": '61723406-6634-2fc0-060a-0b11104d2667',
|
||||
"additionalParams": {
|
||||
"upgrade_type": "RollingUpdate",
|
||||
"lcm-operation-coordinate-old-vnf": "./Scripts/coordinate_old_vnf.py",
|
||||
"lcm-operation-coordinate-old-vnf-class": "CoordinateOldVnf",
|
||||
"lcm-operation-coordinate-new-vnf": "./Scripts/coordinate_new_vnf.py",
|
||||
"lcm-operation-coordinate-new-vnf-class": "CoordinateNewVnf",
|
||||
"vdu_params": [{
|
||||
"vdu_id": "VDU1",
|
||||
"old_vnfc_param": {
|
||||
"cp_name": "CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"},
|
||||
"new_vnfc_param": {
|
||||
"cp_name": "CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"},
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TestVnfLcmDriverV2(base.BaseTestCase):
|
||||
|
||||
|
@ -1688,3 +1711,193 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
|
|||
for key, value in check_reses.items():
|
||||
for name, ids in value.items():
|
||||
self.assertEqual(len(expected_res_ids[key][name]), len(ids))
|
||||
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'grant')
|
||||
def test_change_vnfpkg_grant_update_reses(self, mocked_grant):
|
||||
# prepare
|
||||
inst = objects.VnfInstanceV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfdId=SAMPLE_VNFD_ID,
|
||||
vnfProvider='provider',
|
||||
vnfProductName='product name',
|
||||
vnfSoftwareVersion='software version',
|
||||
vnfdVersion='vnfd version',
|
||||
instantiationState='INSTANTIATED'
|
||||
)
|
||||
inst_info = objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
|
||||
_inst_info_example)
|
||||
inst.instantiatedVnfInfo = inst_info
|
||||
req = objects.ChangeCurrentVnfPkgRequest.from_dict(
|
||||
_change_vnfpkg_example)
|
||||
lcmocc = objects.VnfLcmOpOccV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
operationState=fields.LcmOperationStateType.PROCESSING,
|
||||
stateEnteredTime=datetime.utcnow(),
|
||||
startTime=datetime.utcnow(),
|
||||
vnfInstanceId=inst.id,
|
||||
operation=fields.LcmOperationType.CHANGE_VNFPKG,
|
||||
isAutomaticInvocation=False,
|
||||
isCancelPending=False,
|
||||
operationParams=req)
|
||||
|
||||
mocked_grant.return_value = objects.GrantV1()
|
||||
|
||||
# run change_vnfpkg_grant
|
||||
grant_req, _ = self.driver.grant(
|
||||
self.context, lcmocc, inst, self.vnfd_1)
|
||||
|
||||
# check grant_req is constructed according to intention
|
||||
grant_req = grant_req.to_dict()
|
||||
expected_fixed_items = {
|
||||
'vnfInstanceId': inst.id,
|
||||
'vnfLcmOpOccId': lcmocc.id,
|
||||
'vnfdId': '61723406-6634-2fc0-060a-0b11104d2667',
|
||||
'operation': 'CHANGE_VNFPKG',
|
||||
'isAutomaticInvocation': False
|
||||
}
|
||||
for key, value in expected_fixed_items.items():
|
||||
self.assertEqual(value, grant_req[key])
|
||||
|
||||
update_reses = grant_req['updateResources']
|
||||
target_vdu_list = [
|
||||
vdu_param.get(
|
||||
'vdu_id') for vdu_param in req.additionalParams.get(
|
||||
'vdu_params')]
|
||||
for i in range(len(update_reses)):
|
||||
self.assertEqual('COMPUTE', update_reses[i]['type'])
|
||||
for target_vdu in target_vdu_list:
|
||||
self.assertEqual(target_vdu,
|
||||
update_reses[i]['resourceTemplateId'])
|
||||
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'grant')
|
||||
def test_change_vnfpkg_grant_add_reses(self, mocked_grant):
|
||||
# prepare
|
||||
inst = objects.VnfInstanceV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfdId=SAMPLE_VNFD_ID,
|
||||
vnfProvider='provider',
|
||||
vnfProductName='product name',
|
||||
vnfSoftwareVersion='software version',
|
||||
vnfdVersion='vnfd version',
|
||||
instantiationState='INSTANTIATED'
|
||||
)
|
||||
inst_info = objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
|
||||
_inst_info_example)
|
||||
inst.instantiatedVnfInfo = inst_info
|
||||
req = objects.ChangeCurrentVnfPkgRequest.from_dict(
|
||||
_change_vnfpkg_example)
|
||||
lcmocc = objects.VnfLcmOpOccV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
operationState=fields.LcmOperationStateType.PROCESSING,
|
||||
stateEnteredTime=datetime.utcnow(),
|
||||
startTime=datetime.utcnow(),
|
||||
vnfInstanceId=inst.id,
|
||||
operation=fields.LcmOperationType.CHANGE_VNFPKG,
|
||||
isAutomaticInvocation=False,
|
||||
isCancelPending=False,
|
||||
operationParams=req)
|
||||
|
||||
mocked_grant.return_value = objects.GrantV1()
|
||||
|
||||
# run change_vnfpkg_grant
|
||||
grant_req, _ = self.driver.grant(
|
||||
self.context, lcmocc, inst, self.vnfd_1)
|
||||
|
||||
# check grant_req is constructed according to intention
|
||||
grant_req = grant_req.to_dict()
|
||||
expected_fixed_items = {
|
||||
'vnfInstanceId': inst.id,
|
||||
'vnfLcmOpOccId': lcmocc.id,
|
||||
'vnfdId': '61723406-6634-2fc0-060a-0b11104d2667',
|
||||
'operation': 'CHANGE_VNFPKG',
|
||||
'isAutomaticInvocation': False
|
||||
}
|
||||
for key, value in expected_fixed_items.items():
|
||||
self.assertEqual(value, grant_req[key])
|
||||
|
||||
add_reses = grant_req['addResources']
|
||||
for inst_vnc in inst_info.vnfcResourceInfo:
|
||||
nodes = self.vnfd_1.get_vdu_nodes(inst_info.flavourId)
|
||||
vdu_storage_names = self.vnfd_1.get_vdu_storages(
|
||||
nodes[inst_vnc.vduId])
|
||||
for i in range(len(add_reses)):
|
||||
self.assertEqual('STORAGE', add_reses[i]['type'])
|
||||
for vdu_storage_name in vdu_storage_names:
|
||||
self.assertEqual(vdu_storage_name,
|
||||
add_reses[i]['resourceTemplateId'])
|
||||
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'grant')
|
||||
def test_change_vnfpkg_grant_remove_reses(self, mocked_grant):
|
||||
# prepare
|
||||
inst = objects.VnfInstanceV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfdId=SAMPLE_VNFD_ID,
|
||||
vnfProvider='provider',
|
||||
vnfProductName='product name',
|
||||
vnfSoftwareVersion='software version',
|
||||
vnfdVersion='vnfd version',
|
||||
instantiationState='INSTANTIATED'
|
||||
)
|
||||
inst_info = objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
|
||||
_inst_info_example)
|
||||
inst.instantiatedVnfInfo = inst_info
|
||||
req = objects.ChangeCurrentVnfPkgRequest.from_dict(
|
||||
_change_vnfpkg_example)
|
||||
lcmocc = objects.VnfLcmOpOccV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
operationState=fields.LcmOperationStateType.PROCESSING,
|
||||
stateEnteredTime=datetime.utcnow(),
|
||||
startTime=datetime.utcnow(),
|
||||
vnfInstanceId=inst.id,
|
||||
operation=fields.LcmOperationType.CHANGE_VNFPKG,
|
||||
isAutomaticInvocation=False,
|
||||
isCancelPending=False,
|
||||
operationParams=req)
|
||||
|
||||
mocked_grant.return_value = objects.GrantV1()
|
||||
|
||||
# run change_vnfpkg_grant
|
||||
grant_req, _ = self.driver.grant(
|
||||
self.context, lcmocc, inst, self.vnfd_1)
|
||||
|
||||
# check grant_req is constructed according to intention
|
||||
grant_req = grant_req.to_dict()
|
||||
expected_fixed_items = {
|
||||
'vnfInstanceId': inst.id,
|
||||
'vnfLcmOpOccId': lcmocc.id,
|
||||
'vnfdId': '61723406-6634-2fc0-060a-0b11104d2667',
|
||||
'operation': 'CHANGE_VNFPKG',
|
||||
'isAutomaticInvocation': False
|
||||
}
|
||||
for key, value in expected_fixed_items.items():
|
||||
self.assertEqual(value, grant_req[key])
|
||||
|
||||
remove_reses = grant_req['removeResources']
|
||||
inst_stor_info = inst_info.virtualStorageResourceInfo
|
||||
check_reses = []
|
||||
for str_info in inst_stor_info:
|
||||
check_res = {
|
||||
'resourceId': str_info.storageResource.resourceId,
|
||||
'vimLevelResourceType':
|
||||
str_info.storageResource.vimLevelResourceType
|
||||
}
|
||||
check_reses.append(check_res)
|
||||
for i in range(len(remove_reses)):
|
||||
self.assertEqual('STORAGE', remove_reses[i]['type'])
|
||||
self.assertEqual(str_info.virtualStorageDescId,
|
||||
remove_reses[i]['resourceTemplateId'])
|
||||
for j in range(len(check_reses)):
|
||||
for k in range(len(remove_reses)):
|
||||
if j == k:
|
||||
self.assertEqual(
|
||||
check_reses[j]['resourceId'],
|
||||
remove_reses[j]['resource']['resourceId'])
|
||||
self.assertEqual(
|
||||
check_reses[j]['vimLevelResourceType'],
|
||||
remove_reses[j]['resource']['vimLevelResourceType'])
|
||||
|
|
|
@ -125,6 +125,74 @@ class TestVnflcmV2(db_base.SqlTestCase):
|
|||
self.assertRaises(sol_ex.VnfdIdNotEnabled,
|
||||
self.controller.create, request=self.request, body=body)
|
||||
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnf_package_info_vnfd')
|
||||
def test_change_vnfpkg_pkg_disabled(self,
|
||||
mocked_get_vnf_package_info_vnfd):
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
inst_id, _ = self._create_inst_and_lcmocc('INSTANTIATED',
|
||||
fields.LcmOperationStateType.COMPLETED)
|
||||
body = {"vnfdId": vnfd_id}
|
||||
pkg_info = objects.VnfPkgInfoV2(
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfdId=vnfd_id,
|
||||
vnfProvider="provider",
|
||||
vnfProductName="product",
|
||||
vnfSoftwareVersion="software version",
|
||||
vnfdVersion="vnfd version",
|
||||
operationalState="DISABLED"
|
||||
)
|
||||
mocked_get_vnf_package_info_vnfd.return_value = pkg_info
|
||||
self.assertRaises(sol_ex.VnfdIdNotEnabled,
|
||||
self.controller.change_vnfpkg, request=self.request, id=inst_id,
|
||||
body=body)
|
||||
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnf_package_info_vnfd')
|
||||
def test_change_vnfpkg_pkg_no_additional_params(
|
||||
self, mocked_get_vnf_package_info_vnfd):
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
inst_id, _ = self._create_inst_and_lcmocc('INSTANTIATED',
|
||||
fields.LcmOperationStateType.COMPLETED)
|
||||
body = {"vnfdId": vnfd_id}
|
||||
pkg_info = objects.VnfPkgInfoV2(
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfdId=vnfd_id,
|
||||
vnfProvider="provider",
|
||||
vnfProductName="product",
|
||||
vnfSoftwareVersion="software version",
|
||||
vnfdVersion="vnfd version",
|
||||
operationalState="ENABLED"
|
||||
)
|
||||
mocked_get_vnf_package_info_vnfd.return_value = pkg_info
|
||||
self.assertRaises(sol_ex.SolValidationError,
|
||||
self.controller.change_vnfpkg, request=self.request, id=inst_id,
|
||||
body=body)
|
||||
|
||||
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnf_package_info_vnfd')
|
||||
def test_change_vnfpkg_pkg_upgrade_type(
|
||||
self, mocked_get_vnf_package_info_vnfd):
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
inst_id, _ = self._create_inst_and_lcmocc('INSTANTIATED',
|
||||
fields.LcmOperationStateType.COMPLETED)
|
||||
body = {
|
||||
"vnfdId": vnfd_id,
|
||||
"additionalParams": {
|
||||
"upgrade_type": "BuleGreen"
|
||||
}
|
||||
}
|
||||
pkg_info = objects.VnfPkgInfoV2(
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfdId=vnfd_id,
|
||||
vnfProvider="provider",
|
||||
vnfProductName="product",
|
||||
vnfSoftwareVersion="software version",
|
||||
vnfdVersion="vnfd version",
|
||||
operationalState="ENABLED"
|
||||
)
|
||||
mocked_get_vnf_package_info_vnfd.return_value = pkg_info
|
||||
self.assertRaises(sol_ex.NotSupportUpgradeType,
|
||||
self.controller.change_vnfpkg, request=self.request, id=inst_id,
|
||||
body=body)
|
||||
|
||||
def test_delete_instantiated(self):
|
||||
inst_id, _ = self._create_inst_and_lcmocc('INSTANTIATED',
|
||||
fields.LcmOperationStateType.COMPLETED)
|
||||
|
@ -157,6 +225,26 @@ class TestVnflcmV2(db_base.SqlTestCase):
|
|||
self.controller.instantiate, request=self.request, id=inst_id,
|
||||
body=body)
|
||||
|
||||
def test_change_vnfpkg_not_instantiated(self):
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
inst_id, _ = self._create_inst_and_lcmocc('NOT_INSTANTIATED',
|
||||
fields.LcmOperationStateType.COMPLETED)
|
||||
body = {"vnfdId": vnfd_id}
|
||||
|
||||
self.assertRaises(sol_ex.VnfInstanceIsNotInstantiated,
|
||||
self.controller.change_vnfpkg, request=self.request, id=inst_id,
|
||||
body=body)
|
||||
|
||||
def test_change_vnfpkg_lcmocc_in_progress(self):
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
inst_id, _ = self._create_inst_and_lcmocc('INSTANTIATED',
|
||||
fields.LcmOperationStateType.FAILED_TEMP)
|
||||
body = {"vnfdId": vnfd_id}
|
||||
|
||||
self.assertRaises(sol_ex.OtherOperationInProgress,
|
||||
self.controller.change_vnfpkg, request=self.request, id=inst_id,
|
||||
body=body)
|
||||
|
||||
def test_terminate_not_instantiated(self):
|
||||
inst_id, _ = self._create_inst_and_lcmocc('NOT_INSTANTIATED',
|
||||
fields.LcmOperationStateType.COMPLETED)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,6 +15,7 @@
|
|||
|
||||
import yaml
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.infra_drivers.openstack import userdata_utils
|
||||
|
||||
|
@ -23,39 +24,39 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
|
||||
@staticmethod
|
||||
def instantiate(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = req['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
if 'computeFlavourId' in vdu_value:
|
||||
vdu_value['computeFlavourId'] = (
|
||||
userdata_utils.get_param_flavor(
|
||||
common_script_utils.get_param_flavor(
|
||||
vdu_name, flavour_id, vnfd, grant))
|
||||
if 'vcImageId' in vdu_value:
|
||||
vdu_value['vcImageId'] = userdata_utils.get_param_image(
|
||||
vdu_value['vcImageId'] = common_script_utils.get_param_image(
|
||||
vdu_name, flavour_id, vnfd, grant)
|
||||
if 'locationConstraints' in vdu_value:
|
||||
vdu_value['locationConstraints'] = (
|
||||
userdata_utils.get_param_zone(
|
||||
common_script_utils.get_param_zone(
|
||||
vdu_name, grant_req, grant))
|
||||
if 'desired_capacity' in vdu_value:
|
||||
vdu_value['desired_capacity'] = (
|
||||
userdata_utils.get_param_capacity(
|
||||
common_script_utils.get_param_capacity(
|
||||
vdu_name, inst, grant_req))
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
for cp_name, cp_value in cps.items():
|
||||
if 'network' in cp_value:
|
||||
cp_value['network'] = userdata_utils.get_param_network(
|
||||
cp_value['network'] = common_script_utils.get_param_network(
|
||||
cp_name, grant, req)
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = userdata_utils.get_param_fixed_ips(
|
||||
ext_fixed_ips = common_script_utils.get_param_fixed_ips(
|
||||
cp_name, grant, req)
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
|
@ -70,7 +71,7 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
fixed_ips.append(ips_i)
|
||||
cp_value['fixed_ips'] = fixed_ips
|
||||
|
||||
userdata_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
common_script_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
|
||||
if 'nfv' in req.get('additionalParams', {}):
|
||||
nfv_dict = inst_utils.json_merge_patch(nfv_dict,
|
||||
|
@ -98,19 +99,19 @@ class DefaultUserData(userdata_utils.AbstractUserData):
|
|||
# NOTE: complete 'nfv' dict can not be made at the moment
|
||||
# since InstantiateVnfRequest is necessary to make it.
|
||||
|
||||
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
nfv_dict = userdata_utils.init_nfv_dict(top_hot)
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
new_vdus = {}
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
if 'desired_capacity' in vdu_value:
|
||||
capacity = userdata_utils.get_param_capacity(
|
||||
capacity = common_script_utils.get_param_capacity(
|
||||
vdu_name, inst, grant_req)
|
||||
new_vdus[vdu_name] = {'desired_capacity': capacity}
|
||||
|
||||
|
|
Loading…
Reference in New Issue