Implementation Artifacts support in Tacker

Implements: bp/add-artifacts-vnf-packages

* python-tackerclient

  * Modify ``vnf package show`` command to display ``additionalArtifacts``
    information.
  * Add new OSC command ``vnf package artifact`` to fetch individual artifact
    in an on-boarded VNF package.

Change-Id: Id0694724f2f2e335c14824bc5ca5bf2e67ac4b96
This commit is contained in:
LiangLu 2020-07-08 12:08:23 -04:00
parent 60c268f04d
commit 9cd8f11d83
12 changed files with 2202 additions and 16 deletions

View File

@ -84,6 +84,7 @@ openstack.tackerclient.v1 =
vnf_package_delete = tackerclient.osc.v1.vnfpkgm.vnf_package:DeleteVnfPackage
vnf_package_update = tackerclient.osc.v1.vnfpkgm.vnf_package:UpdateVnfPackage
vnf_package_download = tackerclient.osc.v1.vnfpkgm.vnf_package:DownloadVnfPackage
vnf_package_artifact_download = tackerclient.osc.v1.vnfpkgm.vnf_package:DownloadVnfPackageArtifact
vnflcm_create = tackerclient.osc.v1.vnflcm.vnflcm:CreateVnfLcm
vnflcm_show = tackerclient.osc.v1.vnflcm.vnflcm:ShowVnfLcm
vnflcm_list = tackerclient.osc.v1.vnflcm.vnflcm:ListVnfLcm

View File

@ -32,13 +32,14 @@ LOG = logging.getLogger(__name__)
formatters = {'softwareImages': tacker_osc_utils.FormatComplexDataColumn,
'checksum': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn,
'userDefinedData': tacker_osc_utils.FormatComplexDataColumn}
'userDefinedData': tacker_osc_utils.FormatComplexDataColumn,
'additionalArtifacts': tacker_osc_utils.FormatComplexDataColumn}
_mixed_case_fields = ('usageState', 'onboardingState', 'operationalState',
'vnfProductName', 'softwareImages', 'userDefinedData',
'vnfdId', 'vnfdVersion', 'vnfSoftwareVersion',
'vnfProvider')
'vnfProvider', 'additionalArtifacts')
def _get_columns(vnf_package_obj):
@ -59,7 +60,8 @@ def _get_columns(vnf_package_obj):
'vnfProductName': 'VNF Product Name',
'vnfdId': 'VNFD ID',
'vnfdVersion': 'VNFD Version',
'checksum': 'Checksum'
'checksum': 'Checksum',
'additionalArtifacts': 'Additional Artifacts'
})
return sdk_utils.get_osc_show_columns_for_sdk_resource(vnf_package_obj,
@ -146,7 +148,11 @@ class ListVnfPackage(command.Lister):
exclude_fields=None, exclude_default=False):
fields = ['id', 'vnfProductName', 'onboardingState',
'usageState', 'operationalState', '_links']
complex_fields = ['checksum', 'softwareImages', 'userDefinedData']
complex_fields = [
'checksum',
'softwareImages',
'userDefinedData',
'additionalArtifacts']
simple_fields = ['vnfdVersion', 'vnfProvider', 'vnfSoftwareVersion',
'vnfdId']
@ -409,6 +415,51 @@ class DownloadVnfPackage(command.Command):
sdk_utils.save_data(body, parsed_args.file)
class DownloadVnfPackageArtifact(command.Command):
_description = _("Download VNF package artifact of an on-boarded "
"VNF package.")
def get_parser(self, prog_name):
parser = super(DownloadVnfPackageArtifact, self).get_parser(prog_name)
parser.add_argument(
"vnf_package",
metavar="<vnf-package>",
help=_("VNF package ID")
)
parser.add_argument(
"artifact_path",
metavar="<artifact-path>",
help=_("The artifact file's path")
)
parser.add_argument(
"--file",
metavar="<FILE>",
help=_("Local file to save downloaded VNF Package or VNFD data. "
"If this is not specified and there is no redirection "
"then data will not be saved.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
if sys.stdout.isatty() and not (parsed_args.file):
msg = (
"No redirection or local file specified for downloaded "
"vnf package artifact data. Please specify a "
"local file with --file to "
"save downloaded vnf package artifact data "
"or use redirection.")
sdk_utils.exit(msg)
body = client.download_artifact_from_vnf_package(
parsed_args.vnf_package, parsed_args.artifact_path)
if not parsed_args.file:
print(body)
return
else:
sdk_utils.save_data(body, parsed_args.file)
class UpdateVnfPackage(command.ShowOne):
_description = _("Update information about an individual VNF package")

View File

@ -0,0 +1,202 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: ETSI NFV SOL 001 common types definitions version 2.6.1
metadata:
template_name: etsi_nfv_sol001_common_types
template_author: ETSI_NFV
template_version: 2.6.1
data_types:
tosca.datatypes.nfv.L2AddressData:
derived_from: tosca.datatypes.Root
description: Describes the information on the MAC addresses to be assigned to a connection point.
properties:
mac_address_assignment:
type: boolean
description: Specifies if the address assignment is the responsibility of management and orchestration function or not. If it is set to True, it is the management and orchestration function responsibility
required: true
tosca.datatypes.nfv.L3AddressData:
derived_from: tosca.datatypes.Root
description: Provides information about Layer 3 level addressing scheme and parameters applicable to a CP
properties:
ip_address_assignment:
type: boolean
description: Specifies if the address assignment is the responsibility of management and orchestration function or not. If it is set to True, it is the management and orchestration function responsibility
required: true
floating_ip_activated:
type: boolean
description: Specifies if the floating IP scheme is activated on the Connection Point or not
required: true
ip_address_type:
type: string
description: Defines address type. The address type should be aligned with the address type supported by the layer_protocols properties of the parent VnfExtCp
required: false
constraints:
- valid_values: [ ipv4, ipv6 ]
number_of_ip_address:
type: integer
description: Minimum number of IP addresses to be assigned
required: false
constraints:
- greater_than: 0
tosca.datatypes.nfv.AddressData:
derived_from: tosca.datatypes.Root
description: Describes information about the addressing scheme and parameters applicable to a CP
properties:
address_type:
type: string
description: Describes the type of the address to be assigned to a connection point. The content type shall be aligned with the address type supported by the layerProtocol property of the connection point
required: true
constraints:
- valid_values: [ mac_address, ip_address ]
l2_address_data:
type: tosca.datatypes.nfv.L2AddressData
description: Provides the information on the MAC addresses to be assigned to a connection point.
required: false
l3_address_data:
type: tosca.datatypes.nfv.L3AddressData
description: Provides the information on the IP addresses to be assigned to a connection point
required: false
tosca.datatypes.nfv.ConnectivityType:
derived_from: tosca.datatypes.Root
description: describes additional connectivity information of a virtualLink
properties:
layer_protocols:
type: list
description: Identifies the protocol a virtualLink gives access to (ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire).The top layer protocol of the virtualLink protocol stack shall always be provided. The lower layer protocols may be included when there are specific requirements on these layers.
required: true
entry_schema:
type: string
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
flow_pattern:
type: string
description: Identifies the flow pattern of the connectivity
required: false
constraints:
- valid_values: [ line, tree, mesh ]
tosca.datatypes.nfv.LinkBitrateRequirements:
derived_from: tosca.datatypes.Root
description: describes the requirements in terms of bitrate for a virtual link
properties:
root:
type: integer # in bits per second
description: Specifies the throughput requirement in bits per second of the link (e.g. bitrate of E-Line, root bitrate of E-Tree, aggregate capacity of E-LAN).
required: true
constraints:
- greater_or_equal: 0
leaf:
type: integer # in bits per second
description: Specifies the throughput requirement in bits per second of leaf connections to the link when applicable to the connectivity type (e.g. for E-Tree and E LAN branches).
required: false
constraints:
- greater_or_equal: 0
tosca.datatypes.nfv.CpProtocolData:
derived_from: tosca.datatypes.Root
description: Describes and associates the protocol layer that a CP uses together with other protocol and connection point information
properties:
associated_layer_protocol:
type: string
required: true
description: One of the values of the property layer_protocols of the CP
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
address_data:
type: list
description: Provides information on the addresses to be assigned to the CP
entry_schema:
type: tosca.datatypes.nfv.AddressData
required: false
tosca.datatypes.nfv.VnfProfile:
derived_from: tosca.datatypes.Root
description: describes a profile for instantiating VNFs of a particular NS DF according to a specific VNFD and VNF DF.
properties:
instantiation_level:
type: string
description: Identifier of the instantiation level of the VNF DF to be used for instantiation. If not present, the default instantiation level as declared in the VNFD shall be used.
required: false
min_number_of_instances:
type: integer
description: Minimum number of instances of the VNF based on this VNFD that is permitted to exist for this VnfProfile.
required: true
constraints:
- greater_or_equal: 0
max_number_of_instances:
type: integer
description: Maximum number of instances of the VNF based on this VNFD that is permitted to exist for this VnfProfile.
required: true
constraints:
- greater_or_equal: 0
tosca.datatypes.nfv.Qos:
derived_from: tosca.datatypes.Root
description: describes QoS data for a given VL used in a VNF deployment flavour
properties:
latency:
type: scalar-unit.time #Number
description: Specifies the maximum latency
required: true
constraints:
- greater_than: 0 s
packet_delay_variation:
type: scalar-unit.time #Number
description: Specifies the maximum jitter
required: true
constraints:
- greater_or_equal: 0 s
packet_loss_ratio:
type: float
description: Specifies the maximum packet loss ratio
required: false
constraints:
- in_range: [ 0.0, 1.0 ]
capability_types:
tosca.capabilities.nfv.VirtualLinkable:
derived_from: tosca.capabilities.Node
description: A node type that includes the VirtualLinkable capability indicates that it can be pointed by tosca.relationships.nfv.VirtualLinksTo relationship type
relationship_types:
tosca.relationships.nfv.VirtualLinksTo:
derived_from: tosca.relationships.DependsOn
description: Represents an association relationship between the VduCp and VnfVirtualLink node types
valid_target_types: [ tosca.capabilities.nfv.VirtualLinkable ]
node_types:
tosca.nodes.nfv.Cp:
derived_from: tosca.nodes.Root
description: Provides information regarding the purpose of the connection point
properties:
layer_protocols:
type: list
description: Identifies which protocol the connection point uses for connectivity purposes
required: true
entry_schema:
type: string
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
role: #Name in ETSI NFV IFA011 v0.7.3: cpRole
type: string
description: Identifies the role of the port in the context of the traffic flow patterns in the VNF or parent NS
required: false
constraints:
- valid_values: [ root, leaf ]
description:
type: string
description: Provides human-readable information on the purpose of the connection point
required: false
protocol:
type: list
description: Provides information on the addresses to be assigned to the connection point(s) instantiated from this Connection Point Descriptor
required: false
entry_schema:
type: tosca.datatypes.nfv.CpProtocolData
trunk_mode:
type: boolean
description: Provides information about whether the CP instantiated from this Cp is in Trunk mode (802.1Q or other), When operating in "trunk mode", the Cp is capable of carrying traffic for several VLANs. Absence of this property implies that trunkMode is not configured for the Cp i.e. It is equivalent to boolean value "false".
required: false

View File

@ -0,0 +1,277 @@
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
- helloworld3_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: ntt.nslab.VNF
properties:
flavour_id: simple
requirements:
virtual_link_external: [ CP1, virtual_link ]
node_templates:
VNF:
type: ntt.nslab.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
# supporting only 'instantiate', 'terminate', 'modify'
# not supporting LCM script, supporting only default LCM
instantiate: []
instantiate_start: []
instantiate_end: []
terminate: []
terminate_start: []
terminate_end: []
modify_information: []
modify_information_start: []
modify_information_end: []
# change_flavour: []
# change_flavour_start: []
# change_flavour_end: []
# change_external_connectivity: []
# change_external_connectivity_start: []
# change_external_connectivity_end: []
# operate: []
# operate_start: []
# operate_end: []
# heal: []
# heal_start: []
# heal_end: []
# scale: []
# scale_start: []
# scale_end: []
# scale_to_level: []
# scale_to_level_start: []
# scale_to_level_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: 1
sw_image_data:
name: Software of VDU1
version: '0.4.0'
checksum:
algorithm: sha-256
hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d
container_format: bare
disk_format: qcow2
min_disk: 1 GB
size: 1 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: cirros-0.4.0-x86_64-disk.img
repository: http://download.cirros-cloud.net/0.4.0/
capabilities:
virtual_compute:
properties:
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 1 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: 3
capabilities:
virtual_compute:
properties:
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 1 GB
requirements:
- virtual_storage: VirtualStorage
VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 30 GB
rdma_enabled: true
sw_image_data:
name: VrtualStorage
version: '0.4.0'
checksum:
algorithm: sha-256
hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d
container_format: bare
disk_format: qcow2
min_disk: 2 GB
min_ram: 8192 MB
size: 2 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: cirros-0.4.0-x86_64-disk.img
repository: http://download.cirros-cloud.net/0.4.0/
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
vnic_type: direct-physical
requirements:
- virtual_binding: VDU1
#- virtual_link: # the target node is determined in the NSD
CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL2
CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL2
internalVL2:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: Internal Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 11.11.0.0/24
policies:
- scaling_aspects:
type: tosca.policies.nfv.ScalingAspects
properties:
aspects:
worker_instance:
name: worker_instance_aspect
description: worker_instance scaling aspect
max_scale_level: 2
step_deltas:
- delta_1
- VDU2_initial_delta:
type: tosca.policies.nfv.VduInitialDelta
properties:
initial_delta:
number_of_instances: 1
targets: [ VDU2 ]
- VDU2_scaling_aspect_deltas:
type: tosca.policies.nfv.VduScalingAspectDeltas
properties:
aspect: worker_instance
deltas:
delta_2:
number_of_instances: 1
targets: [ VDU2 ]
- instantiation_levels:
type: tosca.policies.nfv.InstantiationLevels
properties:
levels:
instantiation_level_1:
description: Smallest size
scale_info:
worker_instance:
scale_level: 0
instantiation_level_2:
description: Largest size
scale_info:
worker_instance:
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 ]
- internalVL2_instantiation_levels:
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
properties:
levels:
instantiation_level_1:
bitrate_requirements:
root: 1048576
leaf: 1048576
instantiation_level_2:
bitrate_requirements:
root: 1048576
leaf: 1048576
targets: [ internalVL2 ]

View File

@ -0,0 +1,32 @@
tosca_definitions_version: tosca_simple_yaml_1_0
description: Sample VNF of NTT NS lab.
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- helloworld3_types.yaml
- helloworld3_df_simple.yaml
# - helloworld3_df_complex.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: ntt.nslab.VNF
properties:
flavour_id: { get_input: selected_flavour }
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d70a1177
provider: NTT NS lab
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

View File

@ -0,0 +1,53 @@
tosca_definitions_version: tosca_simple_yaml_1_0
description: ntt.nslab.VNF type definition
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
node_types:
ntt.nslab.VNF:
derived_from: tosca.nodes.nfv.VNF
properties:
descriptor_id:
type: string
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d70a1177 ] ]
default: b1bb0ce7-ebca-4fa7-95ed-4840d70a1177
descriptor_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
provider:
type: string
constraints: [ valid_values: [ 'NTT NS lab' ] ]
default: 'NTT NS lab'
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: ""
requirements:
- virtual_link_external:
capability: tosca.capabilities.nfv.VirtualLinkable
- virtual_link_internal:
capability: tosca.capabilities.nfv.VirtualLinkable
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm

View File

@ -0,0 +1,3 @@
#!/bin/bash
echo "Hello, World!"

View File

@ -0,0 +1,8 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Dummy User
CSAR-Version: 1.1
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml
Source: Scripts/install.sh
Algorithm: SHA-256
Hash: 27bbdb25d8f4ed6d07d6f6581b86515e8b2f0059b236ef7b6f50d6674b34f02a

View File

@ -56,9 +56,14 @@ def _get_columns_vnf_package(action='list', vnf_package_obj=None):
if action in ['show', 'create']:
if vnf_package_obj and vnf_package_obj[
'onboardingState'] == 'ONBOARDED':
columns.extend(['VNFD ID', 'VNF Provider', 'VNF Software Version',
'VNFD Version', 'Software Images',
'VNF Product Name', 'Checksum'])
columns.extend(['VNFD ID',
'VNF Provider',
'VNF Software Version',
'VNFD Version',
'Software Images',
'VNF Product Name',
'Checksum',
'Additional Artifacts'])
return columns
@ -127,7 +132,11 @@ class TestListVnfPackage(TestVnfPackage):
columns = ['Id', 'Vnf Product Name', 'Onboarding State', 'Usage State',
'Operational State', 'Links']
complex_columns = ['Checksum', 'Software Images', 'User Defined Data']
complex_columns = [
'Checksum',
'Software Images',
'User Defined Data',
'Additional Artifacts']
simple_columns = ['Vnfd Version', 'Vnf Provider', 'Vnfd Id',
'Vnf Software Version']
@ -195,9 +204,11 @@ class TestListVnfPackage(TestVnfPackage):
def test_take_action_with_exclude_fields(self):
parsed_args = self.check_parser(
self.list_vnf_package,
["--exclude_fields", 'softwareImages,checksum,userDefinedData',
["--exclude_fields", 'softwareImages,checksum,'
'userDefinedData,additionalArtifacts',
"--filter", '(eq,onboardingState,ONBOARDED)'],
[('exclude_fields', 'softwareImages,checksum,userDefinedData'),
[('exclude_fields', 'softwareImages,checksum,'
'userDefinedData,additionalArtifacts'),
('filter', '(eq,onboardingState,ONBOARDED)')])
vnf_packages = self._get_vnf_packages(onboarded_vnf_package=True)
updated_vnf_packages = {'vnf_packages': []}
@ -205,10 +216,12 @@ class TestListVnfPackage(TestVnfPackage):
vnf_pkg.pop('softwareImages')
vnf_pkg.pop('checksum')
vnf_pkg.pop('userDefinedData')
vnf_pkg.pop('additionalArtifacts')
updated_vnf_packages['vnf_packages'].append(vnf_pkg)
self._get_mock_response_for_list_vnf_packages(
'filter=(eq,onboardingState,ONBOARDED)&'
'exclude_fields=softwareImages,checksum,userDefinedData',
'exclude_fields=softwareImages,checksum,'
'userDefinedData,additionalArtifacts',
json=updated_vnf_packages)
actual_columns, data = self.list_vnf_package.take_action(parsed_args)
@ -216,7 +229,7 @@ class TestListVnfPackage(TestVnfPackage):
headers, columns = tacker_osc_utils.get_column_definitions(
self.list_vnf_package.get_attributes(
exclude_fields=['softwareImages', 'checksum',
'userDefinedData']),
'userDefinedData', 'additionalArtifacts']),
long_listing=True)
for vnf_package_obj in updated_vnf_packages['vnf_packages']:
@ -224,7 +237,7 @@ class TestListVnfPackage(TestVnfPackage):
vnf_package_obj, columns=columns, list_action=True))
expected_columns = self.get_list_columns(
exclude_fields=['Software Images', 'Checksum',
'User Defined Data'])
'User Defined Data', 'Additional Artifacts'])
self.assertCountEqual(expected_columns, actual_columns)
self.assertListItemsEqual(expected_data, list(data))
@ -731,3 +744,60 @@ class TestDownloadVnfPackage(TestVnfPackage):
"Downloaded contents don't match test file")
self.assertTrue(self._check_valid_zip_file(local_file.name))
shutil.rmtree(temp_dir)
@ddt.ddt
class TestDownloadVnfPackageArtifact(TestVnfPackage):
# The new vnf package created.
_vnf_package = vnf_package_fakes.vnf_package_obj(
attrs={'userDefinedData': {'Test_key': 'Test_value'}})
def setUp(self):
super(TestDownloadVnfPackageArtifact, self).setUp()
self.download_vnf_package_artifacts = vnf_package.\
DownloadVnfPackageArtifact(
self.app, self.app_args,
cmd_name='vnf package artifact download')
def test_download_no_options(self):
self.assertRaises(base.ParserException, self.check_parser,
self.download_vnf_package_artifacts, [], [])
def _mock_request_url_for_download_artifacts(
self, artifact_path, artifact_data):
self.header = {'content-type': 'text/plain'}
url = os.path.join(self.url, 'vnfpkgm/v1/vnf_packages',
self._vnf_package['id'], 'artifacts', artifact_path)
self.requests_mock.register_uri('GET', url,
headers=self.header,
text=artifact_data)
def _get_arglist_and_verifylist(self, localfile):
arglist = [
self._vnf_package['id'],
localfile.name[1:],
'--file', localfile.name
]
verifylist = [
('vnf_package', self._vnf_package['id']),
('artifact_path', localfile.name[1:]),
('file', localfile.name)
]
return arglist, verifylist
def test_download_artifacts_from_vnf_package(self):
test_file = ('./tackerclient/tests//unit/osc/v1/fixture_data/'
'sample_vnf_package_artifacts/Scripts/'
'install.sh')
local_file = tempfile.NamedTemporaryFile(suffix='install.sh')
artifact_data = open(test_file, 'r').read()
arglist, verifylist = self._get_arglist_and_verifylist(
local_file)
parsed_args = self.check_parser(
self.download_vnf_package_artifacts, arglist, verifylist)
self._mock_request_url_for_download_artifacts(
local_file.name[1:], artifact_data)
self.download_vnf_package_artifacts.take_action(parsed_args)
self.assertTrue(filecmp.cmp(test_file, local_file.name),
"Downloaded contents don't match test file")

View File

@ -83,7 +83,17 @@ def vnf_package_obj(attrs=None, onboarded_state=False):
"packageContent": {
"href": "string"
}
}}
},
"additionalArtifacts": [
{
"artifactPath": "string",
"metadata": {},
"checksum": {
"algorithm": "string",
"hash": "string"
}
}]
}
# Overwrite default attributes.
fake_vnf_package.update(attrs)
@ -102,7 +112,7 @@ def get_vnf_package_data(vnf_package_obj, **kwargs):
'CREATED', 'DISABLED', 'NOT_IN_USE', {'Test_key': 'Test_value'}]
"""
complex_attributes = ['softwareImages', 'checksum', '_links',
'userDefinedData']
'userDefinedData', 'additionalArtifacts']
for attribute in complex_attributes:
if vnf_package_obj.get(attribute):
vnf_package_obj.update(

View File

@ -229,6 +229,8 @@ class ClientBase(object):
self.format = 'zip'
elif 'text/plain' == resp.headers.get('Content-Type'):
self.format = 'text'
elif 'artifacts' in action:
self.format = 'any'
else:
self.format = 'json'
@ -265,7 +267,7 @@ class ClientBase(object):
def deserialize(self, data, status_code):
"""Deserializes an XML or JSON string into a dictionary."""
if status_code in (204, 202) or self.format in ('zip', 'text'):
if status_code in (204, 202) or self.format in ('zip', 'text', 'any'):
return data
return serializer.Serializer(self.get_attr_metadata()).deserialize(
data, self.content_type())['body']
@ -775,6 +777,8 @@ class VnfPackageClient(ClientBase):
vnfpackage_path = '/vnfpkgm/v1/vnf_packages/%s'
vnfpackage_vnfd_path = '/vnfpkgm/v1/vnf_packages/%s/vnfd'
vnfpackage_download_path = '/vnfpkgm/v1/vnf_packages/%s/package_content'
vnfpakcage_artifact_path = '/vnfpkgm/v1/vnf_packages/%(id)s/artifacts/' \
'%(artifact_path)s'
def build_action(self, action):
return action
@ -845,6 +849,11 @@ class VnfPackageClient(ClientBase):
self.format = 'both'
return self.get(self.vnfpackage_vnfd_path % vnf_package)
@APIParamsCall
def download_artifact_from_vnf_package(self, vnf_package, artifact_path):
return self.get(self.vnfpakcage_artifact_path %
{'id': vnf_package, 'artifact_path': artifact_path})
@APIParamsCall
def update_vnf_package(self, vnf_package, body):
return self.patch(self.vnfpackage_path % vnf_package, body=body)
@ -1183,5 +1192,10 @@ class Client(object):
return self.vnf_package_client.download_vnfd_from_vnf_package(
vnf_package, accept)
def download_artifact_from_vnf_package(self, vnf_package, artifact_path):
return self.vnf_package_client.download_artifact_from_vnf_package(
vnf_package, artifact_path
)
def download_vnf_package(self, vnf_package):
return self.vnf_package_client.download_vnf_package(vnf_package)