Support ChangeCurrentVNFPackage for VNF of v2 API
This patch implements change current vnf package task defined in ETSI NFV-SOL003 v3.3.1 5.5.2.11a. Retry and rollback operation of change current vnf package API are also supported. Note that currently only 'RollingUpdate' is supported for VNF image's update. This patch refactors the userdata_utils file and writes the common methods into the new common/common_script_utils.py file, so some changes are made to the existing instantiate code. Implements: blueprint upgrade-vnf-package Change-Id: Ic810a3e9063e660e43e094d65111340d92917cf0
This commit is contained in:
parent
30b6f74c64
commit
78dbba65c7
@ -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',
|
||||
|
45
tacker/sol_refactored/common/cinder_utils.py
Normal file
45
tacker/sol_refactored/common/cinder_utils.py
Normal file
@ -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)
|
279
tacker/sol_refactored/common/common_script_utils.py
Normal file
279
tacker/sol_refactored/common/common_script_utils.py
Normal file
@ -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))
|
644
tacker/tests/functional/sol_v2/test_change_vnfpkg.py
Normal file
644
tacker/tests/functional/sol_v2/test_change_vnfpkg.py
Normal file
@ -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'])
|
||||
|
File diff suppressed because it is too large
Load Diff
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
Block a user