Enhancement of Tacker API resource access control

For fine-grained access control based on user and VNF information
for API resources, this patch does the following things:
1.Add three comparison attributes of area, vendor, and tenant
  for the enhanced Tacker policy.
2.Convert special roles to API attributes in context.
3.Modify the API process to support Tacker policy authorize.
4.Add the Tacker policy filter to the list API processes.

Implements: blueprint enhance-api-policy
Change-Id: I5b4c39387860133a3bcf4544f18a6353c80773f6
This commit is contained in:
kexuesheng 2023-01-20 16:00:36 +09:00
parent 3fe7831d63
commit 05fe9fa42c
49 changed files with 9231 additions and 61 deletions

View File

@ -683,6 +683,38 @@
setup_multi_az: true setup_multi_az: true
controller_tacker_hostname: "{{ hostvars['controller-tacker']['ansible_hostname'] }}" controller_tacker_hostname: "{{ hostvars['controller-tacker']['ansible_hostname'] }}"
- job:
name: tacker-functional-devstack-enhanced-policy-sol
parent: tacker-functional-devstack-multinode-legacy
description: |
Enhanced policy job for SOL devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-enhanced-policy-sol
devstack_local_conf:
post-config:
$TACKER_CONF:
oslo_policy:
enhanced_tacker_policy: True
vars:
config_enhanced_policy: true
- job:
name: tacker-functional-devstack-enhanced-policy-sol-kubernetes
parent: tacker-functional-devstack-multinode-sol-kubernetes-v2
description: |
Enhanced policy job for SOL Kubernetes devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-enhanced-policy-sol-kubernetes
devstack_local_conf:
post-config:
$TACKER_CONF:
oslo_policy:
enhanced_tacker_policy: True
vars:
config_enhanced_policy: true
- job: - job:
name: tacker-compliance-devstack-multinode-sol name: tacker-compliance-devstack-multinode-sol
parent: tacker-functional-devstack-multinode-legacy parent: tacker-functional-devstack-multinode-legacy
@ -720,4 +752,6 @@
- tacker-functional-devstack-multinode-sol-kubernetes-multi-tenant - tacker-functional-devstack-multinode-sol-kubernetes-multi-tenant
- tacker-functional-devstack-kubernetes-oidc-auth - tacker-functional-devstack-kubernetes-oidc-auth
- tacker-functional-devstack-multinode-sol-v2-az-retry - tacker-functional-devstack-multinode-sol-v2-az-retry
- tacker-functional-devstack-enhanced-policy-sol
- tacker-functional-devstack-enhanced-policy-sol-kubernetes
- tacker-compliance-devstack-multinode-sol - tacker-compliance-devstack-multinode-sol

File diff suppressed because it is too large Load Diff

View File

@ -60,3 +60,4 @@ Use Case Guide
fault_notification_use_case_guide fault_notification_use_case_guide
prometheus_plugin_use_case_guide prometheus_plugin_use_case_guide
db_migration_tool_usage_guide db_migration_tool_usage_guide
enhanced_tacker_policy_usage_guide

View File

@ -0,0 +1,251 @@
# Decides what is required for the 'is_admin:True' check to succeed.
"context_is_admin": "role:admin"
# Default rule for most non-Admin APIs.
"admin_or_owner": "is_admin:True or project_id:%(project_id)s"
# Default rule for most Admin APIs.
"admin_only": "is_admin:True"
# Default rule for sharing vims.
"shared": "field:vims:shared=True"
# Default rule for most non-Admin APIs.
"default": "rule:admin_or_owner"
# For manager
"manager_and_owner": "role:manager and project_id:%(project_id)s"
# For user
"owner": "project_id:%(project_id)s"
# VIM resource attributes compare rule.
"vim_attrs_cmp": "area:%(area)s"
# Register a VIM.
# Post /v1.0/vims
"create_vim": "rule:manager_and_owner or role:admin"
# List VIMs or show a VIM.
# GET /v1.0/vims
# GET /v1.0/vims/{vim_id}
"get_vim": "rule:vim_attrs_cmp or role:admin"
# Update a VIM.
# PUT /v1.0/vims/{vim_id}
"update_vim": "rule:vim_attrs_cmp and rule:manager_and_owner or role:admin"
# Delete a VIM.
# DELETE /v1.0/vims/{vim_id}
"delete_vim": "rule:vim_attrs_cmp and rule:manager_and_owner or role:admin"
# vnf_packages resource attributes compare rule.
"vnf_pkg_attrs_cmp": "vendor:%(vendor)s"
# Creates a VNF package.
# POST /vnf_packages
"os_nfv_orchestration_api:vnf_packages:create": "rule:admin_or_owner"
# Show a VNF package.
# GET /vnf_packages/{vnf_package_id}
"os_nfv_orchestration_api:vnf_packages:show": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# List all VNF packages.
# GET /vnf_packages/
"os_nfv_orchestration_api:vnf_packages:index": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# Delete a VNF package.
# DELETE /vnf_packages/{vnf_package_id}
"os_nfv_orchestration_api:vnf_packages:delete": "rule:vnf_pkg_attrs_cmp and rule:manager_and_owner or role:admin"
# Fetch the contents of an on-boarded VNF Package.
# GET /vnf_packages/{vnf_package_id}/package_content
"os_nfv_orchestration_api:vnf_packages:fetch_package_content": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# Upload a VNF package content.
# PUT /vnf_packages/{vnf_package_id}/package_content
"os_nfv_orchestration_api:vnf_packages:upload_package_content": "rule:vnf_pkg_attrs_cmp or role:admin"
# Upload a VNF package content from URI.
# POST /vnf_packages/{vnf_package_id}/package_content/upload_from_uri
"os_nfv_orchestration_api:vnf_packages:upload_from_uri": "rule:admin_or_owner"
# Update information of VNF package.
# PATCH /vnf_packages/{vnf_package_id}
"os_nfv_orchestration_api:vnf_packages:patch": "rule:vnf_pkg_attrs_cmp and rule:manager_and_owner or role:admin"
# Read the content of the VNFD within a VNF package.
# GET /vnf_packages/{vnf_package_id}/vnfd
"os_nfv_orchestration_api:vnf_packages:get_vnf_package_vnfd": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# Read the content of the artifact within a VNF package.
# GET /vnf_packages/{vnfPkgId}/artifacts/{artifactPath}
"os_nfv_orchestration_api:vnf_packages:fetch_artifact": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# vnflcm create attributes compare rule.
"vnflcm_create_attrs_cmp": "vendor:%(vendor)s"
# vnflcm instantiate attributes compare rule.
"vnflcm_inst_attrs_cmp": "vendor:%(vendor)s"
# vnflcm delete attributes compare rule.
"vnflcm_delete_attrs_cmp": "vendor:%(vendor)s"
# vnflcm resource attributes compare rule.
"vnflcm_attrs_cmp": "area:%(area)s and vendor:%(vendor)s and tenant:%(tenant)s"
# Get API Versions.
# GET /vnflcm/v1/api_versions
"os_nfv_orchestration_api:vnf_instances:api_versions": "@"
# Create VNF instance.
# POST /vnflcm/v1/vnf_instances
"os_nfv_orchestration_api:vnf_instances:create": "rule:vnflcm_create_attrs_cmp and rule:manager_and_owner or role:admin"
# Instantiate VNF instance.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/instantiate
"os_nfv_orchestration_api:vnf_instances:instantiate": "rule:vnflcm_inst_attrs_cmp and rule:manager_and_owner or role:admin"
# Query an Individual VNF instance.
# GET /vnflcm/v1/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api:vnf_instances:show": "rule:vnflcm_attrs_cmp and rule:owner or role:admin"
# Terminate a VNF instance.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/terminate
"os_nfv_orchestration_api:vnf_instances:terminate": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Heal a VNF instance.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/heal
"os_nfv_orchestration_api:vnf_instances:heal": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Scale a VNF instance.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/scale
"os_nfv_orchestration_api:vnf_instances:scale": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Query an Individual VNF LCM operation occurrence.
# GET /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}
"os_nfv_orchestration_api:vnf_instances:show_lcm_op_occs": "rule:admin_or_owner"
# Query VNF LCM operation occurrence.
# GET /vnflcm/v1/vnf_lcm_op_occs
"os_nfv_orchestration_api:vnf_instances:list_lcm_op_occs": "rule:admin_or_owner"
# Query VNF instances.
# GET /vnflcm/v1/vnf_instances
"os_nfv_orchestration_api:vnf_instances:index": "rule:vnflcm_attrs_cmp and rule:owner or role:admin"
# Delete an Individual VNF instance.
# DELETE /vnflcm/v1/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api:vnf_instances:delete": "rule:vnflcm_delete_attrs_cmp and rule:manager_and_owner or role:admin"
# Update an Individual VNF instance.
# PATCH /vnflcm/v1/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api:vnf_instances:update_vnf": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Rollback a VNF instance.
# POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/rollback
"os_nfv_orchestration_api:vnf_instances:rollback": "rule:admin_or_owner"
# Cancel a VNF instance.
# POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/cancel
"os_nfv_orchestration_api:vnf_instances:cancel": "rule:admin_or_owner"
# Fail a VNF instance.
# POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/fail
"os_nfv_orchestration_api:vnf_instances:fail": "rule:admin_or_owner"
# Retry a VNF instance.
# POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry
"os_nfv_orchestration_api:vnf_instances:retry": "rule:admin_or_owner"
# Change external VNF connectivity.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/change_ext_conn
"os_nfv_orchestration_api:vnf_instances:change_ext_conn": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Get API Versions.
# GET /vnflcm/v2/api_versions
"os_nfv_orchestration_api_v2:vnf_instances:api_versions": "@"
# Creates VNF instance.
# POST /vnflcm/v2/vnf_instances
"os_nfv_orchestration_api_v2:vnf_instances:create": "rule:vnflcm_create_attrs_cmp and rule:manager_and_owner or role:admin"
# Query VNF instances.
# GET /vnflcm/v2/vnf_instances
"os_nfv_orchestration_api_v2:vnf_instances:index": "rule:vnflcm_attrs_cmp and rule:owner or role:admin"
# Query an Individual VNF instance.
# GET /vnflcm/v2/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api_v2:vnf_instances:show": "rule:vnflcm_attrs_cmp and rule:owner or role:admin"
# Delete an Individual VNF instance.
# DELETE /vnflcm/v2/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api_v2:vnf_instances:delete": "rule:vnflcm_delete_attrs_cmp and rule:manager_and_owner or role:admin"
# Modify VNF instance information.
# PATCH /vnflcm/v2/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api_v2:vnf_instances:update": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Instantiate VNF instance.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/instantiate
"os_nfv_orchestration_api_v2:vnf_instances:instantiate": "rule:vnflcm_inst_attrs_cmp and rule:manager_and_owner or role:admin"
# Terminate VNF instance.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/terminate
"os_nfv_orchestration_api_v2:vnf_instances:terminate": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Scale VNF instance.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/scale
"os_nfv_orchestration_api_v2:vnf_instances:scale": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Heal VNF instance.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/heal
"os_nfv_orchestration_api_v2:vnf_instances:heal": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Change external VNF connectivity.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/change_ext_conn
"os_nfv_orchestration_api_v2:vnf_instances:change_ext_conn": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Change VNF package.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/change_vnfpkg
"os_nfv_orchestration_api_v2:vnf_instances:change_vnfpkg": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Create subscription.
# POST /vnflcm/v2/subscriptions
"os_nfv_orchestration_api_v2:vnf_instances:subscription_create": "@"
# List subscription.
# GET /vnflcm/v2/subscriptions
"os_nfv_orchestration_api_v2:vnf_instances:subscription_list": "@"
# Show subscription.
# GET /vnflcm/v2/vnf_instances/{subscriptionId}
"os_nfv_orchestration_api_v2:vnf_instances:subscription_show": "@"
# Delete subscription.
# DELETE /vnflcm/v2/vnf_instances/{subscriptionId}
"os_nfv_orchestration_api_v2:vnf_instances:subscription_delete": "@"
# List VnfLcmOpOcc.
# GET /vnflcm/v2/vnf_lcm_op_occs
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_list": "@"
# Show VnfLcmOpOcc.
# GET /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_show": "@"
# Retry VnfLcmOpOcc.
# POST /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_retry": "@"
# Rollback VnfLcmOpOcc.
# POST /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}/rollback
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_rollback": "@"
# Fail VnfLcmOpOcc.
# POST /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}/fail
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_fail": "@"
# Delete VnfLcmOpOcc.
# DELETE /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_delete": "@"

View File

@ -15,6 +15,8 @@
when: setup_multi_az is defined and setup_multi_az | bool when: setup_multi_az is defined and setup_multi_az | bool
- role: setup-fake-https-server - role: setup-fake-https-server
when: https_setup is defined and https_setup | bool when: https_setup is defined and https_setup | bool
- role: config-enhanced-policy
when: config_enhanced_policy is defined and config_enhanced_policy | bool
- role: bindep - role: bindep
bindep_profile: test bindep_profile: test
bindep_dir: "{{ zuul_work_dir }}" bindep_dir: "{{ zuul_work_dir }}"

View File

@ -0,0 +1,6 @@
---
features:
- |
Enhance the existing Tacker policy function so that users can obtain more
fine-grained access control based on user roles and VNF information for
API resources.

View File

@ -0,0 +1,12 @@
- block:
- name: Copy policy.yaml
copy:
src: "{{ devstack_base_dir }}/tacker/etc/tacker/enhanced_tacker_policy.yaml.sample"
dest: "/etc/tacker/policy.yaml"
remote_src: true
mode: 0644
owner: stack
group: stack
become: yes
when:
- inventory_hostname == 'controller-tacker'

View File

@ -288,7 +288,7 @@ class VnfLcmController(wsgi.Controller):
vim_client_obj = vim_client.VimClient() vim_client_obj = vim_client.VimClient()
try: try:
vim_client_obj.get_vim( return vim_client_obj.get_vim(
context, vim_id, region_name=region_name) context, vim_id, region_name=region_name)
except nfvo.VimDefaultNotDefined as exp: except nfvo.VimDefaultNotDefined as exp:
raise webob.exc.HTTPBadRequest(explanation=exp.message) raise webob.exc.HTTPBadRequest(explanation=exp.message)
@ -439,7 +439,8 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.create) @validation.schema(vnf_lcm.create)
def create(self, request, body): def create(self, request, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'create') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'create')
try: try:
req_body = utils.convert_camelcase_to_snakecase(body) req_body = utils.convert_camelcase_to_snakecase(body)
vnfd_id = req_body.get('vnfd_id') vnfd_id = req_body.get('vnfd_id')
@ -466,6 +467,10 @@ class VnfLcmController(wsgi.Controller):
vnfd_id) vnfd_id)
return self._make_problem_detail( return self._make_problem_detail(
msg, 500, 'Internal Server Error') msg, 500, 'Internal Server Error')
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'create',
target={'vendor': vnfd.vnf_provider})
try: try:
# get default vim information # get default vim information
vim_client_obj = vim_client.VimClient() vim_client_obj = vim_client.VimClient()
@ -489,6 +494,17 @@ class VnfLcmController(wsgi.Controller):
try: try:
vnf_instance.create() vnf_instance.create()
vim_conn = objects.VimConnectionInfo(
id=uuidutils.generate_uuid(),
vim_id=default_vim.get('vim_id'),
vim_type=default_vim.get('vim_type'),
access_info={},
interface_info={},
extra=default_vim.get('extra', {})
)
vnf_instance.vim_connection_info = [vim_conn]
vnf_instance.save()
# create entry to 'vnf' table and 'vnf_attribute' table # create entry to 'vnf' table and 'vnf_attribute' table
attributes = {'placement_attr': default_vim. attributes = {'placement_attr': default_vim.
get('placement_attr', {})} get('placement_attr', {})}
@ -560,12 +576,52 @@ class VnfLcmController(wsgi.Controller):
return vnf_package_info[0] return vnf_package_info[0]
def _get_policy_target(self, vnf_instance):
vendor = vnf_instance.vnf_provider
area = None
if vnf_instance.vim_connection_info:
for connection_info in vnf_instance.vim_connection_info:
area = connection_info.extra.get('area')
if area:
break
tenant = None
if (vnf_instance.instantiation_state ==
fields.VnfInstanceState.INSTANTIATED):
if vnf_instance.vnf_metadata:
tenant = vnf_instance.vnf_metadata.get('namespace')
# TODO(kexuesheng): Add steps to get tenant of VNFs deployed
# in OpenStack VIM. This is a temporary workaround until that
# information is available.
if vnf_instance.vim_connection_info:
vim_type = vnf_instance.vim_connection_info[0].vim_type
if vim_type in ["openstack", "ETSINFV.OPENSTACK_KEYSTONE.v_2"]:
tenant = '*'
else:
tenant = '*'
target = {
'vendor': vendor,
'area': area,
'tenant': tenant
}
target = {k: v for k, v in target.items() if v is not None}
return target
@wsgi.response(http_client.OK) @wsgi.response(http_client.OK)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND)) @wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
def show(self, request, id): def show(self, request, id):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'show') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'show')
vnf_instance = self._get_vnf_instance(context, id) vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'show',
target=self._get_policy_target(vnf_instance))
return self._view_builder.show(vnf_instance) return self._view_builder.show(vnf_instance)
@ -579,8 +635,8 @@ class VnfLcmController(wsgi.Controller):
@api_common.validate_supported_params({'filter', 'nextpage_opaque_marker', @api_common.validate_supported_params({'filter', 'nextpage_opaque_marker',
'all_records'}) 'all_records'})
def index(self, request): def index(self, request):
if 'tacker.context' in request.environ: context = request.environ['tacker.context']
context = request.environ['tacker.context'] if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'index') context.can(vnf_lcm_policies.VNFLCM % 'index')
filters = request.GET.get('filter') filters = request.GET.get('filter')
@ -611,6 +667,13 @@ class VnfLcmController(wsgi.Controller):
return self._make_problem_detail( return self._make_problem_detail(
str(e), 500, title='Internal Server Error') str(e), 500, title='Internal Server Error')
if CONF.oslo_policy.enhanced_tacker_policy:
result = [vnf_instance for vnf_instance in result
if context.can(
vnf_lcm_policies.VNFLCM % 'index',
target=self._get_policy_target(vnf_instance),
fatal=False)]
result = self._view_builder.index(result) result = self._view_builder.index(result)
res = webob.Response(content_type='application/json') res = webob.Response(content_type='application/json')
@ -650,6 +713,10 @@ class VnfLcmController(wsgi.Controller):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
vnf_instance = self._get_vnf_instance(context, id) vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'delete',
target=self._get_policy_target(vnf_instance))
vnf = self._get_vnf(context, id) vnf = self._get_vnf(context, id)
self._delete(context, vnf_instance, vnf) self._delete(context, vnf_instance, vnf)
@ -683,7 +750,12 @@ class VnfLcmController(wsgi.Controller):
req_body, context=context) req_body, context=context)
# validate the vim connection id passed through request is exist or not # validate the vim connection id passed through request is exist or not
self._validate_vim_connection(context, instantiate_vnf_request) vim_res = self._validate_vim_connection(
context, instantiate_vnf_request)
if instantiate_vnf_request.vim_connection_info:
instantiate_vnf_request.vim_connection_info[0].extra = vim_res.get(
'extra', {})
vnf_instance.task_state = fields.VnfInstanceTaskState.INSTANTIATING vnf_instance.task_state = fields.VnfInstanceTaskState.INSTANTIATING
vnf_instance.save() vnf_instance.save()
@ -715,10 +787,14 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.instantiate) @validation.schema(vnf_lcm.instantiate)
def instantiate(self, request, id, body): def instantiate(self, request, id, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'instantiate') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'instantiate')
vnf = self._get_vnf(context, id) vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id) vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'instantiate',
target=self._get_policy_target(vnf_instance))
return self._instantiate(context, vnf_instance, vnf, body) return self._instantiate(context, vnf_instance, vnf, body)
@ -766,10 +842,14 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.terminate) @validation.schema(vnf_lcm.terminate)
def terminate(self, request, id, body): def terminate(self, request, id, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'terminate') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'terminate')
vnf = self._get_vnf(context, id) vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id) vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'terminate',
target=self._get_policy_target(vnf_instance))
return self._terminate(context, vnf_instance, vnf, body) return self._terminate(context, vnf_instance, vnf, body)
@check_vnf_status_and_error_point(action="heal", @check_vnf_status_and_error_point(action="heal",
@ -822,10 +902,14 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.heal) @validation.schema(vnf_lcm.heal)
def heal(self, request, id, body): def heal(self, request, id, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'heal') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'heal')
vnf = self._get_vnf(context, id) vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id) vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'heal',
target=self._get_policy_target(vnf_instance))
if vnf_instance.instantiation_state not in \ if vnf_instance.instantiation_state not in \
[fields.VnfInstanceState.INSTANTIATED]: [fields.VnfInstanceState.INSTANTIATED]:
@ -865,7 +949,12 @@ class VnfLcmController(wsgi.Controller):
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND)) @wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
def update(self, request, id, body): def update(self, request, id, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'update_vnf') if CONF.oslo_policy.enhanced_tacker_policy:
vnf_instance = self._get_vnf_instance(context, id)
context.can(vnf_lcm_policies.VNFLCM % 'update_vnf',
target=self._get_policy_target(vnf_instance))
else:
context.can(vnf_lcm_policies.VNFLCM % 'update_vnf')
# get body # get body
body_data = {} body_data = {}
@ -1343,7 +1432,8 @@ class VnfLcmController(wsgi.Controller):
http_client.NOT_FOUND, http_client.CONFLICT)) http_client.NOT_FOUND, http_client.CONFLICT))
def scale(self, request, id, body): def scale(self, request, id, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'scale') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'scale')
try: try:
vnf_info = self._vnfm_plugin.get_vnf(context, id) vnf_info = self._vnfm_plugin.get_vnf(context, id)
@ -1351,6 +1441,9 @@ class VnfLcmController(wsgi.Controller):
return self._make_problem_detail( return self._make_problem_detail(
'VNF IS NOT ACTIVE', 409, title='VNF IS NOT ACTIVE') 'VNF IS NOT ACTIVE', 409, title='VNF IS NOT ACTIVE')
vnf_instance = self._get_vnf_instance(context, id) vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'scale',
target=self._get_policy_target(vnf_instance))
if not vnf_instance.instantiated_vnf_info.scale_status: if not vnf_instance.instantiated_vnf_info.scale_status:
return self._make_problem_detail( return self._make_problem_detail(
'NOT SCALE VNF', 409, title='NOT SCALE VNF') 'NOT SCALE VNF', 409, title='NOT SCALE VNF')
@ -1361,6 +1454,8 @@ class VnfLcmController(wsgi.Controller):
except webob.exc.HTTPNotFound as inst_e: except webob.exc.HTTPNotFound as inst_e:
return self._make_problem_detail( return self._make_problem_detail(
str(inst_e), 404, title='VNF NOT FOUND') str(inst_e), 404, title='VNF NOT FOUND')
except exceptions.PolicyNotAuthorized:
raise
except Exception as e: except Exception as e:
LOG.error(traceback.format_exc()) LOG.error(traceback.format_exc())
return self._make_problem_detail( return self._make_problem_detail(
@ -1819,10 +1914,14 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.change_ext_conn) @validation.schema(vnf_lcm.change_ext_conn)
def change_ext_conn(self, request, id, body): def change_ext_conn(self, request, id, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'change_ext_conn') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'change_ext_conn')
vnf = self._get_vnf(context, id) vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id) vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'change_ext_conn',
target=self._get_policy_target(vnf_instance))
if (vnf_instance.instantiation_state != if (vnf_instance.instantiation_state !=
fields.VnfInstanceState.INSTANTIATED): fields.VnfInstanceState.INSTANTIATED):
return self._make_problem_detail( return self._make_problem_detail(

View File

@ -104,7 +104,8 @@ class VnfPkgmController(wsgi.Controller):
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND)) @wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
def show(self, request, id): def show(self, request, id):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'show') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'show')
# check if id is of type uuid format # check if id is of type uuid format
if not uuidutils.is_uuid_like(id): if not uuidutils.is_uuid_like(id):
@ -115,18 +116,33 @@ class VnfPkgmController(wsgi.Controller):
vnf_package = vnf_package_obj.VnfPackage.get_by_id( vnf_package = vnf_package_obj.VnfPackage.get_by_id(
request.context, id, expected_attrs=[ request.context, id, expected_attrs=[
"vnf_deployment_flavours", "vnfd", "vnf_artifacts"]) "vnf_deployment_flavours", "vnfd", "vnf_artifacts"])
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'show',
target=self._get_policy_target(vnf_package))
except exceptions.VnfPackageNotFound: except exceptions.VnfPackageNotFound:
msg = _("Can not find requested vnf package: %s") % id msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg) raise webob.exc.HTTPNotFound(explanation=msg)
return self._view_builder.show(vnf_package) return self._view_builder.show(vnf_package)
def _get_policy_target(self, vnf_package):
if vnf_package.onboarding_state == \
fields.PackageOnboardingStateType.ONBOARDED:
vendor = (vnf_package.vnfd.get('vnf_provider')
if vnf_package.vnfd is not None else None)
return {'vendor': vendor} if vendor else {}
else:
return {'vendor': '*'}
@wsgi.response(http_client.OK) @wsgi.response(http_client.OK)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN)) @wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
@validation.query_schema(vnf_packages.query_params_v1) @validation.query_schema(vnf_packages.query_params_v1)
def index(self, request): def index(self, request):
if 'tacker.context' in request.environ: context = request.environ['tacker.context']
context = request.environ['tacker.context'] if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'index') context.can(vnf_package_policies.VNFPKGM % 'index')
search_opts = {} search_opts = {}
@ -178,6 +194,13 @@ class VnfPkgmController(wsgi.Controller):
return self._make_problem_detail( return self._make_problem_detail(
str(e), 500, title='Internal Server Error') str(e), 500, title='Internal Server Error')
if CONF.oslo_policy.enhanced_tacker_policy:
result = [vnf_package for vnf_package in result
if context.can(
vnf_package_policies.VNFPKGM % 'index',
target=self._get_policy_target(vnf_package),
fatal=False)]
results = self._view_builder.index(result, results = self._view_builder.index(result,
all_fields=all_fields, all_fields=all_fields,
exclude_fields=exclude_fields, exclude_fields=exclude_fields,
@ -206,9 +229,13 @@ class VnfPkgmController(wsgi.Controller):
http_client.CONFLICT)) http_client.CONFLICT))
def delete(self, request, id): def delete(self, request, id):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'delete') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'delete')
vnf_package = self._get_vnf_package(id, request) vnf_package = self._get_vnf_package(id, request)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'delete',
target=self._get_policy_target(vnf_package))
if (vnf_package.operational_state == if (vnf_package.operational_state ==
fields.PackageOperationalStateType.ENABLED or fields.PackageOperationalStateType.ENABLED or
@ -244,9 +271,13 @@ class VnfPkgmController(wsgi.Controller):
http_client.REQUESTED_RANGE_NOT_SATISFIABLE)) http_client.REQUESTED_RANGE_NOT_SATISFIABLE))
def fetch_vnf_package_content(self, request, id): def fetch_vnf_package_content(self, request, id):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'fetch_package_content') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'fetch_package_content')
vnf_package = self._get_vnf_package(id, request) vnf_package = self._get_vnf_package(id, request)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'fetch_package_content',
target=self._get_policy_target(vnf_package))
if vnf_package.onboarding_state != \ if vnf_package.onboarding_state != \
fields.PackageOnboardingStateType.ONBOARDED: fields.PackageOnboardingStateType.ONBOARDED:
@ -367,7 +398,9 @@ class VnfPkgmController(wsgi.Controller):
http_client.CONFLICT)) http_client.CONFLICT))
def upload_vnf_package_content(self, request, id, body): def upload_vnf_package_content(self, request, id, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'upload_package_content') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(
vnf_package_policies.VNFPKGM % 'upload_package_content')
# check if id is of type uuid format # check if id is of type uuid format
if not uuidutils.is_uuid_like(id): if not uuidutils.is_uuid_like(id):
@ -404,6 +437,13 @@ class VnfPkgmController(wsgi.Controller):
try: try:
(location, size, checksum, multihash, (location, size, checksum, multihash,
loc_meta) = glance_store.store_csar(context, id, body) loc_meta) = glance_store.store_csar(context, id, body)
if CONF.oslo_policy.enhanced_tacker_policy:
zip_path = glance_store.load_csar(vnf_package.id, location)
vnf_data, flavours, vnf_artifacts = csar_utils.load_csar_data(
context.elevated(), vnf_package.id, zip_path)
context.can(
vnf_package_policies.VNFPKGM % 'upload_package_content',
target={'vendor': vnf_data.get('provider')})
except exceptions.UploadFailedToGlanceStore: except exceptions.UploadFailedToGlanceStore:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
vnf_package.onboarding_state = ( vnf_package.onboarding_state = (
@ -413,6 +453,19 @@ class VnfPkgmController(wsgi.Controller):
except Exception as e: except Exception as e:
return self._make_problem_detail( return self._make_problem_detail(
'Internal Server Error', str(e), 500) 'Internal Server Error', str(e), 500)
except exceptions.Forbidden:
with excutils.save_and_reraise_exception():
vnf_package.onboarding_state = (
fields.PackageOnboardingStateType.CREATED)
try:
# Delete from glance store
glance_store.delete_csar(context, vnf_package.id,
location)
csar_utils.delete_csar_data(vnf_package.id)
vnf_package.save()
except Exception as e:
return self._make_problem_detail(
'Internal Server Error', str(e), 500)
vnf_package.algorithm = CONF.vnf_package.hashing_algorithm vnf_package.algorithm = CONF.vnf_package.hashing_algorithm
vnf_package.hash = multihash vnf_package.hash = multihash
@ -475,9 +528,13 @@ class VnfPkgmController(wsgi.Controller):
@validation.schema(vnf_packages.patch) @validation.schema(vnf_packages.patch)
def patch(self, request, id, body): def patch(self, request, id, body):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'patch') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'patch')
old_vnf_package = self._get_vnf_package(id, request) old_vnf_package = self._get_vnf_package(id, request)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'patch',
target=self._get_policy_target(old_vnf_package))
vnf_package = old_vnf_package.obj_clone() vnf_package = old_vnf_package.obj_clone()
user_data = body.get('userDefinedData') user_data = body.get('userDefinedData')
@ -529,7 +586,8 @@ class VnfPkgmController(wsgi.Controller):
http_client.INTERNAL_SERVER_ERROR)) http_client.INTERNAL_SERVER_ERROR))
def get_vnf_package_vnfd(self, request, id): def get_vnf_package_vnfd(self, request, id):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'get_vnf_package_vnfd') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'get_vnf_package_vnfd')
valid_accept_headers = ['application/zip', 'text/plain'] valid_accept_headers = ['application/zip', 'text/plain']
accept_headers = request.headers['Accept'].split(',') accept_headers = request.headers['Accept'].split(',')
@ -543,6 +601,9 @@ class VnfPkgmController(wsgi.Controller):
valid_accept_headers)}) valid_accept_headers)})
vnf_package = self._get_vnf_package(id, request) vnf_package = self._get_vnf_package(id, request)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'get_vnf_package_vnfd',
target=self._get_policy_target(vnf_package))
if vnf_package.onboarding_state != \ if vnf_package.onboarding_state != \
fields.PackageOnboardingStateType.ONBOARDED: fields.PackageOnboardingStateType.ONBOARDED:
@ -586,7 +647,8 @@ class VnfPkgmController(wsgi.Controller):
def fetch_vnf_package_artifacts(self, request, id, artifact_path): def fetch_vnf_package_artifacts(self, request, id, artifact_path):
context = request.environ['tacker.context'] context = request.environ['tacker.context']
# get policy # get policy
context.can(vnf_package_policies.VNFPKGM % 'fetch_artifact') if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'fetch_artifact')
# get vnf_package # get vnf_package
if not uuidutils.is_uuid_like(id): if not uuidutils.is_uuid_like(id):
@ -597,6 +659,10 @@ class VnfPkgmController(wsgi.Controller):
vnf_package = vnf_package_obj.VnfPackage.get_by_id( vnf_package = vnf_package_obj.VnfPackage.get_by_id(
request.context, id, request.context, id,
expected_attrs=["vnf_artifacts"]) expected_attrs=["vnf_artifacts"])
if CONF.oslo_policy.enhanced_tacker_policy:
# get policy
context.can(vnf_package_policies.VNFPKGM % 'fetch_artifact',
target=self._get_policy_target(vnf_package))
except exceptions.VnfPackageNotFound: except exceptions.VnfPackageNotFound:
msg = _("Can not find requested vnf package: %s") % id msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg) raise webob.exc.HTTPNotFound(explanation=msg)

View File

@ -174,6 +174,18 @@ def is_valid_ipv4(address):
return False return False
def is_valid_area(area):
"""Verify that the area attribute is valid.
Area attribute is an area-region pair. The value of this attribute should
be a string in the format of "area@region".
"""
try:
return re.match(r'\w+@\w+', area) is not None
except Exception:
return False
def change_memory_unit(mem, to): def change_memory_unit(mem, to):
"""Change the memory value(mem) based on the unit('to') specified. """Change the memory value(mem) based on the unit('to') specified.

View File

@ -18,6 +18,7 @@ from oslo_config import cfg
from tacker.conf import conductor from tacker.conf import conductor
from tacker.conf import coordination from tacker.conf import coordination
from tacker.conf import policy
from tacker.conf import vnf_lcm from tacker.conf import vnf_lcm
from tacker.conf import vnf_package from tacker.conf import vnf_package
@ -29,3 +30,4 @@ vnf_lcm.register_opts(CONF)
conductor.register_opts(CONF) conductor.register_opts(CONF)
coordination.register_opts(CONF) coordination.register_opts(CONF)
glance_store.register_opts(CONF) glance_store.register_opts(CONF)
policy.register_opts(CONF)

33
tacker/conf/policy.py Normal file
View File

@ -0,0 +1,33 @@
# Copyright (C) 2023 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 oslo_config import cfg
CONF = cfg.CONF
policy_opts = [
cfg.BoolOpt('enhanced_tacker_policy',
default=False,
help=_('Enable enhanced tacker policy')),
]
def register_opts(conf):
conf.register_opts(policy_opts, group='oslo_policy')
def list_opts():
return {'oslo_policy': policy_opts}

View File

@ -148,11 +148,14 @@ class ContextBase(oslo_context.RequestContext):
:return: returns a non-False value (not necessarily "True") if :return: returns a non-False value (not necessarily "True") if
authorized and False if not authorized and fatal is False. authorized and False if not authorized and fatal is False.
""" """
if target is None: tgt = {
target = {'project_id': self.tenant_id, 'project_id': self.tenant_id,
'user_id': self.user_id} 'user_id': self.user_id
}
if target is not None:
tgt.update(target)
try: try:
return policy.authorize(self, action, target) return policy.authorize(self, action, tgt)
except exceptions.Forbidden: except exceptions.Forbidden:
if fatal: if fatal:
raise raise

View File

@ -14,6 +14,7 @@
# under the License. # under the License.
from collections import abc from collections import abc
import copy
import re import re
import sys import sys
@ -28,6 +29,7 @@ from oslo_utils import importutils
from tacker._i18n import _ from tacker._i18n import _
from tacker.api.v1 import attributes from tacker.api.v1 import attributes
from tacker.common import exceptions from tacker.common import exceptions
from tacker.common.utils import is_valid_area
from tacker import policies from tacker import policies
@ -60,10 +62,133 @@ def init(conf=cfg.CONF, policy_file=None):
_ENFORCER.load_rules() _ENFORCER.load_rules()
def _pre_enhanced_policy_check(target, credentials):
"""Preprocesses target and credentials for enhanced tacker policy.
This method does the following things:
1) Convert special roles to enhanced policy attributes in credentials.
Note: Special roles are roles that have prefixes 'AREA_', 'VENDOR_',
or 'TENANT_'.
Example::
Before conversion:
credentials = {
'roles': [
'AREA_area_A@region_A',
'VENDOR_company_A',
'TENANT_default'
]
}
After conversion:
credentials = {
'roles': [
'AREA_area_A@region_A',
'VENDOR_company_A',
'TENANT_default'
],
'area': ['*', 'area_A@region_A'],
'vendor': ['*', 'company_A'],
'tenant: ['*', 'default']
}
2) Convert special value `all` to the corresponding attribute value in
target.
:param target: a dictionary of the attributes of the object
being accessed.
:param credentials: The information about the user performing the action.
:return tgt: The preprocessed target is returned.
:return user_attrs: The preprocessed credentials is returned.
"""
if not cfg.CONF.oslo_policy.enhanced_tacker_policy:
return target, credentials
if target is None:
tgt = {}
else:
tgt = copy.copy(target)
LOG.debug(f'target: {target}')
convert_map = {
'area_': 'area',
'vendor_': 'vendor',
'tenant_': 'tenant'
}
user_attrs = {
'area': ['*'],
'vendor': ['*'],
'tenant': ['*']
}
# Convert special roles to enhanced policy attributes in credentials.
for role in credentials.get('roles'):
role = role.lower()
for prefix, key in convert_map.items():
if role.startswith(prefix):
attr = role[len(prefix):]
if attr:
user_attrs[key].append(attr)
common_keys = user_attrs.keys() & tgt.keys()
# Convert special value `all` to the corresponding attribute value in
# target.
for key in common_keys:
tgt[key] = tgt[key].lower()
attrs = user_attrs.get(key)
if tgt.get(key) == '*':
continue
to_remove = []
if 'area' == key:
if not is_valid_area(tgt.get(key)):
continue
for attr in attrs:
if not is_valid_area(attr):
continue
if 'all@all' == attr:
# example:
# target = {'area': 'area_A@region_A'}
# then:
# 'all@all' -> 'area_A@region_A'
to_remove.append('all@all')
attrs.append(tgt.get(key))
elif attr.startswith('all@'):
to_remove.append(attr)
area, region = attr.split('@', 1)
t_area, t_region = tgt.get(key).split('@', 1)
if region == t_region:
# example:
# target = {'area': 'area_A@region_A'}
# then:
# 'all@region_A' -> 'area_A@region_A'
attrs.append(f'{t_area}@{region}')
# else:
# example:
# target = {'area': 'area_A@region_B'}
# then:
# 'all@region_A' -> to be removed.
else:
for attr in attrs:
if 'all' == attr:
# example:
# target = {'vendor': 'company_A'}
# then:
# 'all' -> 'company_A'
to_remove.append('all')
attrs.append(tgt.get(key))
for item in to_remove:
attrs.remove(item)
user_attrs.update(credentials)
return tgt, user_attrs
def authorize(context, action, target, do_raise=True, exc=None): def authorize(context, action, target, do_raise=True, exc=None):
init() init()
credentials = context.to_policy_values() credentials = context.to_policy_values()
target, credentials = _pre_enhanced_policy_check(target, credentials)
if not exc: if not exc:
exc = exceptions.PolicyNotAuthorized exc = exceptions.PolicyNotAuthorized
try: try:
@ -387,6 +512,15 @@ def check(context, action, target, plugin=None, might_not_exist=False,
action, action,
target, target,
pluralized) pluralized)
target = copy.copy(target)
if 'area' not in target:
area = target.get('extra', {}).get('area')
if area:
target.update({'area': area})
if 'tenant_id' in target:
target['project_id'] = target['tenant_id']
target, credentials = _pre_enhanced_policy_check(target, credentials)
result = _ENFORCER.enforce(match_rule, result = _ENFORCER.enforce(match_rule,
target, target,
credentials, credentials,
@ -397,7 +531,8 @@ def check(context, action, target, plugin=None, might_not_exist=False,
return result return result
def enforce(context, action, target, plugin=None, pluralized=None): def enforce(context, action, target, plugin=None, pluralized=None,
exc=exceptions.PolicyNotAuthorized):
"""Verifies that the action is valid on the target in this context. """Verifies that the action is valid on the target in this context.
:param context: tacker context :param context: tacker context
@ -410,9 +545,12 @@ def enforce(context, action, target, plugin=None, pluralized=None):
Kept for backward compatibility. Kept for backward compatibility.
:param pluralized: pluralized case of resource :param pluralized: pluralized case of resource
e.g. firewall_policy -> pluralized = "firewall_policies" e.g. firewall_policy -> pluralized = "firewall_policies"
:param exc: Class of the exception to raise if the check fails.
If not specified, :class:`PolicyNotAuthorized` will be used.
:raises oslo_policy.policy.PolicyNotAuthorized: :raises tacker.common.exceptions.PolicyNotAuthorized or exc specified by
if verification fails. caller:
if verification fails.
""" """
# If we already know the context has admin rights do not perform an # If we already know the context has admin rights do not perform an
# additional check and authorize the operation # additional check and authorize the operation
@ -422,10 +560,19 @@ def enforce(context, action, target, plugin=None, pluralized=None):
action, action,
target, target,
pluralized) pluralized)
target = copy.copy(target)
if 'area' not in target:
area = target.get('extra', {}).get('area')
if area:
target.update({'area': area})
if 'tenant_id' in target:
target['project_id'] = target['tenant_id']
target, credentials = _pre_enhanced_policy_check(target, credentials)
try: try:
result = _ENFORCER.enforce(rule, target, credentials, action=action, result = _ENFORCER.enforce(rule, target, credentials, action=action,
do_raise=True) do_raise=True, exc=exc)
except policy.PolicyNotAuthorized: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
log_rule_list(rule) log_rule_list(rule)
LOG.error("Failed policy check for '%s'", action) LOG.error("Failed policy check for '%s'", action)

View File

@ -36,6 +36,20 @@ VNF_LCM_OP_OCCS_PATH = V2_PATH + '/vnf_lcm_op_occs'
VNF_LCM_OP_OCCS_ID_PATH = VNF_LCM_OP_OCCS_PATH + '/{vnfLcmOpOccId}' VNF_LCM_OP_OCCS_ID_PATH = VNF_LCM_OP_OCCS_PATH + '/{vnfLcmOpOccId}'
SERVER_NOTIFICATION_PATH = CONF.server_notification.uri_path_prefix SERVER_NOTIFICATION_PATH = CONF.server_notification.uri_path_prefix
ENHANCED_POLICY_ACTIONS = (
POLICY_NAME.format('create'),
POLICY_NAME.format('index'),
POLICY_NAME.format('show'),
POLICY_NAME.format('delete'),
POLICY_NAME.format('update'),
POLICY_NAME.format('instantiate'),
POLICY_NAME.format('terminate'),
POLICY_NAME.format('scale'),
POLICY_NAME.format('heal'),
POLICY_NAME.format('change_ext_conn'),
POLICY_NAME.format('change_vnfpkg'),
)
rules = [ rules = [
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=POLICY_NAME.format('api_versions'), name=POLICY_NAME.format('api_versions'),

View File

@ -24,6 +24,8 @@ from tacker.common import exceptions as common_ex
from tacker import context from tacker import context
from tacker.sol_refactored.api import api_version from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.api.policies.vnflcm_v2 import (
ENHANCED_POLICY_ACTIONS)
from tacker.sol_refactored.common import config from tacker.sol_refactored.common import config
from tacker.sol_refactored.common import exceptions as sol_ex from tacker.sol_refactored.common import exceptions as sol_ex
@ -159,7 +161,9 @@ class SolResource(object):
return return
if action == 'reject': if action == 'reject':
return return
request.context.can(self.policy_name.format(action)) if not (self.policy_name.format(action) in ENHANCED_POLICY_ACTIONS
and config.CONF.oslo_policy.enhanced_tacker_policy):
request.context.can(self.policy_name.format(action))
def _dispatch(self, request, action, action_args): def _dispatch(self, request, action, action_args):
controller_method = getattr(self.controller, action) controller_method = getattr(self.controller, action)

View File

@ -48,6 +48,7 @@ def vim_to_conn_info(vim):
region = vim['placement_attr']['regions'][0] region = vim['placement_attr']['regions'][0]
vim_auth = vim['vim_auth'] vim_auth = vim['vim_auth']
extra = vim.get('extra', {})
if vim['vim_type'] == "openstack": if vim['vim_type'] == "openstack":
# see. https://nfvwiki.etsi.org/index.php # see. https://nfvwiki.etsi.org/index.php
@ -73,7 +74,8 @@ def vim_to_conn_info(vim):
vimId=vim['vim_id'], vimId=vim['vim_id'],
vimType='ETSINFV.OPENSTACK_KEYSTONE.V_3', vimType='ETSINFV.OPENSTACK_KEYSTONE.V_3',
interfaceInfo=interface_info, interfaceInfo=interface_info,
accessInfo=access_info accessInfo=access_info,
extra=extra
) )
if vim['vim_type'] == "kubernetes": if vim['vim_type'] == "kubernetes":
# When vimType is kubernetes, it will be converted to the vimType name # When vimType is kubernetes, it will be converted to the vimType name
@ -116,7 +118,8 @@ def vim_to_conn_info(vim):
vimId=vim['vim_id'], vimId=vim['vim_id'],
vimType=vim_type, vimType=vim_type,
interfaceInfo=interface_info, interfaceInfo=interface_info,
accessInfo=access_info accessInfo=access_info,
extra=extra
) )
raise sol_ex.SolException(sol_detail='not support vim type') raise sol_ex.SolException(sol_detail='not support vim type')

View File

@ -33,8 +33,9 @@ from tacker.sol_refactored.objects.v2 import fields as v2fields
# and the creation of lcmocc and the call to start_lcm_op are # and the creation of lcmocc and the call to start_lcm_op are
# all executed by the controller, notification driver, etc. # all executed by the controller, notification driver, etc.
@coordinate.lock_vnf_instance('{vnf_instance_id}') @coordinate.lock_vnf_instance('{vnf_instance_id}')
def heal(context, vnf_instance_id, body): def heal(context, vnf_instance_id, body, inst=None):
inst = inst_utils.get_inst(context, vnf_instance_id) if not inst:
inst = inst_utils.get_inst(context, vnf_instance_id)
if inst.instantiationState != 'INSTANTIATED': if inst.instantiationState != 'INSTANTIATED':
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=vnf_instance_id) raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=vnf_instance_id)
@ -73,8 +74,9 @@ def heal(context, vnf_instance_id, body):
# and the creation of lcmocc and the call to start_lcm_op are # and the creation of lcmocc and the call to start_lcm_op are
# all executed by the controller, notification driver, etc. # all executed by the controller, notification driver, etc.
@coordinate.lock_vnf_instance('{vnf_instance_id}') @coordinate.lock_vnf_instance('{vnf_instance_id}')
def scale(context, vnf_instance_id, body): def scale(context, vnf_instance_id, body, inst=None):
inst = inst_utils.get_inst(context, vnf_instance_id) if not inst:
inst = inst_utils.get_inst(context, vnf_instance_id)
if inst.instantiationState != 'INSTANTIATED': if inst.instantiationState != 'INSTANTIATED':
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=vnf_instance_id) raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=vnf_instance_id)

View File

@ -18,6 +18,7 @@ from oslo_log import log as logging
from oslo_utils import uuidutils from oslo_utils import uuidutils
from tacker.sol_refactored.api import api_version from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.api.policies.vnflcm_v2 import POLICY_NAME
from tacker.sol_refactored.api.schemas import vnflcm_v2 as schema from tacker.sol_refactored.api.schemas import vnflcm_v2 as schema
from tacker.sol_refactored.api import validator from tacker.sol_refactored.api import validator
from tacker.sol_refactored.api import wsgi as sol_wsgi from tacker.sol_refactored.api import wsgi as sol_wsgi
@ -62,6 +63,10 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
pkg_info = self.nfvo_client.get_vnf_package_info_vnfd( pkg_info = self.nfvo_client.get_vnf_package_info_vnfd(
context, vnfd_id) context, vnfd_id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('create'),
target={'vendor': pkg_info.vnfProvider})
if pkg_info.operationalState != "ENABLED": if pkg_info.operationalState != "ENABLED":
raise sol_ex.VnfdIdNotEnabled(vnfd_id=vnfd_id) raise sol_ex.VnfdIdNotEnabled(vnfd_id=vnfd_id)
@ -122,6 +127,11 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
insts = inst_utils.get_inst_all(request.context, insts = inst_utils.get_inst_all(request.context,
marker=pager.marker) marker=pager.marker)
if config.CONF.oslo_policy.enhanced_tacker_policy:
insts = [inst for inst in insts if request.context.can(
POLICY_NAME.format('index'),
target=self._get_policy_target(inst),
fatal=False)]
resp_body = self._inst_view.detail_list(insts, filters, resp_body = self._inst_view.detail_list(insts, filters,
selector, pager) selector, pager)
@ -130,7 +140,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def show(self, request, id): def show(self, request, id):
inst = inst_utils.get_inst(request.context, id) inst = inst_utils.get_inst(request.context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
request.context.can(POLICY_NAME.format('show'),
target=self._get_policy_target(inst))
resp_body = self._inst_view.detail(inst) resp_body = self._inst_view.detail(inst)
return sol_wsgi.SolResponse(200, resp_body) return sol_wsgi.SolResponse(200, resp_body)
@ -139,7 +151,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def delete(self, request, id): def delete(self, request, id):
context = request.context context = request.context
inst = inst_utils.get_inst(context, id) inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('delete'),
target=self._get_policy_target(inst))
if inst.instantiationState != 'NOT_INSTANTIATED': if inst.instantiationState != 'NOT_INSTANTIATED':
raise sol_ex.VnfInstanceIsInstantiated(inst_id=id) raise sol_ex.VnfInstanceIsInstantiated(inst_id=id)
@ -158,7 +172,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def update(self, request, id, body): def update(self, request, id, body):
context = request.context context = request.context
inst = inst_utils.get_inst(context, id) inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('update'),
target=self._get_policy_target(inst))
lcmocc_utils.check_lcmocc_in_progress(context, id) lcmocc_utils.check_lcmocc_in_progress(context, id)
if (inst.instantiationState == 'NOT_INSTANTIATED' if (inst.instantiationState == 'NOT_INSTANTIATED'
and 'vimConnectionInfo' in body): and 'vimConnectionInfo' in body):
@ -200,11 +216,54 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
return sol_wsgi.SolResponse(202, None, location=location) return sol_wsgi.SolResponse(202, None, location=location)
def _get_policy_target(self, vnf_instance):
vendor = vnf_instance.vnfProvider
if vnf_instance.instantiationState == 'NOT_INSTANTIATED':
area = '*'
tenant = '*'
else:
vim_type = None
area = None
if vnf_instance.obj_attr_is_set('vimConnectionInfo'):
for _, vim_conn_info in vnf_instance.vimConnectionInfo.items():
area = vim_conn_info.get('extra', {}).get('area')
vim_type = vim_conn_info.vimType
if area and vim_type:
break
tenant = None
if (vnf_instance.obj_attr_is_set('instantiatedVnfInfo') and
vnf_instance.instantiatedVnfInfo.obj_attr_is_set(
'metadata')):
tenant = (vnf_instance.instantiatedVnfInfo
.metadata.get('namespace'))
# TODO(kexuesheng): Add steps to get tenant of VNFs deployed
# in OpenStack VIM. This is a temporary workaround until that
# information is available.
if vim_type == "ETSINFV.OPENSTACK_KEYSTONE.V_3":
tenant = '*'
target = {
'vendor': vendor,
'area': area,
'tenant': tenant
}
target = {k: v for k, v in target.items() if v is not None}
return target
@validator.schema(schema.InstantiateVnfRequest_V200, '2.0.0') @validator.schema(schema.InstantiateVnfRequest_V200, '2.0.0')
@coordinate.lock_vnf_instance('{id}') @coordinate.lock_vnf_instance('{id}')
def instantiate(self, request, id, body): def instantiate(self, request, id, body):
context = request.context context = request.context
inst = inst_utils.get_inst(context, id) inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('instantiate'),
target=self._get_policy_target(inst))
if inst.instantiationState != 'NOT_INSTANTIATED': if inst.instantiationState != 'NOT_INSTANTIATED':
raise sol_ex.VnfInstanceIsInstantiated(inst_id=id) raise sol_ex.VnfInstanceIsInstantiated(inst_id=id)
@ -239,6 +298,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def terminate(self, request, id, body): def terminate(self, request, id, body):
context = request.context context = request.context
inst = inst_utils.get_inst(context, id) inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('terminate'),
target=self._get_policy_target(inst))
if inst.instantiationState != 'INSTANTIATED': if inst.instantiationState != 'INSTANTIATED':
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=id) raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=id)
@ -258,7 +320,12 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
@validator.schema(schema.ScaleVnfRequest_V200, '2.0.0') @validator.schema(schema.ScaleVnfRequest_V200, '2.0.0')
def scale(self, request, id, body): def scale(self, request, id, body):
context = request.context context = request.context
lcmocc = vnflcm_utils.scale(context, id, body) inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('scale'),
target=self._get_policy_target(inst))
lcmocc = vnflcm_utils.scale(context, id, body, inst=inst)
location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint) location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)
@ -267,7 +334,12 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
@validator.schema(schema.HealVnfRequest_V200, '2.0.0') @validator.schema(schema.HealVnfRequest_V200, '2.0.0')
def heal(self, request, id, body): def heal(self, request, id, body):
context = request.context context = request.context
lcmocc = vnflcm_utils.heal(context, id, body) inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('heal'),
target=self._get_policy_target(inst))
lcmocc = vnflcm_utils.heal(context, id, body, inst=inst)
location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint) location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)
@ -278,6 +350,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def change_ext_conn(self, request, id, body): def change_ext_conn(self, request, id, body):
context = request.context context = request.context
inst = inst_utils.get_inst(context, id) inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('change_ext_conn'),
target=self._get_policy_target(inst))
if inst.instantiationState != 'INSTANTIATED': if inst.instantiationState != 'INSTANTIATED':
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=id) raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=id)
@ -336,6 +411,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def change_vnfpkg(self, request, id, body): def change_vnfpkg(self, request, id, body):
context = request.context context = request.context
inst = inst_utils.get_inst(context, id) inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('change_vnfpkg'),
target=self._get_policy_target(inst))
vnfd_id = body['vnfdId'] vnfd_id = body['vnfdId']
if inst.instantiationState != 'INSTANTIATED': if inst.instantiationState != 'INSTANTIATED':

View File

@ -0,0 +1,38 @@
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: 2
desired_capacity: 1
resource:
type: vdu1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, flavor ] }
image: { get_param: [ nfv, VDU, VDU1, image ] }
net1: { get_param: [ nfv, CP, CP1, network ] }
VDU1_scale_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_scale_in:
type: OS::Heat::ScalingPolicy
properties:
scaling_adjustment: -1
auto_scaling_group_id:
get_resource: VDU1_scale
adjustment_type: change_in_capacity
outputs: {}

View File

@ -0,0 +1,28 @@
heat_template_version: 2013-05-23
description: "Template for test _generate_hot_from_tosca()."
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: CP1
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
outputs: {}

View File

@ -0,0 +1,195 @@
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
- sample_vnfd_types.yaml
topology_template:
inputs:
id:
type: string
vendor:
type: string
version:
type: version
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_external: []
node_templates:
VNF:
type: company.provider.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
# supporting only 'instantiate', 'terminate', 'modify',
# and 'heal'
# 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: 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: 1 GB
size: 1 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
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ipv4]
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL1
internalVL1:
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: 10.0.0.0/24
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: 1
targets: [VDU1]
- 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]

View File

@ -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
- sample_vnfd_types.yaml
- sample_vnfd_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: c1bb0ce7-ebca-4fa7-95ed-4840d70a1175
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

View File

@ -0,0 +1,63 @@
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:
id:
type: string
description: ID of this VNF
default: vnf_id
vendor:
type: string
description: name of the vendor who generate this VNF
default: vendor
version:
type: version
description: version of the software for this VNF
default: 1.0
descriptor_id:
type: string
constraints: [valid_values: [c1bb0ce7-ebca-4fa7-95ed-4840d70a1175]]
default: c1bb0ce7-ebca-4fa7-95ed-4840d70a1175
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: This is the default flavour description
requirements:
- virtual_link_internal:
capability: tosca.capabilities.nfv.VirtualLinkable
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm

View File

@ -0,0 +1,4 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Dummy User
CSAR-Version: 1.1
Entry-Definitions: Definitions/sample_vnfd_top.yaml

View File

@ -0,0 +1,35 @@
#
# 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.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
import tacker.vnfm.lcm_user_data.utils as UserDataUtil
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
api_param = UserDataUtil.get_diff_base_hot_param_from_api(
base_hot_dict, inst_req_info)
initial_param_dict = \
UserDataUtil.create_initial_param_server_port_dict(
base_hot_dict)
vdu_flavor_dict = \
UserDataUtil.create_vdu_flavor_capability_name_dict(vnfd_dict)
vdu_image_dict = UserDataUtil.create_sw_image_dict(vnfd_dict)
cpd_vl_dict = UserDataUtil.create_network_dict(
inst_req_info, initial_param_dict)
final_param_dict = UserDataUtil.create_final_param_dict(
initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict)
return {**final_param_dict, **api_param}

View File

@ -78,7 +78,8 @@ class SessionClient(adapter.Adapter):
def do_request(self, url, method, **kwargs): def do_request(self, url, method, **kwargs):
kwargs.setdefault('authenticated', True) kwargs.setdefault('authenticated', True)
resp = self.request(url, method, **kwargs) resp = self.request(url, method, **kwargs)
if resp.headers['Content-Type'] == 'application/zip': if ('Content-Type' not in resp.headers or
resp.headers['Content-Type'] == 'application/zip'):
return resp, resp.content return resp, resp.content
body = self._decode_json(resp) body = self._decode_json(resp)
return resp, body return resp, body

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
#
# 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.tests.functional.sol_enhanced_policy.base import (
VimAPIsTest)
class VimAPIsOpenstackTest(VimAPIsTest):
def test_vim_apis_vim_with_area_openstack(self):
self._test_vim_apis_enhanced_policy('openstack', 'local-vim.yaml')
def test_vim_apis_vim_without_area_openstack(self):
self._test_vim_apis_vim_without_area_attribute(
'openstack', 'local-vim.yaml')

View File

@ -0,0 +1,244 @@
#
# 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.path
import time
from oslo_serialization import jsonutils
from tacker.tests.functional.base import BaseTackerTest
from tacker.tests.functional.sol_enhanced_policy.base import (
BaseEnhancedPolicyTest)
class BaseVnfPackageAPIsTest(BaseTackerTest, BaseEnhancedPolicyTest):
VNF_PACKAGE_UPLOAD_TIMEOUT = 300
base_url = '/vnfpkgm/v1/vnf_packages'
user_role_map = {
'user_a': ['VENDOR_company_A', 'manager'],
'user_b': ['VENDOR_company_B', 'manager'],
'user_all': ['VENDOR_all', 'manager'],
}
@classmethod
def setUpClass(cls):
BaseTackerTest.setUpClass()
BaseEnhancedPolicyTest.setUpClass(cls)
@classmethod
def tearDownClass(cls):
BaseEnhancedPolicyTest.tearDownClass()
BaseTackerTest.tearDownClass()
def _step_pkg_create(self, username):
client = self.get_tk_http_client_by_user(username)
resp, pkg = client.do_request(
self.base_url, 'POST',
body=jsonutils.dumps({"userDefinedData": {"foo": "bar"}}))
self.assertEqual(201, resp.status_code)
return pkg
def _step_pkg_show(self, username, pkg, expected_status_code,
expected_vendor=None):
client = self.get_tk_http_client_by_user(username)
resp, pkg = client.do_request(
os.path.join(self.base_url, pkg.get('id')),
'GET')
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 200 and expected_vendor:
self.assertEqual(expected_vendor, pkg.get('vnfProvider'))
def _step_pkg_list(self, username, expected_pkg_list):
client = self.get_tk_http_client_by_user(username)
resp, pkgs = client.do_request(self.base_url, 'GET')
self.assertEqual(200, resp.status_code)
pkg_ids = set([pkg.get('id') for pkg in pkgs])
for pkg in expected_pkg_list:
self.assertIn(pkg.get('id'), pkg_ids)
def _get_csar_dir_path(self, csar_name):
csar_dir = os.path.abspath(
os.path.join(os.path.dirname(__file__),
"../../../etc/samples/etsi/nfv", csar_name))
return csar_dir
def _wait_for_onboard(self, client, package_uuid):
show_url = os.path.join(self.base_url, package_uuid)
timeout = self.VNF_PACKAGE_UPLOAD_TIMEOUT
start_time = int(time.time())
while True:
resp, body = client.do_request(show_url, "GET")
if body['onboardingState'] == "ONBOARDED":
break
if (int(time.time()) - start_time) > timeout:
raise Exception("Failed to onboard vnf package")
time.sleep(1)
def _step_pkg_upload_content(self, username, pkg, csar_name, provider,
expected_status_code):
client = self.get_tk_http_client_by_user(username)
csar_dir = self._get_csar_dir_path(csar_name)
file_path, vnfd_id = self.custom_csar(csar_dir, provider)
self.addCleanup(os.remove, file_path)
with open(file_path, 'rb') as file_object:
resp, resp_body = client.do_request(
'{base_path}/{id}/package_content'.format(
id=pkg['id'],
base_path=self.base_url),
"PUT", body=file_object, content_type='application/zip')
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
self._wait_for_onboard(client, pkg['id'])
def _step_pkg_read_vnfd(self, username, pkg, expected_status_code):
client = self.get_tk_http_client_by_user(username)
resp, resp_body = client.do_request(
'{base_path}/{id}/vnfd'.format(id=pkg['id'],
base_path=self.base_url),
"GET", content_type='application/zip')
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 200:
self.assertEqual('application/zip', resp.headers['Content-Type'])
self.assertIsNotNone(resp.text)
def _step_pkg_fetch(self, username, pkg, expected_status_code):
client = self.get_tk_http_client_by_user(username)
response = client.do_request(
'{base_path}/{id}/package_content'.format(
id=pkg['id'], base_path=self.base_url),
"GET", body={}, headers={})
self.assertEqual(expected_status_code, response[0].status_code)
def _step_pkg_update(self, username, pkg, expected_status_code):
client = self.get_tk_http_client_by_user(username)
update_req_body = jsonutils.dumps({
"operationalState": "DISABLED"})
resp, _ = client.do_request(
'{base_path}/{id}'.format(id=pkg['id'],
base_path=self.base_url),
"PATCH", content_type='application/json', body=update_req_body)
self.assertEqual(expected_status_code, resp.status_code)
def _step_pkg_delete(self, username, pkg, expected_status_code):
client = self.get_tk_http_client_by_user(username)
resp, _ = client.do_request(os.path.join(self.base_url, pkg.get('id')),
'DELETE')
self.assertEqual(expected_status_code, resp.status_code)
def _test_vnf_package_apis_enhanced_policy(self, csar_name):
# step 1 PKG-Create, Resource Group A / User Group A
pkg_a = self._step_pkg_create('user_a')
# step 2 PKG-Create, Resource Group B / User Group all
pkg_b = self._step_pkg_create('user_all')
# step 3 PKG-Show, Resource Group A / User Group A
self._step_pkg_show('user_a', pkg_a, 200)
# step 4 PKG-Show, Resource Group B / User Group A
self._step_pkg_show('user_a', pkg_b, 200)
# step 5 PKG-Show, Resource Group A / User Group A
self._step_pkg_show('user_all', pkg_b, 200)
# step 6 PKG-List, Resource Group - / User Group A
self._step_pkg_list('user_a', [pkg_a, pkg_b])
# step 7 PKG-List, Resource Group - / User Group B
self._step_pkg_list('user_b', [pkg_a, pkg_b])
# step 8 PKG-List, Resource Group - / User Group all
self._step_pkg_list('user_all', [pkg_a, pkg_b])
# step 9 PKG-Upload-content, Resource Group B / User Group A
self._step_pkg_upload_content(
'user_a', pkg_a, csar_name, 'company_B', 403)
# step 10 PKG-Upload-content, Resource Group A / User Group A
self._step_pkg_upload_content(
'user_a', pkg_a, csar_name, 'company_A', 202)
# step 11 PKG-Upload-content, Resource Group B / User Group all
self._step_pkg_upload_content(
'user_all', pkg_b, csar_name, 'company_B', 202)
# step 12 PKG-Show, Resource Group A / User Group A
self._step_pkg_show('user_a', pkg_a, 200)
# step 13 PKG-Show, Resource Group B / User Group A
self._step_pkg_show('user_a', pkg_b, 403)
# step 14 PKG-Show, Resource Group A / User Group A
self._step_pkg_show('user_all', pkg_b, 200)
# step 15 PKG-List, Resource Group - / User Group A
self._step_pkg_list('user_a', [pkg_a])
# step 16 PKG-List, Resource Group - / User Group B
self._step_pkg_list('user_b', [pkg_b])
# step 17 PKG-List, Resource Group - / User Group all
self._step_pkg_list('user_all', [pkg_a, pkg_b])
# step 18 PKG-Read-vnfd, Resource Group A / User Group A
self._step_pkg_read_vnfd('user_a', pkg_a, 200)
# step 19 PKG-Read-vnfd, Resource Group B / User Group A
self._step_pkg_read_vnfd('user_a', pkg_b, 403)
# step 20 PKG-Read-vnfd, Resource Group B / User Group all
self._step_pkg_read_vnfd('user_all', pkg_b, 200)
# step 21 PKG-Read-vnfd, Resource Group A / User Group A
self._step_pkg_fetch('user_a', pkg_a, 200)
# step 22 PKG-Read-vnfd, Resource Group B / User Group A
self._step_pkg_fetch('user_a', pkg_b, 403)
# step 23 PKG-Read-vnfd, Resource Group B / User Group all
self._step_pkg_fetch('user_all', pkg_b, 200)
# step 24 PKG-Update, Resource Group B / User Group A
self._step_pkg_update('user_a', pkg_b, 403)
# step 25 PKG-Update, Resource Group A / User Group A
self._step_pkg_update('user_a', pkg_a, 200)
# step 26 PKG-Update, Resource Group B / User Group all
self._step_pkg_update('user_all', pkg_b, 200)
# step 27 PKG-Delete, Resource Group A / User Group A
self._step_pkg_delete('user_a', pkg_a, 204)
# step 29 PKG-Delete, Resource Group B / User Group all
self._step_pkg_delete('user_all', pkg_b, 204)
class VnfPackageAPIsTest(BaseVnfPackageAPIsTest):
def test_vnf_package_apis_enhanced_policy_vnf(self):
self._test_vnf_package_apis_enhanced_policy('test_enhanced_policy')
class CnfPackageAPIsTest(BaseVnfPackageAPIsTest):
def test_vnf_package_apis_enhanced_policy_cnf(self):
self._test_vnf_package_apis_enhanced_policy('test_cnf_scale')

View File

@ -0,0 +1,253 @@
#
# 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
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from tacker.tests.functional.sol.vnflcm import fake_vnflcm
from tacker.tests.functional.sol.vnflcm.test_vnf_instance import (
get_external_virtual_links)
from tacker.tests.functional.sol_enhanced_policy.base import (
VnflcmAPIsV1Base)
class VnflcmAPIsV1Test(VnflcmAPIsV1Base):
@classmethod
def setUpClass(cls):
super().setUpClass()
vim_type = 'openstack'
local_vim = 'local-vim.yaml'
cls.vim_a = cls._step_vim_register(
'user_a', vim_type, local_vim, 'vim_a', 'area_A@region_A')
cls.vim_a_1 = cls._step_vim_register(
'user_a', vim_type, local_vim, 'vim_a_1', 'area_A@region_A')
cls.vim_b = cls._step_vim_register(
'user_b', vim_type, local_vim, 'vim_b', 'area_B@region_B')
cls.vim_b_1 = cls._step_vim_register(
'user_b', vim_type, local_vim, 'vim_b_1', 'area_B@region_B')
cls.vim_c = cls._step_vim_register(
'user_b', vim_type, local_vim, 'vim_c', None)
cls.vim_c_1 = cls._step_vim_register(
'user_b', vim_type, local_vim, 'vim_c_1', None)
cls.pkg_a = cls._step_pkg_create('user_a')
cls.vnfd_id_a = cls._step_pkg_upload_content(
'user_a', cls.pkg_a, 'test_enhanced_policy', 'company_A')
cls.pkg_b = cls._step_pkg_create('user_b')
cls.vnfd_id_b = cls._step_pkg_upload_content(
'user_b', cls.pkg_b, 'test_enhanced_policy', 'company_B')
cls.pkg_c = cls._step_pkg_create('user_c')
cls.vnfd_id_c = cls._step_pkg_upload_content(
'user_c', cls.pkg_c, 'test_enhanced_policy', 'company_C')
@classmethod
def tearDownClass(cls):
cls._step_pkg_disable('user_a', cls.pkg_a,)
cls._step_pkg_disable('user_b', cls.pkg_b,)
cls._step_pkg_disable('user_c', cls.pkg_c,)
cls._step_pkg_delete('user_a', cls.pkg_a)
cls._step_pkg_delete('user_b', cls.pkg_b)
cls._step_pkg_delete('user_c', cls.pkg_c)
cls._step_vim_delete('user_a', cls.vim_a)
cls._step_vim_delete('user_a', cls.vim_a_1)
cls._step_vim_delete('user_b', cls.vim_b)
cls._step_vim_delete('user_b', cls.vim_b_1)
cls._step_vim_delete('user_admin', cls.vim_c)
cls._step_vim_delete('user_admin', cls.vim_c_1)
super().tearDownClass()
def _instantiate_vnf_request(self, flavour_id,
instantiation_level_id=None, vim_id=None, ext_vl=None,
ext_managed_vl=None):
request_body = {
"flavourId": flavour_id,
"additionalParams": {
"lcm-operation-user-data": "./UserData/lcm_user_data.py",
"lcm-operation-user-data-class": "SampleUserData"
}
}
if instantiation_level_id:
request_body["instantiationLevelId"] = instantiation_level_id
if ext_managed_vl:
request_body["extManagedVirtualLinks"] = ext_managed_vl
if ext_vl:
request_body["extVirtualLinks"] = ext_vl
if vim_id:
request_body["vimConnectionInfo"] = [
{"id": uuidutils.generate_uuid(),
"vimId": vim_id,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2"}]
return request_body
def _step_lcm_instantiate(
self, username, inst_id, vim_id, expected_status_code):
neutron_client = self.neutronclient()
net = neutron_client.list_networks()
networks = {}
for network in net['networks']:
networks[network['name']] = network['id']
net0_id = networks.get('net0')
if not net0_id:
self.fail("net0 network is not available")
net_mgmt_id = networks.get('net_mgmt')
if not net_mgmt_id:
self.fail("net_mgmt network is not available")
network_uuid = self.create_network(neutron_client,
"external_network")
port_uuid = self.create_port(neutron_client, network_uuid)
ext_vl = get_external_virtual_links(net0_id, net_mgmt_id,
port_uuid)
request_body = self._instantiate_vnf_request(
"simple", vim_id=vim_id, ext_vl=ext_vl)
self._lcm_instantiate(
username, inst_id, request_body, expected_status_code)
def _step_lcm_scale_out(self, username, inst_id, expected_status_code):
request_body = fake_vnflcm.VnfInstances.make_scale_request_body(
'SCALE_OUT')
self._step_lcm_scale(
username, inst_id, request_body, expected_status_code)
def _step_lcm_scale_in(self, username, inst_id, expected_status_code):
request_body = fake_vnflcm.VnfInstances.make_scale_request_body(
'SCALE_IN')
self._step_lcm_scale(
username, inst_id, request_body, expected_status_code)
def _change_ext_conn_vnf_request(self, vim_id=None, ext_vl=None,
vim_type="ETSINFV.OPENSTACK_KEYSTONE.v_2"):
request_body = {}
if ext_vl:
request_body["extVirtualLinks"] = ext_vl
if vim_id:
request_body["vimConnectionInfo"] = [
{"id": uuidutils.generate_uuid(),
"vimId": vim_id,
"vimType": vim_type}]
return request_body
def _step_lcm_change_connectivity(self, username, inst_id, new_vim_id,
expected_status_code):
client = self.get_tk_http_client_by_user(username)
neutron_client = self.neutronclient()
net = neutron_client.list_networks()
networks = {}
for network in net['networks']:
networks[network['name']] = network['id']
net0_id = networks.get('net0')
if not net0_id:
self.fail("net0 network is not available")
net_mgmt_id = networks.get('net_mgmt')
if not net_mgmt_id:
self.fail("net_mgmt network is not available")
network_uuid = self.create_network(neutron_client,
"external_network")
port_uuid = self.create_port(neutron_client, network_uuid)
ext_vl = get_external_virtual_links(net0_id, net_mgmt_id,
port_uuid)
change_ext_conn_req_body = self._change_ext_conn_vnf_request(
vim_id=new_vim_id, ext_vl=ext_vl)
url = os.path.join(
self.base_vnf_instances_url,
inst_id,
"change_ext_conn")
resp, body = client.do_request(url, "POST",
body=jsonutils.dumps(change_ext_conn_req_body))
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
self._wait_lcm_done(
operation='CHANGE_EXT_CONN',
expected_operation_status='COMPLETED',
vnf_instance_id=inst_id)
def test_vnflcm_apis_vnf_instance_with_area_vnf(self):
self.register_subscription()
inst_id_a, inst_id_b = self.steps_lcm_create_and_get_with_area()
# step 12 LCM-Instantiate, Resource Group A / User Group A
self._step_lcm_instantiate('user_a', inst_id_a, self.vim_a['id'], 202)
# step 13 LCM-Instantiate, Resource Group B / User Group A
self._step_lcm_instantiate('user_a', inst_id_b, self.vim_b['id'], 403)
# step 14 LCM-Instantiate, Resource Group B / User Group all
self._step_lcm_instantiate(
'user_all', inst_id_b, self.vim_b['id'], 202)
self.steps_lcm_get_scale_heal_modify_with_area(inst_id_a, inst_id_b)
# step 34 LCM-Change-Connectivity, Resource Group A / User Group A
self._step_lcm_change_connectivity(
'user_a', inst_id_a, self.vim_a_1['id'], 202)
# step 35 LCM-Change-Connectivity, Resource Group B / User Group A
self._step_lcm_change_connectivity(
'user_a', inst_id_b, self.vim_b_1['id'], 403)
# step 36 LCM-Change-Connectivity, Resource Group B / User Group all
self._step_lcm_change_connectivity(
'user_all', inst_id_b, self.vim_b_1['id'], 202)
self.steps_lcm_terminate_delete_with_area(inst_id_a, inst_id_b)
def test_vnflcm_apis_vnf_instance_without_area_vnf(self):
self.register_subscription()
inst_id_c = self.steps_lcm_create_and_get_without_area()
# step 8 LCM-Instantiate, Resource Group C / User Group C
self._step_lcm_instantiate('user_c', inst_id_c, self.vim_c['id'], 202)
self.steps_lcm_get_scale_heal_modify_without_area(inst_id_c)
# step 27 LCM-Change-Connectivity, Resource Group C / User Group C
self._step_lcm_change_connectivity(
'user_c', inst_id_c, self.vim_c_1['id'], 403)
# step 28 LCM-Change-Connectivity, Resource Group C / User Group all
self._step_lcm_change_connectivity(
'user_all', inst_id_c, self.vim_c_1['id'], 403)
# step 29 LCM-Change-Connectivity, Resource Group C / User Group admin
self._step_lcm_change_connectivity(
'user_admin', inst_id_c, self.vim_c_1['id'], 202)
self.steps_lcm_terminate_delete_without_area(inst_id_c)

View File

@ -0,0 +1,974 @@
#
# 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 time
import yaml
from oslo_utils import uuidutils
from tacker.sol_refactored.common import http_client
from tacker.sol_refactored import objects
from tacker.tests.functional.sol_enhanced_policy.base import (
BaseEnhancedPolicyTest)
from tacker.tests.functional.sol_v2_common import base_v2
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests.functional.sol_v2_common.test_vnflcm_basic_common import (
CommonVnfLcmTest)
from tacker.tests import utils as base_utils
class VnflcmAPIsV2VNFBase(CommonVnfLcmTest, BaseEnhancedPolicyTest):
user_role_map = {
'user_a': ['VENDOR_company_A', 'AREA_area_A@region_A',
'TENANT_namespace_A', 'manager'],
'user_a_1': ['VENDOR_company_A', 'manager'],
'user_b': ['VENDOR_company_B', 'AREA_area_B@region_B',
'TENANT_namespace_B', 'manager'],
'user_c': ['VENDOR_company_C', 'AREA_area_C@region_C',
'TENANT_namespace-c', 'manager'],
'user_all': ['VENDOR_all', 'AREA_all@all', 'TENANT_all', 'manager'],
'user_admin': ['admin']
}
@classmethod
def setUpClass(cls):
CommonVnfLcmTest.setUpClass()
BaseEnhancedPolicyTest.setUpClass(cls)
for user in cls.users:
client = cls.get_local_tacker_http_client(user.name)
setattr(cls,
cls.TK_HTTP_CLIENT_NAME % {'username': user.name}, client)
cls.tacker_client = cls.get_local_tacker_http_client('user_all')
cur_dir = os.path.dirname(__file__)
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))
# for basic lcms tests min pattern
basic_lcms_min_path = os.path.join(cur_dir,
"../../sol_v2_common/samples/basic_lcms_min")
# for update vnf test
update_vnf_path = os.path.join(cur_dir,
"../../sol_v2_common/samples/update_vnf")
# for change ext conn
change_vnfpkg_from_image_to_image_path_2 = os.path.join(cur_dir,
"../../sol_v2_common/samples/test_change_vnf_pkg_with_new_image")
# for user_a
cls.vnf_pkg_a, cls.vnfd_id_a = cls.create_vnf_package(
basic_lcms_min_path, image_path=image_path, provider='company_A')
cls.vnf_pkg_a_1, cls.vnfd_id_a_1 = cls.create_vnf_package(
update_vnf_path, provider='company_A')
cls.vnf_pkg_a_2, cls.vnfd_id_a_2 = cls.create_vnf_package(
change_vnfpkg_from_image_to_image_path_2, image_path=image_path,
provider='company_A')
# for user_b
cls.vnf_pkg_b, cls.vnfd_id_b = cls.create_vnf_package(
basic_lcms_min_path, image_path=image_path, provider='company_B')
cls.vnf_pkg_b_1, cls.vnfd_id_b_1 = cls.create_vnf_package(
update_vnf_path, provider='company_B')
cls.vnf_pkg_b_2, cls.vnfd_id_b_2 = cls.create_vnf_package(
change_vnfpkg_from_image_to_image_path_2, image_path=image_path,
provider='company_B')
# for user_c
cls.vnf_pkg_c, cls.vnfd_id_c = cls.create_vnf_package(
basic_lcms_min_path, image_path=image_path, provider='company_C')
cls.vnf_pkg_c_1, cls.vnfd_id_c_1 = cls.create_vnf_package(
update_vnf_path, provider='company_C')
cls.vnf_pkg_c_2, cls.vnfd_id_c_2 = cls.create_vnf_package(
change_vnfpkg_from_image_to_image_path_2, image_path=image_path,
provider='company_C')
@classmethod
def tearDownClass(cls):
cls.delete_vnf_package(cls.vnf_pkg_a)
cls.delete_vnf_package(cls.vnf_pkg_a_1)
cls.delete_vnf_package(cls.vnf_pkg_a_2)
cls.delete_vnf_package(cls.vnf_pkg_b)
cls.delete_vnf_package(cls.vnf_pkg_b_1)
cls.delete_vnf_package(cls.vnf_pkg_b_2)
cls.delete_vnf_package(cls.vnf_pkg_c)
cls.delete_vnf_package(cls.vnf_pkg_c_1)
cls.delete_vnf_package(cls.vnf_pkg_c_2)
BaseEnhancedPolicyTest.tearDownClass()
super(VnflcmAPIsV2VNFBase, cls).tearDownClass()
@classmethod
def get_vim_info(cls, vim_conf='local-vim.yaml'):
vim_params = yaml.safe_load(base_utils.read_file(vim_conf))
vim_params['auth_url'] += '/v3'
vim_info = objects.VimConnectionInfo(
interfaceInfo={'endpoint': vim_params['auth_url']},
accessInfo={
'region': 'RegionOne',
'project': vim_params['project_name'],
'username': vim_params['username'],
'password': vim_params['password'],
'userDomain': vim_params['user_domain_name'],
'projectDomain': vim_params['project_domain_name']
}
)
return vim_info
@classmethod
def get_local_tacker_http_client(cls, username):
vim_info = cls.get_vim_info(vim_conf=cls.local_vim_conf_file)
auth = http_client.KeystonePasswordAuthHandle(
auth_url=vim_info.interfaceInfo['endpoint'],
username=username,
password='devstack',
project_name=vim_info.accessInfo['project'],
user_domain_name=vim_info.accessInfo['userDomain'],
project_domain_name=vim_info.accessInfo['projectDomain']
)
return http_client.HttpClient(auth)
def change_ext_conn_max(self, net_ids, subnets, auth_url, area):
vim_id_1 = uuidutils.generate_uuid()
vim_id_2 = uuidutils.generate_uuid()
ext_vl_1 = {
"id": uuidutils.generate_uuid(),
"vimConnectionId": vim_id_1,
"resourceProviderId": uuidutils.generate_uuid(),
"resourceId": net_ids['ft-net1'],
"extCps": [
{
"cpdId": "VDU1_CP1",
"cpConfig": {
"VDU1_CP1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
# "macAddress": omitted,
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
# "fixedAddresses": omitted,
"numDynamicAddresses": 1,
# "addressRange": omitted,
"subnetId": subnets[
'ft-ipv4-subnet1']}]
}
}]}
}
},
{
"cpdId": "VDU2_CP2",
"cpConfig": {
"VDU2_CP2": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
# "macAddress": omitted,
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": [
"22.22.22.101"
],
# "numDynamicAddresses": omitted
# "addressRange": omitted,
"subnetId": subnets['ft-ipv4-subnet1']
}, {
"type": "IPV6",
# "fixedAddresses": omitted,
# "numDynamicAddresses": omitted,
"numDynamicAddresses": 1,
# "addressRange": omitted,
"subnetId": subnets['ft-ipv6-subnet1']
}]
}
}]
}}
}
]
}
vim_1 = {
"vimId": vim_id_1,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"project": "nfv",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {"area": area}
}
vim_2 = {
"vimId": vim_id_2,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"password": "dummy_password",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {"area": area}
}
if not area:
vim_1.pop('extra')
vim_2.pop('extra')
return {
"extVirtualLinks": [
ext_vl_1
],
"vimConnectionInfo": {
"vim1": vim_1,
"vim2": vim_2
},
"additionalParams": {"dummy-key": "dummy-val"}
}
def _step_lcm_create(self, username, vnfd_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
create_req = paramgen.create_vnf_min(vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 201:
return body['id']
else:
return None
def instantiate_vnf(self, area=None, vim_id=None):
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
vim_id_1 = uuidutils.generate_uuid()
vim_id_2 = uuidutils.generate_uuid()
if area:
vim_1 = {
"vimId": vim_id_1,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {"endpoint": self.auth_url},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"project": "nfv",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {"area": area}
}
vim_2 = {
"vimId": vim_id_2,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {"endpoint": self.auth_url},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"password": "dummy_password",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {"area": area}
}
if vim_id:
vim_1 = {
"vimId": vim_id,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3"
}
vim_2 = {
"vimId": vim_id,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3"
}
return {
"flavourId": "simple",
"vimConnectionInfo": {
"vim1": vim_1,
"vim2": vim_2
}
}
def _step_lcm_instantiate(self, username, inst_id, glance_image,
flavour_vdu_dict, zone_name_list, expected_status_code,
area=None, vim_id=None):
self.tacker_client = self.get_tk_http_client_by_user(username)
self._set_grant_response(
False, 'INSTANTIATE', glance_image=glance_image,
flavour_vdu_dict=flavour_vdu_dict, zone_name_list=zone_name_list)
instantiate_req = self.instantiate_vnf(area, vim_id)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
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 instantiate completion.
time.sleep(3)
def _step_lcm_show(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(expected_status_code, resp.status_code)
def _step_lcm_list(self, username, expected_inst_list):
self.tacker_client = self.get_tk_http_client_by_user(username)
resp, vnf_instances = self.list_vnf_instance()
self.assertEqual(200, resp.status_code)
inst_ids = set([inst.get('id') for inst in vnf_instances])
for inst_id in expected_inst_list:
self.assertIn(inst_id, inst_ids)
def _step_lcm_heal(self, username, inst_id, glance_image, flavour_vdu_dict,
zone_name_list, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
self._set_grant_response(
False, 'HEAL', glance_image=glance_image,
flavour_vdu_dict=flavour_vdu_dict, zone_name_list=zone_name_list)
heal_req = paramgen.heal_vnf_all_min()
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
time.sleep(3)
def _step_lcm_update(self, username, inst_id, update_vnfd_id,
expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
update_req = paramgen.update_vnf_min_with_parameter(update_vnfd_id)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
def _step_lcm_scale_out(self, username, inst_id, glance_image,
flavour_vdu_dict, zone_name_list,
expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
self._set_grant_response(
False, 'SCALE', glance_image=glance_image,
flavour_vdu_dict=flavour_vdu_dict, zone_name_list=zone_name_list)
scaleout_req = paramgen.scaleout_vnf_min()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
def _step_lcm_scale_in(self, username, inst_id, glance_image,
flavour_vdu_dict, zone_name_list, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
self._set_grant_response(
False, 'SCALE', glance_image=glance_image,
flavour_vdu_dict=flavour_vdu_dict, zone_name_list=zone_name_list)
scalein_req = paramgen.scalein_vnf_min()
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
def _step_lcm_change_vnfpkg(self, username, inst_id, change_vnfd_id,
glance_image, flavour_vdu_dict, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
change_vnfpkg_req = paramgen.change_vnfpkg_with_ext_vl(
change_vnfd_id, self.get_network_ids(['net1']))
del change_vnfpkg_req[
"additionalParams"]["lcm-operation-coordinate-old-vnf"]
del change_vnfpkg_req[
"additionalParams"]["lcm-operation-coordinate-new-vnf"]
self._set_grant_response(False, 'CHANGE_VNFPKG',
glance_image=glance_image,
flavour_vdu_dict=flavour_vdu_dict)
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
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 change_vnfpkg completion.
time.sleep(3)
def _step_lcm_change_ext_conn(self, username, inst_id, area,
zone_name_list, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
self._set_grant_response(
False, 'CHANGE_EXT_CONN', zone_name_list=zone_name_list)
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
# Create a new network for change external connectivity
ft_net1_name = 'ft-net1'
ft_net1_subs = {
'ft-ipv4-subnet1': {
'range': '22.22.22.0/24',
'ip_version': 4
},
'ft-ipv6-subnet1': {
'range': '1111:2222:4444::/64',
'ip_version': 6
}
}
ft_net1_id = self.create_network(ft_net1_name)
self.addCleanup(self.delete_network, ft_net1_id)
for sub_name, val in ft_net1_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net1_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0', 'ft-net1'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0',
'ft-ipv4-subnet1', 'ft-ipv6-subnet1'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
change_ext_conn_req = self.change_ext_conn_max(
net_ids, subnet_ids, self.auth_url, area)
resp, body = self.change_ext_conn(inst_id, change_ext_conn_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
time.sleep(3)
def _step_lcm_terminate(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
self._set_grant_response(False, 'TERMINATE')
terminate_req = paramgen.terminate_vnf_min()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
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(3)
def _step_lcm_delete(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(expected_status_code, resp.status_code)
def vnflcm_apis_v2_vnf_test_before_instantiate(self):
# Create subscription
self.tacker_client = self.get_tk_http_client_by_user('user_all')
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_min(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# Test notification
self.assert_notification_get(callback_url)
# check usageState of VNF Package
self._check_package_usage(False, self.vnf_pkg_a)
# step 1 LCM-CreateV2, Resource Group A / User Group A
inst_id_a = self._step_lcm_create('user_a', self.vnfd_id_a, 201)
# step 2 LCM-CreateV2, Resource Group B / User Group A
self._step_lcm_create('user_a', self.vnfd_id_b, 403)
# step 3 LCM-CreateV2, Resource Group B / User Group all
inst_id_b = self._step_lcm_create('user_all', self.vnfd_id_b, 201)
# step 4 LCM-ShowV2, Resource Group A / User Group A
self._step_lcm_show('user_a', inst_id_a, 200)
# step 5 LCM-ShowV2, Resource Group A / User Group A-1
self._step_lcm_show('user_a_1', inst_id_a, 200)
# step 6 LCM-ShowV2, Resource Group B / User Group A
self._step_lcm_show('user_a', inst_id_b, 403)
# step 7 LCM-ShowV2, Resource Group B / User Group all
self._step_lcm_show('user_all', inst_id_b, 200)
# step 8 LCM-ListV2, Resource Group A / User Group A
self._step_lcm_list('user_a', [inst_id_a])
# step 9 LCM-ListV2, Resource Group - / User Group A-1
self._step_lcm_list('user_a_1', [inst_id_a])
# step 10 LCM-ListV2, Resource Group - / User Group B
self._step_lcm_list('user_b', [inst_id_b])
# step 11 LCM-ListV2, Resource Group - / User Group all
self._step_lcm_list('user_all', [inst_id_a, inst_id_b])
return sub_id, inst_id_a, inst_id_b
def vnflcm_apis_v2_vnf_test_after_instantiate(
self, sub_id, inst_id_a, inst_id_b, zone_name_list, glance_image,
flavour_vdu_dict):
# step 15 LCM-ShowV2, Resource Group A / User Group A
self._step_lcm_show('user_a', inst_id_a, 200)
# step 16 LCM-ShowV2, Resource Group A / User Group A-1
self._step_lcm_show('user_a_1', inst_id_a, 403)
# step 17 LCM-ShowV2, Resource Group B / User Group A
self._step_lcm_show('user_a', inst_id_b, 403)
# step 18 LCM-ShowV2, Resource Group B / User Group all
self._step_lcm_show('user_all', inst_id_b, 200)
# step 19 LCM-ListV2, Resource Group - / User Group A
self._step_lcm_list('user_a', [inst_id_a])
# step 20 LCM-ListV2, Resource Group - / User Group A-1
self._step_lcm_list('user_a_1', [])
# step 21 LCM-ListV2, Resource Group - / User Group B
self._step_lcm_list('user_b', [inst_id_b])
# step 22 LCM-ListV2, Resource Group - / User Group all
self._step_lcm_list('user_all', [inst_id_a, inst_id_b])
# step 23 LCM-ScaleV2(out), Resource Group A / User Group A
self._step_lcm_scale_out('user_a', inst_id_a, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 24 LCM-ScaleV2(out), Resource Group B / User Group A
self._step_lcm_scale_out('user_a', inst_id_b, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 25 LCM-ScaleV2(out), Resource Group B / User Group all
self._step_lcm_scale_out('user_all', inst_id_b, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 26 LCM-ScaleV2(in), Resource Group A / User Group A
self._step_lcm_scale_in('user_a', inst_id_a, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 27 LCM-ScaleV2(in), Resource Group B / User Group A
self._step_lcm_scale_in('user_a', inst_id_b, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 28 LCM-ScaleV2(in), Resource Group B / User Group all
self._step_lcm_scale_in('user_all', inst_id_b, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 29 LCM-HealV2, Resource Group A / User Group A
self._step_lcm_heal('user_a', inst_id_a, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 30 LCM-HealV2, Resource Group B / User Group A
self._step_lcm_heal('user_a', inst_id_b, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 31 LCM-HealV2, Resource Group B / User Group all
self._step_lcm_heal('user_all', inst_id_b, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 32 LCM-ModifyV2, Resource Group A / User Group A
self._step_lcm_update('user_a', inst_id_a, self.vnfd_id_a_1, 202)
# step 33 LCM-ModifyV2, Resource Group b / User Group A
self._step_lcm_update('user_a', inst_id_b, self.vnfd_id_b_1, 403)
# step 34 LCM-ModifyV2, Resource Group B / User Group all
self._step_lcm_update('user_all', inst_id_b, self.vnfd_id_b_1, 202)
# step 35 LCM-Change-ConnectivityV2, Resource Group A / User Group A
self._step_lcm_change_ext_conn(
'user_a', inst_id_a, 'area_A@region_A', zone_name_list, 202)
# step 36 LCM-Change-ConnectivityV2, Resource Group B / User Group A
self._step_lcm_change_ext_conn(
'user_a', inst_id_b, 'area_B@region_B', zone_name_list, 403)
# step 37 LCM-Change-ConnectivityV2, Resource Group B / User Group all
self._step_lcm_change_ext_conn(
'user_all', inst_id_b, 'area_B@region_B', zone_name_list, 202)
# step 38 LCM-Change-VnfPkgV2, Resource Group A / User Group A
self._step_lcm_update('user_a', inst_id_a, self.vnfd_id_a, 202)
self._step_lcm_change_vnfpkg('user_a', inst_id_a, self.vnfd_id_a_2,
glance_image, flavour_vdu_dict, 202)
# step 39 LCM-Change-VnfPkgV2, Resource Group B / User Group A
self._step_lcm_change_vnfpkg('user_a', inst_id_b, self.vnfd_id_b_2,
glance_image, flavour_vdu_dict, 403)
# step 40 LCM-Change-VnfPkgV2, Resource Group B / User Group all
self._step_lcm_update('user_all', inst_id_b, self.vnfd_id_b, 202)
self._step_lcm_change_vnfpkg('user_all', inst_id_b, self.vnfd_id_b_2,
glance_image, flavour_vdu_dict, 202)
# step 41 LCM-TerminateV2, Resource Group A / User Group A
self._step_lcm_terminate('user_a', inst_id_a, 202)
# step 42 LCM-TerminateV2, Resource Group B / User Group A
self._step_lcm_terminate('user_a', inst_id_b, 403)
# step 43 LCM-TerminateV2, Resource Group B / User Group all
self._step_lcm_terminate('user_all', inst_id_b, 202)
# step 44 LCM-DeleteV2, Resource Group A / User Group A
self._step_lcm_delete('user_a', inst_id_a, 204)
# step 45 LCM-DeleteV2, Resource Group B / User Group A
self._step_lcm_delete('user_a', inst_id_b, 403)
# step 46 LCM-DeleteV2, Resource Group B / User Group all
self._step_lcm_delete('user_all', inst_id_b, 204)
# Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# Show subscription
resp, body = self.show_subscription(sub_id)
self.assertEqual(404, resp.status_code)
class VnflcmAPIsV2VNFInstantiateWithArea(VnflcmAPIsV2VNFBase):
def test_vnflcm_apis_v2_vnf_with_area_in_vim_conn_info(self):
glance_image = None
flavour_vdu_dict = None
zone_name_list = None
sub_id, inst_id_a, inst_id_b = (
self.vnflcm_apis_v2_vnf_test_before_instantiate())
# step 12 LCM-InstantiateV2, Resource Group A / User Group A
self._step_lcm_instantiate('user_a', inst_id_a, glance_image,
flavour_vdu_dict, zone_name_list, 202, area='area_A@region_A')
# step 13 LCM-InstantiateV2, Resource Group B / User Group A
self._step_lcm_instantiate('user_a', inst_id_b, glance_image,
flavour_vdu_dict, zone_name_list, 403,
area='area_B@region_B')
# step 14 LCM-InstantiateV2, Resource Group B / User Group all
self._step_lcm_instantiate('user_all', inst_id_b, glance_image,
flavour_vdu_dict, zone_name_list, 202,
area='area_B@region_B')
self.vnflcm_apis_v2_vnf_test_after_instantiate(
sub_id, inst_id_a, inst_id_b, zone_name_list, glance_image,
flavour_vdu_dict)
class VnflcmAPIsV2VNFInstantiateWithoutArea(VnflcmAPIsV2VNFBase):
@classmethod
def setUpClass(cls):
super().setUpClass()
vim_type = 'openstack'
local_vim = 'local-vim.yaml'
cls.vim_a = cls._step_vim_register(
'user_a', vim_type, local_vim, 'vim_a', 'area_A@region_A')
cls.vim_a_1 = cls._step_vim_register(
'user_a', vim_type, local_vim, 'vim_a_1', 'area_A@region_A')
cls.vim_b = cls._step_vim_register(
'user_b', vim_type, local_vim, 'vim_b', 'area_B@region_B')
cls.vim_b_1 = cls._step_vim_register(
'user_b', vim_type, local_vim, 'vim_b_1', 'area_B@region_B')
@classmethod
def tearDownClass(cls):
cls._step_vim_delete('user_a', cls.vim_a)
cls._step_vim_delete('user_a', cls.vim_a_1)
cls._step_vim_delete('user_b', cls.vim_b)
cls._step_vim_delete('user_b', cls.vim_b_1)
super().tearDownClass()
def test_vnflcm_apis_v2_vnf_without_area_in_vim_conn_info(self):
glance_image = None
flavour_vdu_dict = None
zone_name_list = None
sub_id, inst_id_a, inst_id_b = (
self.vnflcm_apis_v2_vnf_test_before_instantiate())
# step 12 LCM-InstantiateV2, Resource Group A / User Group A
self._step_lcm_instantiate('user_a', inst_id_a,
glance_image,
flavour_vdu_dict, zone_name_list, 202,
vim_id=self.vim_a['id'])
# step 13 LCM-InstantiateV2, Resource Group B / User Group A
self._step_lcm_instantiate('user_a', inst_id_b,
glance_image,
flavour_vdu_dict, zone_name_list, 403,
vim_id=self.vim_b['id'])
# step 14 LCM-InstantiateV2, Resource Group B / User Group all
self._step_lcm_instantiate('user_all', inst_id_b,
glance_image,
flavour_vdu_dict, zone_name_list, 202,
vim_id=self.vim_b['id'])
self.vnflcm_apis_v2_vnf_test_after_instantiate(
sub_id, inst_id_a, inst_id_b, zone_name_list, glance_image,
flavour_vdu_dict)
class VnflcmAPIsV2VNFInstanceWithoutArea(VnflcmAPIsV2VNFBase):
@classmethod
def setUpClass(cls):
super().setUpClass()
vim_type = 'openstack'
local_vim = 'local-vim.yaml'
cls.vim_c = cls._step_vim_register(
'user_c', vim_type, local_vim, 'vim_c', None)
cls.vim_c_1 = cls._step_vim_register(
'user_c', vim_type, local_vim, 'vim_c_1', None)
@classmethod
def tearDownClass(cls):
cls._step_vim_delete('user_admin', cls.vim_c)
cls._step_vim_delete('user_admin', cls.vim_c_1)
super().tearDownClass()
def test_vnflcm_apis_v2_vnf_instance_without_area(self):
glance_image = None
flavour_vdu_dict = None
zone_name_list = None
# Create subscription
self.tacker_client = self.get_tk_http_client_by_user('user_all')
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_min(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# Test notification
self.assert_notification_get(callback_url)
# check usageState of VNF Package
self._check_package_usage(False, self.vnf_pkg_c)
# step 1 LCM-CreateV2, Resource Group C / User Group C
inst_id_c = self._step_lcm_create('user_c', self.vnfd_id_c, 201)
# step 2 LCM-ShowV2, Resource Group C / User Group C
self._step_lcm_show('user_c', inst_id_c, 200)
# step 3 LCM-ShowV2, Resource Group C / User Group all
self._step_lcm_show('user_all', inst_id_c, 200)
# step 4 LCM-ShowV2, Resource Group C / User Group admin
self._step_lcm_show('user_admin', inst_id_c, 200)
# step 5 LCM-ListV2, Resource Group - / User Group C
self._step_lcm_list('user_c', [inst_id_c])
# step 6 LCM-ListV2, Resource Group - / User Group all
self._step_lcm_list('user_all', [inst_id_c])
# step 7 LCM-ListV2, Resource Group - / User Group admin
self._step_lcm_list('user_admin', [inst_id_c])
# step 8 LCM-InstantiateV2, Resource Group C / User Group C
self._step_lcm_instantiate('user_c', inst_id_c,
glance_image,
flavour_vdu_dict, zone_name_list, 202,
vim_id=self.vim_c['id'])
# step 9 LCM-ShowV2, Resource Group C / User Group C
self._step_lcm_show('user_c', inst_id_c, 403)
# step 10 LCM-ShowV2, Resource Group C / User Group all
self._step_lcm_show('user_all', inst_id_c, 403)
# step 11 LCM-ShowV2, Resource Group C / User Group admin
self._step_lcm_show('user_admin', inst_id_c, 200)
# step 12 LCM-ListV2, Resource Group - / User Group C
self._step_lcm_list('user_c', [])
# step 13 LCM-ListV2, Resource Group - / User Group all
self._step_lcm_list('user_all', [])
# step 14 LCM-ListV2, Resource Group - / User Group admin
self._step_lcm_list('user_admin', [inst_id_c])
# step 15 LCM-ScaleV2(out), Resource Group C / User Group C
self._step_lcm_scale_out('user_c', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 16 LCM-ScaleV2(out), Resource Group C / User Group all
self._step_lcm_scale_out('user_all', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 17 LCM-ScaleV2(out), Resource Group C / User Group admin
self._step_lcm_scale_out('user_admin', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 18 LCM-ScaleV2(in), Resource Group C / User Group C
self._step_lcm_scale_in('user_c', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 19 LCM-ScaleV2(in), Resource Group C / User Group A
self._step_lcm_scale_in('user_all', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 20 LCM-ScaleV2(in), Resource Group C / User Group all
self._step_lcm_scale_in('user_admin', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 21 LCM-HealV2, Resource Group C / User Group C
self._step_lcm_heal('user_c', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 22 LCM-HealV2, Resource Group C / User Group A
self._step_lcm_heal('user_all', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 403)
# step 23 LCM-HealV2, Resource Group C / User Group all
self._step_lcm_heal('user_admin', inst_id_c, glance_image,
flavour_vdu_dict, zone_name_list, 202)
# step 24 LCM-ModifyV2, Resource Group C / User Group C
self._step_lcm_update('user_c', inst_id_c, self.vnfd_id_c_1, 403)
# step 25 LCM-ModifyV2, Resource Group C / User Group A
self._step_lcm_update('user_all', inst_id_c, self.vnfd_id_c_1, 403)
# step 26 LCM-ModifyV2, Resource Group C / User Group all
self._step_lcm_update('user_admin', inst_id_c, self.vnfd_id_c_1, 202)
# step 27 LCM-Change-ConnectivityV2, Resource Group C / User Group C
self._step_lcm_change_ext_conn(
'user_c', inst_id_c, None, zone_name_list, 403)
# step 28 LCM-Change-ConnectivityV2, Resource Group C / User Group A
self._step_lcm_change_ext_conn(
'user_all', inst_id_c, None, zone_name_list, 403)
# step 29 LCM-Change-ConnectivityV2, Resource Group C / User Group all
self._step_lcm_change_ext_conn(
'user_admin', inst_id_c, None, zone_name_list, 202)
# step 30 LCM-Change-VnfPkgV2, Resource Group C / User Group C
self._step_lcm_change_vnfpkg('user_c', inst_id_c, self.vnfd_id_c_2,
glance_image, flavour_vdu_dict, 403)
# step 31 LCM-Change-VnfPkgV2, Resource Group C / User Group A
self._step_lcm_change_vnfpkg('user_all', inst_id_c, self.vnfd_id_c_2,
glance_image, flavour_vdu_dict, 403)
# step 32 LCM-Change-VnfPkgV2, Resource Group C / User Group all
self._step_lcm_update('user_admin', inst_id_c, self.vnfd_id_c, 202)
self._step_lcm_change_vnfpkg('user_admin', inst_id_c, self.vnfd_id_c_2,
glance_image, flavour_vdu_dict, 202)
# step 33 LCM-TerminateV2, Resource Group C / User Group C
self._step_lcm_terminate('user_c', inst_id_c, 403)
# step 34 LCM-TerminateV2, Resource Group C / User Group A
self._step_lcm_terminate('user_all', inst_id_c, 403)
# step 35 LCM-TerminateV2, Resource Group C / User Group all
self._step_lcm_terminate('user_admin', inst_id_c, 202)
# step 36 LCM-DeleteV2, Resource Group C / User Group C
self._step_lcm_delete('user_c', inst_id_c, 204)
# Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# Show subscription
resp, body = self.show_subscription(sub_id)
self.assertEqual(404, resp.status_code)

View File

@ -0,0 +1,220 @@
#
# 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 oslo_utils import uuidutils
from tacker import context
from tacker.tests.functional.sol_enhanced_policy.base import (
VnflcmAPIsV1Base)
class VnflcmAPIsV1CNFTest(VnflcmAPIsV1Base):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.client = cls.tackerclient(cls.local_vim_conf_file)
# In the setUp of the parent class, it will be confirmed whether there
# is a default vim named "VIM0". Here, it is confirmed in advance. If
# it does not exist, it will be created to avoid the parent class
# throwing an exception.
cls.if_not_vim_create()
local_k8s_vim = 'local-k8s-vim.yaml'
vim_type = 'kubernetes'
cls.vim_a = cls._step_vim_register(
'user_a', vim_type, local_k8s_vim, 'vim_a', 'area_A@region_A')
cls.vim_a_1 = cls._step_vim_register(
'user_a', vim_type, local_k8s_vim, 'vim_a_1', 'area_A@region_A')
cls.vim_b = cls._step_vim_register(
'user_b', vim_type, local_k8s_vim, 'vim_b', 'area_B@region_B')
cls.vim_b_1 = cls._step_vim_register(
'user_b', vim_type, local_k8s_vim, 'vim_b_1', 'area_B@region_B')
cls.vim_c = cls._step_vim_register(
'user_c', vim_type, local_k8s_vim, 'vim_c', None)
cls.vim_c_1 = cls._step_vim_register(
'user_c', vim_type, local_k8s_vim, 'vim_c_1', None)
cls.pkg_a = cls._step_pkg_create('user_a')
cls.vnfd_id_a = cls._step_pkg_upload_content(
'user_a', cls.pkg_a, 'test_cnf', 'company_A',
namespace='namespace-a')
cls.pkg_b = cls._step_pkg_create('user_b')
cls.vnfd_id_b = cls._step_pkg_upload_content(
'user_b', cls.pkg_b, 'test_cnf', 'company_B',
namespace='namespace-b')
cls.pkg_c = cls._step_pkg_create('user_c')
cls.vnfd_id_c = cls._step_pkg_upload_content(
'user_c', cls.pkg_c, 'test_cnf', 'company_C',
namespace='namespace-c')
@classmethod
def tearDownClass(cls):
cls._step_pkg_disable('user_a', cls.pkg_a,)
cls._step_pkg_disable('user_b', cls.pkg_b,)
cls._step_pkg_disable('user_c', cls.pkg_c,)
cls._step_pkg_delete('user_a', cls.pkg_a)
cls._step_pkg_delete('user_b', cls.pkg_b)
cls._step_pkg_delete('user_c', cls.pkg_c)
cls._step_vim_delete('user_a', cls.vim_a)
cls._step_vim_delete('user_a', cls.vim_a_1)
cls._step_vim_delete('user_b', cls.vim_b)
cls._step_vim_delete('user_b', cls.vim_b_1)
cls._step_vim_delete('user_admin', cls.vim_c)
cls._step_vim_delete('user_admin', cls.vim_c_1)
super().tearDownClass()
@classmethod
def get_vim(cls, vim_list, vim_name):
if len(vim_list.values()) == 0:
assert False, "vim_list is Empty: Default VIM is missing"
for vim_list in vim_list.values():
for vim in vim_list:
if vim['name'] == vim_name:
return vim
return None
@classmethod
def if_not_vim_create(cls):
cls.context = context.get_admin_context()
vim_list = cls.client.list_vims()
vim_name = 'VIM0'
if not vim_list:
cls.vim_k8s = cls._step_vim_register(
'user_all', 'kubernetes', 'local-k8s-vim.yaml',
vim_name, None)
vim = cls.get_vim(vim_list, vim_name)
if not vim:
vim_k8s = cls._step_vim_register(
'user_all', 'kubernetes', 'local-k8s-vim.yaml',
vim_name, None)
cls.vim_k8s_id = vim_k8s.get('id')
else:
cls.vim_k8s_id = vim.get('id')
@classmethod
def _instantiate_vnf_instance_request(
cls, flavour_id, vim_id=None, additional_param=None,
extra_param=None):
request_body = {"flavourId": flavour_id}
if vim_id:
request_body["vimConnectionInfo"] = [
{"id": uuidutils.generate_uuid(),
"vimId": vim_id,
"vimType": "kubernetes"}]
if extra_param:
request_body["vimConnectionInfo"][0]["extra"] = extra_param
if additional_param:
request_body["additionalParams"] = additional_param
return request_body
def _step_lcm_instantiate(self, username, inst_id, vim_id, namespace,
expected_status_code):
additional_param = {
"lcm-kubernetes-def-files": [
"Files/kubernetes/deployment.yaml",
"Files/kubernetes/namespace.yaml"
],
"namespace": namespace
}
request_body = self._instantiate_vnf_instance_request(
"simple", vim_id, additional_param=additional_param)
self._lcm_instantiate(
username, inst_id, request_body, expected_status_code)
def _step_lcm_scale_out(self, username, inst_id, expected_status_code):
scale_out_req = {
"type": "SCALE_OUT",
"aspectId": "vdu2_aspect",
"numberOfSteps": 1
}
self._step_lcm_scale(
username, inst_id, scale_out_req, expected_status_code)
def _step_lcm_scale_in(self, username, inst_id, expected_status_code):
scale_in_req = {
"type": "SCALE_IN",
"aspectId": "vdu2_aspect",
"numberOfSteps": 1
}
self._step_lcm_scale(
username, inst_id, scale_in_req, expected_status_code)
def test_vnflcm_apis_vnf_instance_with_area_cnf(self):
self.register_subscription()
inst_id_a, inst_id_b = self.steps_lcm_create_and_get_with_area()
# step 12 LCM-Instantiate, Resource Group A / User Group A
self._step_lcm_instantiate(
'user_a', inst_id_a, self.vim_a['id'], 'namespace-a', 202)
# step 13 LCM-Instantiate, Resource Group B / User Group A
self._step_lcm_instantiate(
'user_a', inst_id_b, self.vim_b['id'], 'namespace-b', 403)
# step 14 LCM-Instantiate, Resource Group B / User Group all
self._step_lcm_instantiate(
'user_all', inst_id_b, self.vim_b['id'], 'namespace-b', 202)
self.steps_lcm_get_scale_heal_modify_with_area(inst_id_a, inst_id_b)
# NOTE: CNF has no LCM-Change-Connectivity
# step 34 LCM-Change-Connectivity, Resource Group A / User Group A
# step 35 LCM-Change-Connectivity, Resource Group b / User Group A
# step 36 LCM-Change-Connectivity, Resource Group B / User Group all
self.steps_lcm_terminate_delete_with_area(inst_id_a, inst_id_b)
def test_vnflcm_apis_vnf_instance_without_area_cnf(self):
self.register_subscription()
inst_id_c = self.steps_lcm_create_and_get_without_area()
# step 8 LCM-Instantiate, Resource Group C / User Group C
self._step_lcm_instantiate(
'user_c', inst_id_c, self.vim_c['id'], 'namespace-c', 202)
self.steps_lcm_get_scale_heal_modify_without_area(inst_id_c)
# NOTE: CNF has no LCM-Change-Connectivity
# step 27 LCM-Change-Connectivity, Resource Group C / User Group C
# step 28 LCM-Change-Connectivity, Resource Group C / User Group all
# step 29 LCM-Change-Connectivity, Resource Group C / User Group admin
self.steps_lcm_terminate_delete_without_area(inst_id_c)

View File

@ -0,0 +1,797 @@
#
# 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 time
import yaml
from oslo_utils import uuidutils
from tacker.sol_refactored.common import http_client
from tacker.sol_refactored import objects
from tacker.tests.functional.sol_enhanced_policy.base import (
BaseEnhancedPolicyTest)
from tacker.tests.functional.sol_kubernetes_v2.base_v2 import (
BaseVnfLcmKubernetesV2Test)
from tacker.tests.functional.sol_kubernetes_v2 import paramgen
from tacker.tests import utils as base_utils
class VnflcmAPIsV2CNFBase(BaseVnfLcmKubernetesV2Test, BaseEnhancedPolicyTest):
user_role_map = {
'user_a': ['VENDOR_company_A', 'AREA_area_A@region_A',
'TENANT_namespace-a', 'manager'],
'user_a_1': ['VENDOR_company_A', 'manager'],
'user_b': ['VENDOR_company_B', 'AREA_area_B@region_B',
'TENANT_namespace-b', 'manager'],
'user_c': ['VENDOR_company_C', 'AREA_area_C@region_C',
'TENANT_namespace-c', 'manager'],
'user_all': ['VENDOR_all', 'AREA_all@all', 'TENANT_all', 'manager'],
'user_admin': ['admin']
}
@classmethod
def setUpClass(cls):
BaseVnfLcmKubernetesV2Test.setUpClass()
BaseEnhancedPolicyTest.setUpClass(cls)
for user in cls.users:
client = cls.get_local_tacker_http_client(user.name)
setattr(
cls, cls.TK_HTTP_CLIENT_NAME % {'username': user.name}, client)
cls.tacker_client = cls.get_local_tacker_http_client('user_all')
cur_dir = os.path.dirname(__file__)
test_instantiate_cnf_resources_path = os.path.join(
cur_dir,
"../../sol_kubernetes_v2/samples/test_instantiate_cnf_resources")
test_change_vnf_pkg_with_deployment_path = os.path.join(
cur_dir,
"../../sol_kubernetes_v2/samples/"
"test_change_vnf_pkg_with_deployment")
cls.vnf_pkg_a, cls.vnfd_id_a = cls.create_vnf_package(
test_instantiate_cnf_resources_path, provider='company_A',
namespace='namespace-a')
cls.vnf_pkg_a_1, cls.vnfd_id_a_1 = cls.create_vnf_package(
test_change_vnf_pkg_with_deployment_path, provider='company_A',
namespace='namespace-a')
cls.vnf_pkg_b, cls.vnfd_id_b = cls.create_vnf_package(
test_instantiate_cnf_resources_path, provider='company_B',
namespace='namespace-b')
cls.vnf_pkg_b_1, cls.vnfd_id_b_1 = cls.create_vnf_package(
test_change_vnf_pkg_with_deployment_path, provider='company_B',
namespace='namespace-b')
cls.vnf_pkg_c, cls.vnfd_id_c = cls.create_vnf_package(
test_instantiate_cnf_resources_path, provider='company_C',
namespace='namespace-c')
cls.vnf_pkg_c_1, cls.vnfd_id_c_1 = cls.create_vnf_package(
test_change_vnf_pkg_with_deployment_path, provider='company_C',
namespace='namespace-c')
@classmethod
def tearDownClass(cls):
super(VnflcmAPIsV2CNFBase, cls).tearDownClass()
cls.delete_vnf_package(cls.vnf_pkg_a)
cls.delete_vnf_package(cls.vnf_pkg_a_1)
cls.delete_vnf_package(cls.vnf_pkg_b)
cls.delete_vnf_package(cls.vnf_pkg_b_1)
cls.delete_vnf_package(cls.vnf_pkg_c)
cls.delete_vnf_package(cls.vnf_pkg_c_1)
@classmethod
def get_vim_info(cls, vim_conf='local-vim.yaml'):
vim_params = yaml.safe_load(base_utils.read_file(vim_conf))
vim_params['auth_url'] += '/v3'
vim_info = objects.VimConnectionInfo(
interfaceInfo={'endpoint': vim_params['auth_url']},
accessInfo={
'region': 'RegionOne',
'project': vim_params['project_name'],
'username': vim_params['username'],
'password': vim_params['password'],
'userDomain': vim_params['user_domain_name'],
'projectDomain': vim_params['project_domain_name']
}
)
return vim_info
@classmethod
def get_local_tacker_http_client(cls, username):
vim_info = cls.get_vim_info(vim_conf=cls.local_vim_conf_file)
auth = http_client.KeystonePasswordAuthHandle(
auth_url=vim_info.interfaceInfo['endpoint'],
username=username,
password='devstack',
project_name=vim_info.accessInfo['project'],
user_domain_name=vim_info.accessInfo['userDomain'],
project_domain_name=vim_info.accessInfo['projectDomain']
)
return http_client.HttpClient(auth)
def _step_lcm_create(self, username, vnfd_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.test_instantiate_cnf_resources_create(
vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 201:
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
return inst_id
else:
return None
def _sample_instantiate(self, auth_url, bearer_token, ssl_ca_cert=None,
namespace='default', area=None, vim_id=None):
vim_id_1 = uuidutils.generate_uuid()
vim_id_2 = uuidutils.generate_uuid()
vim_1 = {
"vimId": vim_id_1,
"vimType": "kubernetes",
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"bearer_token": bearer_token,
},
"extra": {"dummy-key": "dummy-val"}
}
vim_2 = {
"vimId": vim_id_2,
"vimType": "kubernetes",
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "dummy_user",
"password": "dummy_password",
},
"extra": {"dummy-key": "dummy-val"}
}
if ssl_ca_cert:
vim_1["interfaceInfo"]["ssl_ca_cert"] = ssl_ca_cert
vim_2["interfaceInfo"]["ssl_ca_cert"] = ssl_ca_cert
if area:
vim_1.update({'extra': {'area': area}})
vim_2.update({'extra': {'area': area}})
if vim_id:
vim_1 = {
"vimId": vim_id,
"vimType": "kubernetes"
}
vim_2 = {
"vimId": vim_id,
"vimType": "kubernetes"
}
return {
"flavourId": "simple",
"vimConnectionInfo": {
"vim1": vim_1,
"vim2": vim_2
},
"additionalParams": {
"lcm-kubernetes-def-files": [
"Files/kubernetes/namespace.yaml",
"Files/kubernetes/deployment.yaml",
],
"namespace": namespace
}
}
def _step_lcm_instantiate(self, username, inst_id, namespace,
expected_status_code, area=None, vim_id=None):
self.tacker_client = self.get_tk_http_client_by_user(username)
# Instantiate a VNF instance
instantiate_req = self._sample_instantiate(
self.auth_url, self.bearer_token, ssl_ca_cert=self.ssl_ca_cert,
namespace=namespace, area=area, vim_id=vim_id
)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
def _step_lcm_show(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(expected_status_code, resp.status_code)
def _step_lcm_list(self, username, expected_inst_list):
self.tacker_client = self.get_tk_http_client_by_user(username)
path = "/vnflcm/v2/vnf_instances"
resp, vnf_instances = self.tacker_client.do_request(
path, "GET", version="2.0.0")
self.assertEqual(200, resp.status_code)
inst_ids = set([inst.get('id') for inst in vnf_instances])
for inst_id in expected_inst_list:
self.assertIn(inst_id, inst_ids)
def _step_lcm_scale_out(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
scale_out_req = {
"type": "SCALE_OUT",
"aspectId": "vdu2_aspect",
"numberOfSteps": 1
}
resp, body = self.scale_vnf_instance(inst_id, scale_out_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
def change_ext_conn_max(self, net_ids, subnets, auth_url, area):
vim_id_1 = uuidutils.generate_uuid()
vim_id_2 = uuidutils.generate_uuid()
ext_vl_1 = {
"id": uuidutils.generate_uuid(),
"vimConnectionId": vim_id_1,
"resourceProviderId": uuidutils.generate_uuid(),
"resourceId": net_ids['ft-net1'],
"extCps": [
{
"cpdId": "VDU1_CP1",
"cpConfig": {
"VDU1_CP1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
# "macAddress": omitted,
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
# "fixedAddresses": omitted,
"numDynamicAddresses": 1,
# "addressRange": omitted,
"subnetId": subnets[
'ft-ipv4-subnet1']}]
}
}]}
}
},
{
"cpdId": "VDU2_CP2",
"cpConfig": {
"VDU2_CP2": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
# "macAddress": omitted,
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": [
"22.22.22.101"
],
# "numDynamicAddresses": omitted
# "addressRange": omitted,
"subnetId": subnets['ft-ipv4-subnet1']
}, {
"type": "IPV6",
# "fixedAddresses": omitted,
# "numDynamicAddresses": omitted,
"numDynamicAddresses": 1,
# "addressRange": omitted,
"subnetId": subnets['ft-ipv6-subnet1']
}]
}
}]
}}
}
]
}
vim_1 = {
"vimId": vim_id_1,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"project": "nfv",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {"area": area}
}
vim_2 = {
"vimId": vim_id_2,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"password": "dummy_password",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {"area": area}
}
return {
"extVirtualLinks": [
ext_vl_1
],
"vimConnectionInfo": {
"vim1": vim_1,
"vim2": vim_2
},
"additionalParams": {"dummy-key": "dummy-val"}
}
def _step_lcm_change_vnfpkg(self, username, inst_id, new_vnfd_id,
expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
change_vnfpkg_req = paramgen.change_vnfpkg(new_vnfd_id)
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
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 change_vnfpkg completion.
time.sleep(3)
def _step_lcm_update(self, username, inst_id,
expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
update_req = {
"vnfInstanceName": "modify_{}".format(inst_id)
}
path = f"/vnflcm/v2/vnf_instances/{inst_id}"
resp, body = self.tacker_client.do_request(
path, "PATCH", body=update_req, version="2.0.0")
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
def _step_lcm_scale_in(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
scale_in_req = {
"type": "SCALE_IN",
"aspectId": "vdu2_aspect",
"numberOfSteps": 1
}
resp, body = self.scale_vnf_instance(inst_id, scale_in_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
def _step_lcm_heal(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user('user_admin')
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
# check vnfc_resource_info
vnfc_infos = body['instantiatedVnfInfo']['vnfcInfo']
vdu2_ids = [vnfc_info['id'] for vnfc_info in vnfc_infos
if vnfc_info['vduId'] == 'VDU2']
target = [vdu2_ids[0]]
heal_req = paramgen.max_sample_heal(target)
self.tacker_client = self.get_tk_http_client_by_user(username)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
def _step_lcm_terminate(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
terminate_req = paramgen.max_sample_terminate()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 202:
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(3)
def _step_lcm_delete(self, username, inst_id, expected_status_code):
self.tacker_client = self.get_tk_http_client_by_user(username)
# Delete a VNF instance
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == 204:
# check deletion of VNF instance
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(404, resp.status_code)
def vnflcm_apis_v2_cnf_test_before_instantiate(self):
# step 1 LCM-CreateV2, Resource Group A / User Group A
inst_id_a = self._step_lcm_create('user_a', self.vnfd_id_a, 201)
# step 2 LCM-CreateV2, Resource Group B / User Group A
self._step_lcm_create('user_a', self.vnfd_id_b, 403)
# step 3 LCM-CreateV2, Resource Group B / User Group all
inst_id_b = self._step_lcm_create('user_all', self.vnfd_id_b, 201)
# step 4 LCM-ShowV2, Resource Group A / User Group A
self._step_lcm_show('user_a', inst_id_a, 200)
# step 5 LCM-ShowV2, Resource Group A / User Group A-1
self._step_lcm_show('user_a_1', inst_id_a, 200)
# step 6 LCM-ShowV2, Resource Group B / User Group A
self._step_lcm_show('user_a', inst_id_b, 403)
# step 7 LCM-ShowV2, Resource Group B / User Group all
self._step_lcm_show('user_all', inst_id_b, 200)
# step 8 LCM-ListV2, Resource Group A / User Group A
self._step_lcm_list('user_a', [inst_id_a])
# step 9 LCM-ListV2, Resource Group - / User Group A-1
self._step_lcm_list('user_a_1', [inst_id_a])
# step 10 LCM-ListV2, Resource Group - / User Group B
self._step_lcm_list('user_b', [inst_id_b])
# step 11 LCM-ListV2, Resource Group - / User Group all
self._step_lcm_list('user_all', [inst_id_a, inst_id_b])
return inst_id_a, inst_id_b
def vnflcm_apis_v2_cnf_test_after_instantiate(self, inst_id_a, inst_id_b):
# step 15 LCM-ShowV2, Resource Group A / User Group A
self._step_lcm_show('user_a', inst_id_a, 200)
# step 16 LCM-Show, Resource Group A / User Group A-1
self._step_lcm_show('user_a_1', inst_id_a, 403)
# step 17 LCM-ShowV2, Resource Group B / User Group A
self._step_lcm_show('user_a', inst_id_b, 403)
# step 18 LCM-ShowV2, Resource Group B / User Group all
self._step_lcm_show('user_all', inst_id_b, 200)
# step 19 LCM-ListV2, Resource Group - / User Group A
self._step_lcm_list('user_a', [inst_id_a])
# step 20 LCM-ListV2, Resource Group - / User Group A-1
self._step_lcm_list('user_a_1', [])
# step 21 LCM-ListV2, Resource Group - / User Group B
self._step_lcm_list('user_b', [inst_id_b])
# step 22 LCM-ListV2, Resource Group - / User Group all
self._step_lcm_list('user_all', [inst_id_a, inst_id_b])
# step 23 LCM-ScaleV2(out), Resource Group A / User Group A
self._step_lcm_scale_out('user_a', inst_id_a, 202)
# step 24 LCM-ScaleV2(out), Resource Group B / User Group A
self._step_lcm_scale_out('user_a', inst_id_b, 403)
# step 25 LCM-ScaleV2(out), Resource Group B / User Group all
self._step_lcm_scale_out('user_all', inst_id_b, 202)
# step 26 LCM-ScaleV2(in), Resource Group A / User Group A
self._step_lcm_scale_in('user_a', inst_id_a, 202)
# step 27 LCM-ScaleV2(in), Resource Group B / User Group A
self._step_lcm_scale_in('user_a', inst_id_b, 403)
# step 28 LCM-ScaleV2(in), Resource Group B / User Group all
self._step_lcm_scale_in('user_all', inst_id_b, 202)
# step 29 LCM-HealV2, Resource Group A / User Group A
self._step_lcm_heal('user_a', inst_id_a, 202)
# step 30 LCM-HealV2, Resource Group B / User Group A
self._step_lcm_heal('user_a', inst_id_b, 403)
# step 31 LCM-HealV2, Resource Group B / User Group all
self._step_lcm_heal('user_all', inst_id_b, 202)
# step 32 LCM-ModifyV2, Resource Group A / User Group A
self._step_lcm_update('user_a', inst_id_a, 202)
# step 33 LCM-ModifyV2, Resource Group b / User Group A
self._step_lcm_update('user_a', inst_id_b, 403)
# step 34 LCM-ModifyV2, Resource Group B / User Group all
self._step_lcm_update('user_all', inst_id_b, 202)
# NOTE: CNF has no LCM-Change-Connectivity
# step 34 LCM-Change-ConnectivityV2, Resource Group A / User Group A
# step 35 LCM-Change-ConnectivityV2, Resource Group b / User Group A
# step 36 LCM-Change-ConnectivityV2, Resource Group B / User Group all
# step 38 LCM-Change-VnfPkgV2, Resource Group A / User Group A
self._step_lcm_change_vnfpkg(
'user_a', inst_id_a, self.vnfd_id_a_1, 202)
# step 39 LCM-Change-VnfPkgV2, Resource Group B / User Group A
self._step_lcm_change_vnfpkg(
'user_a', inst_id_b, self.vnfd_id_b_1, 403)
# step 40 LCM-Change-VnfPkgV2, Resource Group B / User Group all
self._step_lcm_change_vnfpkg(
'user_all', inst_id_b, self.vnfd_id_b_1, 202)
# step 41 LCM-TerminateV2, Resource Group A / User Group A
self._step_lcm_terminate('user_a', inst_id_a, 202)
# step 42 LCM-TerminateV2, Resource Group B / User Group A
self._step_lcm_terminate('user_a', inst_id_b, 403)
# step 43 LCM-TerminateV2, Resource Group B / User Group all
self._step_lcm_terminate('user_all', inst_id_b, 202)
# step 44 LCM-DeleteV2, Resource Group A / User Group A
self._step_lcm_delete('user_a', inst_id_a, 204)
# step 45 LCM-DeleteV2, Resource Group B / User Group A
self._step_lcm_delete('user_a', inst_id_b, 403)
# step 46 LCM-DeleteV2, Resource Group B / User Group all
self._step_lcm_delete('user_all', inst_id_b, 204)
class VnflcmAPIsV2VNFInstantiateWithArea(VnflcmAPIsV2CNFBase):
def test_vnflcm_apis_v2_cnf_without_area_in_vim_conn_info(self):
inst_id_a, inst_id_b = (
self.vnflcm_apis_v2_cnf_test_before_instantiate())
# step 12 LCM-InstantiateV2, Resource Group A / User Group A
self._step_lcm_instantiate(
'user_a', inst_id_a, 'namespace-a', 202, area='area_A@region_A')
# step 13 LCM-InstantiateV2, Resource Group B / User Group A
self._step_lcm_instantiate(
'user_a', inst_id_b, 'namespace-b', 403, area='area_B@region_B')
# step 14 LCM-InstantiateV2, Resource Group B / User Group all
self._step_lcm_instantiate(
'user_all', inst_id_b, 'namespace-b', 202, area='area_B@region_B')
self.vnflcm_apis_v2_cnf_test_after_instantiate(inst_id_a, inst_id_b)
class VnflcmAPIsV2CNFInstantiateWithoutArea(VnflcmAPIsV2CNFBase):
@classmethod
def setUpClass(cls):
super().setUpClass()
vim_type = 'kubernetes'
local_k8s_vim = 'local-k8s-vim.yaml'
cls.vim_a = cls._step_vim_register(
'user_a', vim_type, local_k8s_vim, 'vim_a', 'area_A@region_A')
cls.vim_a_1 = cls._step_vim_register(
'user_a', vim_type, local_k8s_vim, 'vim_a_1', 'area_A@region_A')
cls.vim_b = cls._step_vim_register(
'user_b', vim_type, local_k8s_vim, 'vim_b', 'area_B@region_B')
cls.vim_b_1 = cls._step_vim_register(
'user_b', vim_type, local_k8s_vim, 'vim_b_1', 'area_B@region_B')
@classmethod
def tearDownClass(cls):
cls._step_vim_delete('user_a', cls.vim_a)
cls._step_vim_delete('user_a', cls.vim_a_1)
cls._step_vim_delete('user_b', cls.vim_b)
cls._step_vim_delete('user_b', cls.vim_b_1)
super().tearDownClass()
def test_vnflcm_apis_v2_cnf_with_area_in_vim_conn_info(self):
inst_id_a, inst_id_b = (
self.vnflcm_apis_v2_cnf_test_before_instantiate())
# step 12 LCM-InstantiateV2, Resource Group A / User Group A
self._step_lcm_instantiate(
'user_a', inst_id_a, 'namespace-a', 202, vim_id=self.vim_a['id'])
# step 13 LCM-InstantiateV2, Resource Group B / User Group A
self._step_lcm_instantiate(
'user_a', inst_id_b, 'namespace-b', 403, vim_id=self.vim_b['id'])
# step 14 LCM-InstantiateV2, Resource Group B / User Group all
self._step_lcm_instantiate(
'user_all', inst_id_b, 'namespace-b', 202, vim_id=self.vim_b['id'])
self.vnflcm_apis_v2_cnf_test_after_instantiate(inst_id_a, inst_id_b)
class VnflcmAPIsV2CNFInstanceWithoutArea(VnflcmAPIsV2CNFBase):
@classmethod
def setUpClass(cls):
super().setUpClass()
vim_type = 'kubernetes'
local_k8s_vim = 'local-k8s-vim.yaml'
cls.vim_c = cls._step_vim_register(
'user_c', vim_type, local_k8s_vim, 'vim_c', None)
cls.vim_c_1 = cls._step_vim_register(
'user_c', vim_type, local_k8s_vim, 'vim_c_1', None)
@classmethod
def tearDownClass(cls):
cls._step_vim_delete('user_admin', cls.vim_c)
cls._step_vim_delete('user_admin', cls.vim_c_1)
super().tearDownClass()
def test_vnflcm_apis_v2_cnf_instance_without_area(self):
# step 1 LCM-CreateV2, Resource Group C / User Group C
inst_id_c = self._step_lcm_create('user_c', self.vnfd_id_c, 201)
# step 2 LCM-ShowV2, Resource Group C / User Group C
self._step_lcm_show('user_c', inst_id_c, 200)
# step 3 LCM-ShowV2, Resource Group C / User Group all
self._step_lcm_show('user_all', inst_id_c, 200)
# step 4 LCM-ShowV2, Resource Group C / User Group admin
self._step_lcm_show('user_admin', inst_id_c, 200)
# step 5 LCM-ListV2, Resource Group - / User Group C
self._step_lcm_list('user_c', [inst_id_c])
# step 6 LCM-ListV2, Resource Group - / User Group all
self._step_lcm_list('user_all', [inst_id_c])
# step 7 LCM-ListV2, Resource Group - / User Group admin
self._step_lcm_list('user_admin', [inst_id_c])
# step 8 LCM-InstantiateV2, Resource Group C / User Group C
self._step_lcm_instantiate('user_c', inst_id_c, 'namespace-c', 202,
vim_id=self.vim_c['id'])
# step 9 LCM-ShowV2, Resource Group C / User Group C
self._step_lcm_show('user_c', inst_id_c, 403)
# step 10 LCM-ShowV2, Resource Group C / User Group all
self._step_lcm_show('user_all', inst_id_c, 403)
# step 11 LCM-ShowV2, Resource Group C / User Group admin
self._step_lcm_show('user_admin', inst_id_c, 200)
# step 12 LCM-ListV2, Resource Group - / User Group C
self._step_lcm_list('user_c', [])
# step 13 LCM-ListV2, Resource Group - / User Group all
self._step_lcm_list('user_all', [])
# step 14 LCM-ListV2, Resource Group - / User Group admin
self._step_lcm_list('user_admin', [inst_id_c])
# step 15 LCM-ScaleV2(out), Resource Group C / User Group C
self._step_lcm_scale_out('user_c', inst_id_c, 403)
# step 16 LCM-ScaleV2(out), Resource Group C / User Group all
self._step_lcm_scale_out('user_all', inst_id_c, 403)
# step 17 LCM-ScaleV2(out), Resource Group C / User Group admin
self._step_lcm_scale_out('user_admin', inst_id_c, 202)
# step 18 LCM-ScaleV2(in), Resource Group C / User Group C
self._step_lcm_scale_in('user_c', inst_id_c, 403)
# step 19 LCM-ScaleV2(in), Resource Group C / User Group A
self._step_lcm_scale_in('user_all', inst_id_c, 403)
# step 20 LCM-ScaleV2(in), Resource Group C / User Group all
self._step_lcm_scale_in('user_admin', inst_id_c, 202)
# step 21 LCM-HealV2, Resource Group C / User Group C
self._step_lcm_heal('user_c', inst_id_c, 403)
# step 22 LCM-HealV2, Resource Group C / User Group A
self._step_lcm_heal('user_all', inst_id_c, 403)
# step 23 LCM-HealV2, Resource Group C / User Group all
self._step_lcm_heal('user_admin', inst_id_c, 202)
# step 24 LCM-ModifyV2, Resource Group C / User Group C
self._step_lcm_update('user_c', inst_id_c, 403)
# step 25 LCM-ModifyV2, Resource Group C / User Group A
self._step_lcm_update('user_all', inst_id_c, 403)
# step 26 LCM-ModifyV2, Resource Group C / User Group all
self._step_lcm_update('user_admin', inst_id_c, 202)
# NOTE: CNF has no LCM-Change-Connectivity
# step 27 LCM-Change-ConnectivityV2, Resource Group C / User Group C
# step 28 LCM-Change-ConnectivityV2, Resource Group C / User Group all
# step 29 LCM-Change-ConnectivityV2,
# Resource Group C / User Group admin
# step 30 LCM-Change-VnfPkgV2, Resource Group C / User Group C
self._step_lcm_change_vnfpkg(
'user_c', inst_id_c, self.vnfd_id_c_1, 403)
# step 31 LCM-Change-VnfPkgV2, Resource Group C / User Group A
self._step_lcm_change_vnfpkg(
'user_all', inst_id_c, self.vnfd_id_c_1, 403)
# step 32 LCM-Change-VnfPkgV2, Resource Group C / User Group all
self._step_lcm_change_vnfpkg(
'user_admin', inst_id_c, self.vnfd_id_c_1, 202)
# step 33 LCM-TerminateV2, Resource Group C / User Group C
self._step_lcm_terminate('user_c', inst_id_c, 403)
# step 34 LCM-TerminateV2, Resource Group C / User Group A
self._step_lcm_terminate('user_all', inst_id_c, 403)
# step 35 LCM-TerminateV2, Resource Group C / User Group all
self._step_lcm_terminate('user_admin', inst_id_c, 202)
# step 36 LCM-DeleteV2, Resource Group C / User Group C
self._step_lcm_delete('user_c', inst_id_c, 204)

View File

@ -0,0 +1,25 @@
#
# 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.tests.functional.sol_enhanced_policy.base import (
VimAPIsTest)
class VimAPIsOpenstackTest(VimAPIsTest):
def test_vim_apis_vim_with_area_kubernetes(self):
self._test_vim_apis_enhanced_policy('kubernetes', 'local-k8s-vim.yaml')
def test_vim_apis_vim_without_area_kubernetes(self):
self._test_vim_apis_vim_without_area_attribute(
'kubernetes', 'local-k8s-vim.yaml')

View File

@ -136,11 +136,13 @@ class BaseVnfLcmKubernetesV2Test(base.BaseTestCase):
return None return None
@classmethod @classmethod
def create_vnf_package(cls, sample_path, user_data={}, image_path=None): def create_vnf_package(cls, sample_path, user_data={}, image_path=None,
provider=None, namespace=None):
vnfd_id = uuidutils.generate_uuid() vnfd_id = uuidutils.generate_uuid()
tmp_dir = tempfile.mkdtemp() tmp_dir = tempfile.mkdtemp()
utils.make_zip(sample_path, tmp_dir, vnfd_id, image_path) utils.make_zip(sample_path, tmp_dir, vnfd_id, image_path,
provider=provider, namespace=namespace)
zip_file_name = os.path.basename(os.path.abspath(sample_path)) + ".zip" zip_file_name = os.path.basename(os.path.abspath(sample_path)) + ".zip"
zip_file_path = os.path.join(tmp_dir, zip_file_name) zip_file_path = os.path.join(tmp_dir, zip_file_name)

View File

@ -109,12 +109,13 @@ class BaseSolV2Test(base.BaseTestCase):
@classmethod @classmethod
def create_vnf_package(cls, sample_path, user_data={}, def create_vnf_package(cls, sample_path, user_data={},
image_path=None, nfvo=False, userdata_path=None): image_path=None, nfvo=False, userdata_path=None,
provider=None):
vnfd_id = uuidutils.generate_uuid() vnfd_id = uuidutils.generate_uuid()
tmp_dir = tempfile.mkdtemp() tmp_dir = tempfile.mkdtemp()
utils.make_zip(sample_path, tmp_dir, vnfd_id, image_path, utils.make_zip(sample_path, tmp_dir, vnfd_id, image_path,
userdata_path) userdata_path, provider)
zip_file_name = os.path.basename(os.path.abspath(sample_path)) + ".zip" zip_file_name = os.path.basename(os.path.abspath(sample_path)) + ".zip"
zip_file_path = os.path.join(tmp_dir, zip_file_name) zip_file_path = os.path.join(tmp_dir, zip_file_name)

View File

@ -13,10 +13,13 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import hashlib
import json import json
import os import os
import re
import shutil import shutil
import subprocess import subprocess
import yaml
# vnfdId of samples must be this. # vnfdId of samples must be this.
@ -24,7 +27,7 @@ SAMPLE_VNFD_ID = "b1bb0ce7-ebca-4fa7-95ed-4840d7000000"
def make_zip(sample_dir, tmp_dir, vnfd_id, image_path=None, def make_zip(sample_dir, tmp_dir, vnfd_id, image_path=None,
userdata_path=None): userdata_path=None, provider=None, namespace=None):
# NOTE: '.zip' will be added by shutil.make_archive # NOTE: '.zip' will be added by shutil.make_archive
zip_file_name = os.path.basename(os.path.abspath(sample_dir)) zip_file_name = os.path.basename(os.path.abspath(sample_dir))
zip_file_path = os.path.join(tmp_dir, zip_file_name) zip_file_path = os.path.join(tmp_dir, zip_file_name)
@ -60,9 +63,83 @@ def make_zip(sample_dir, tmp_dir, vnfd_id, image_path=None,
os.makedirs(file_path) os.makedirs(file_path)
shutil.copy(userdata_path, file_path) shutil.copy(userdata_path, file_path)
if provider:
# replace provider
def_path = os.path.join(tmp_contents, "Definitions")
for entry in os.listdir(def_path):
entry_path = os.path.join(def_path, entry)
with open(entry_path, 'r') as f:
data = yaml.safe_load(f)
_update_provider_in_yaml(data, provider)
with open(entry_path, 'w') as f:
yaml.dump(data, f, default_flow_style=False,
allow_unicode=True)
if namespace:
file_path = os.path.join(
tmp_contents, "Files", "kubernetes", "namespace.yaml")
with open(file_path, 'r') as f:
data = yaml.safe_load(f)
data["metadata"]["name"] = namespace
with open(file_path, 'w') as f:
yaml.dump(data, f, default_flow_style=False,
allow_unicode=True)
with open(file_path, 'r') as f:
content = f.read()
hash_value = hashlib.sha256(content.encode()).hexdigest()
tosca_file = os.path.join(tmp_contents, "TOSCA-Metadata", "TOSCA.meta")
with open(tosca_file, 'rb') as f:
artifacts_data = f.read()
artifacts_data_split = re.split(b'\n\n+', artifacts_data)
artifact_data_strs = []
for data in artifacts_data_split:
artifact_data_dict = yaml.safe_load(data)
if re.findall(b'.?Algorithm:.?|.?Hash:.?', data):
artifact_file = (artifact_data_dict['Source']
if 'Source' in artifact_data_dict.keys()
else artifact_data_dict['Name'])
if artifact_file.endswith('namespace.yaml'):
artifact_data_dict['Hash'] = hash_value
if artifact_data_dict:
artifact_data_strs.append(
yaml.dump(
artifact_data_dict,
default_flow_style=False,
allow_unicode=True))
with open(tosca_file, 'w') as f:
f.write('\n'.join(artifact_data_strs))
shutil.make_archive(zip_file_path, "zip", tmp_contents) shutil.make_archive(zip_file_path, "zip", tmp_contents)
def _update_provider_in_yaml(data, provider):
try:
prop = data['topology_template']['node_templates']['VNF'][
'properties']
if prop.get('provider', None):
prop['provider'] = provider
except KeyError:
# Let's check for 'node_types'
pass
if not data.get('node_types', None):
return
for ntype in data['node_types'].values():
if ntype['derived_from'] != 'tosca.nodes.nfv.VNF':
continue
try:
desc_id = ntype['properties']['provider']
if desc_id.get('constraints', None):
for constraint in desc_id.get('constraints'):
if constraint.get('valid_values', None):
constraint['valid_values'] = [provider]
if desc_id.get('default', None):
desc_id['default'] = provider
except KeyError:
# Let's check next node_type
pass
def create_network(network): def create_network(network):
# assume OS_* environment variables are already set # assume OS_* environment variables are already set
subprocess.run( subprocess.run(

View File

@ -13,13 +13,18 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import ddt
from oslo_config import cfg
from unittest import mock from unittest import mock
from tacker import context
from tacker.sol_refactored.api.policies.vnflcm_v2 import POLICY_NAME
from tacker.sol_refactored.api import wsgi as sol_wsgi from tacker.sol_refactored.api import wsgi as sol_wsgi
from tacker.sol_refactored.common import exceptions as sol_ex from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.tests.unit import base from tacker.tests.unit import base
@ddt.ddt
class TestWsgi(base.TestCase): class TestWsgi(base.TestCase):
def test_response_too_big(self): def test_response_too_big(self):
@ -46,3 +51,88 @@ class TestWsgi(base.TestCase):
request.headers = {} request.headers = {}
self.assertRaises(sol_ex.APIVersionMissing, self.assertRaises(sol_ex.APIVersionMissing,
resource._check_api_version, request, 'action') resource._check_api_version, request, 'action')
@mock.patch.object(context.Context, 'can')
def test_enhanced_policy_action(self, mock_can):
cfg.CONF.set_override(
"enhanced_tacker_policy", True, group='oslo_policy')
resource = sol_wsgi.SolResource(sol_wsgi.SolAPIController(),
policy_name=POLICY_NAME)
request = mock.Mock()
request.context = context.Context()
request.headers = {}
enhanced_policy_actions = [
'create',
'index',
'show',
'delete',
'update',
'instantiate',
'terminate',
'scale',
'heal',
'change_ext_conn',
'change_vnfpkg'
]
for action in enhanced_policy_actions:
resource._check_policy(request, action)
mock_can.assert_not_called()
@ddt.data('api_versions',
'subscription_create',
'subscription_list',
'subscription_show',
'subscription_delete',
'lcm_op_occ_list',
'lcm_op_occ_show',
'lcm_op_occ_retry',
'lcm_op_occ_rollback',
'lcm_op_occ_fail',
'lcm_op_occ_delete')
@mock.patch.object(context.Context, 'can')
def test_not_enhanced_policy_action(self, action, mock_can):
cfg.CONF.set_override(
"enhanced_tacker_policy", True, group='oslo_policy')
resource = sol_wsgi.SolResource(sol_wsgi.SolAPIController(),
policy_name=POLICY_NAME)
request = mock.Mock()
request.context = context.Context()
request.headers = {}
resource._check_policy(request, action)
mock_can.assert_called_once()
@ddt.data('create',
'index',
'show',
'delete',
'update',
'instantiate',
'terminate',
'scale',
'heal',
'change_ext_conn',
'change_vnfpkg',
'api_versions',
'subscription_create',
'subscription_list',
'subscription_show',
'subscription_delete',
'lcm_op_occ_list',
'lcm_op_occ_show',
'lcm_op_occ_retry',
'lcm_op_occ_rollback',
'lcm_op_occ_fail',
'lcm_op_occ_delete')
@mock.patch.object(context.Context, 'can')
def test_enhanced_policy_is_false(self, action, mock_can):
cfg.CONF.set_override(
"enhanced_tacker_policy", False, group='oslo_policy')
resource = sol_wsgi.SolResource(sol_wsgi.SolAPIController(),
policy_name=POLICY_NAME)
request = mock.Mock()
request.context = context.Context()
request.headers = {}
resource._check_policy(request, action)
mock_can.assert_called_once()

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@
from copy import deepcopy from copy import deepcopy
import datetime import datetime
from http import client as http_client
import iso8601 import iso8601
import json import json
import os import os
@ -32,6 +33,7 @@ from tacker.objects.instantiate_vnf_req import ExtVirtualLinkData
from tacker.objects.instantiate_vnf_req import InstantiateVnfRequest from tacker.objects.instantiate_vnf_req import InstantiateVnfRequest
from tacker.objects import scale_vnf_request from tacker.objects import scale_vnf_request
from tacker.objects.vim_connection import VimConnectionInfo from tacker.objects.vim_connection import VimConnectionInfo
from tacker.policies import vnf_lcm as vnf_lcm_policies
from tacker.tests import constants from tacker.tests import constants
from tacker.tests import uuidsentinel from tacker.tests import uuidsentinel
from tacker import wsgi from tacker import wsgi
@ -106,9 +108,9 @@ def fake_vnf_package_vnfd_model_dict(**updates):
return vnfd return vnfd
def return_vnf_package_vnfd(): def return_vnf_package_vnfd(**updates):
model_obj = models.VnfPackageVnfd() model_obj = models.VnfPackageVnfd()
model_obj.update(fake_vnf_package_vnfd_model_dict()) model_obj.update(fake_vnf_package_vnfd_model_dict(**updates))
return model_obj return model_obj
@ -1904,3 +1906,510 @@ def fake_subscription_response(**updates):
data = _subscription_links(data) data = _subscription_links(data)
return data return data
def get_test_data_policy_create():
rules = {vnf_lcm_policies.VNFLCM % 'create': "vendor:%(vendor)s"}
test_data = [
# 'expected_status_code': http_client.OK
{
'vnfd_updates': {'vnf_provider': 'provider_A'},
'rules': rules,
'roles': ['VENDOR_provider_A'],
'expected_status_code': http_client.CREATED
},
{
'vnfd_updates': {'vnf_provider': 'provider_A'},
'rules': rules,
'roles': ['VENDOR_all'],
'expected_status_code': http_client.CREATED
},
{
'vnfd_updates': {'vnf_provider': 'provider_B'},
'rules': rules,
'roles': ['VENDOR_provider_B'],
'expected_status_code': http_client.CREATED
},
{
'vnfd_updates': {'vnf_provider': 'provider_B'},
'rules': rules,
'roles': ['VENDOR_all'],
'expected_status_code': http_client.CREATED
},
{
'vnfd_updates': {'vnf_provider': 'provider_B'},
'rules': {vnf_lcm_policies.VNFLCM % 'create': "@"},
'roles': [],
'expected_status_code': http_client.CREATED
},
# 'expected_status_code': http_client.FORBIDDEN
{
'vnfd_updates': {'vnf_provider': ''},
'rules': rules,
'roles': ['VENDOR_'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnfd_updates': {'vnf_provider': 'provider_A'},
'rules': rules,
'roles': ['VENDOR_B'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnfd_updates': {'vnf_provider': 'provider_B'},
'rules': rules,
'roles': ['VENDOR_A'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnfd_updates': {'vnf_provider': 'provider_A'},
'rules': rules,
'roles': [],
'expected_status_code': http_client.FORBIDDEN
},
]
return test_data
def make_vnf_instance(vim_type, vendor, area=None, tenant=None,
instantiated_state=fields.VnfInstanceState.NOT_INSTANTIATED):
vnf_instance_updates = make_vnf_instance_updates(
vim_type, vendor, area=area, tenant=tenant)
return return_vnf_instance(instantiated_state, **vnf_instance_updates)
def get_test_data_policy_index():
rule_area_vendor_tenant = {
vnf_lcm_policies.VNFLCM % 'index':
"area:%(area)s and vendor:%(vendor)s and "
"tenant:%(tenant)s"
}
test_data = []
inst_1 = make_vnf_instance('openstack', 'provider_A',
area='area_A@region_A')
test_data.append({
'vnf_instance_list': [inst_1],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_area_A@region_A', 'VENDOR_provider_A'],
'expected_vnf_inst_ids': [inst_1.id]
})
test_data.append({
'vnf_instance_list': [inst_1],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@region_A', 'VENDOR_provider_A'],
'expected_vnf_inst_ids': [inst_1.id]
})
test_data.append({
'vnf_instance_list': [inst_1],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@all', 'VENDOR_all'],
'expected_vnf_inst_ids': [inst_1.id]
})
inst_2 = make_vnf_instance(
'openstack', 'provider_A', area='area_A@region_A',
instantiated_state=fields.VnfInstanceState.INSTANTIATED)
test_data.append({
'vnf_instance_list': [inst_2],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_area_A@region_A', 'VENDOR_provider_A'],
'expected_vnf_inst_ids': [inst_2.id]
})
test_data.append({
'vnf_instance_list': [inst_2],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@region_A', 'VENDOR_provider_A'],
'expected_vnf_inst_ids': [inst_2.id]
})
test_data.append({
'vnf_instance_list': [inst_2],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@all', 'VENDOR_all'],
'expected_vnf_inst_ids': [inst_2.id]
})
inst_3 = make_vnf_instance(
'openstack', 'provider_A',
instantiated_state=fields.VnfInstanceState.INSTANTIATED)
test_data.append({
'vnf_instance_list': [inst_3],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_area_A@region_A', 'VENDOR_provider_A'],
'expected_vnf_inst_ids': []
})
test_data.append({
'vnf_instance_list': [inst_3],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@region_A', 'VENDOR_provider_A'],
'expected_vnf_inst_ids': []
})
test_data.append({
'vnf_instance_list': [inst_3],
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@all', 'VENDOR_all'],
'expected_vnf_inst_ids': []
})
inst_4 = make_vnf_instance(
'kubernetes', 'provider_A',
tenant='namespace_A',
area='area_A@region_A',
instantiated_state=fields.VnfInstanceState.INSTANTIATED)
test_data.append({
'vnf_instance_list': [inst_4],
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_area_A@region_A',
'VENDOR_provider_A',
'TENANT_namespace_A'
],
'expected_vnf_inst_ids': [inst_4.id]
})
test_data.append({
'vnf_instance_list': [inst_4],
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_all@region_A',
'VENDOR_provider_A',
'TENANT_namespace_A'
],
'expected_vnf_inst_ids': [inst_4.id]
})
test_data.append({
'vnf_instance_list': [inst_4],
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_all@all',
'VENDOR_all',
'TENANT_namespace_A'
],
'expected_vnf_inst_ids': [inst_4.id]
})
return test_data
def get_test_data_policy_instantiate():
test_data = [
# 'expected_status_code': http_client.ACCEPTED
{
'vnf_instance_updates': {'vnf_provider': 'provider_A'},
'rules': {
"os_nfv_orchestration_api:vnf_instances:instantiate":
"vendor:%(vendor)s"
},
'roles': ['VENDOR_provider_A'],
'expected_status_code': http_client.ACCEPTED
},
{
'vnf_instance_updates': {'vnf_provider': 'provider_A'},
'rules': {
"os_nfv_orchestration_api:vnf_instances:instantiate":
"vendor:%(vendor)s"
},
'roles': ['VENDOR_all'],
'expected_status_code': http_client.ACCEPTED
},
# 'expected_status_code': http_client.FORBIDDEN
{
'vnf_instance_updates': {'vnf_provider': 'provider_A'},
'rules': {
"os_nfv_orchestration_api:vnf_instances:instantiate":
"vendor:%(vendor)s"
},
'roles': [],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': {'vnf_provider': 'provider_A'},
'rules': {
"os_nfv_orchestration_api:vnf_instances:instantiate":
"vendor:%(vendor)s"
},
'roles': ['VENDOR_provider_B'],
'expected_status_code': http_client.FORBIDDEN
},
]
return test_data
def get_test_data_policy_vnf_not_instantiated(
action, success_status_code=http_client.OK):
# openstack
vim_connection_info = {
"id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vim_id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vim_type": 'openstack',
"access_info": {"key1": 'value1', "key2": 'value2'},
'extra': {'area': 'area_A@region_A'}
}
vim_connection_info_wrong_area = {
"id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vim_id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vim_type": 'openstack',
"access_info": {"key1": 'value1', "key2": 'value2'},
'extra': {'area': 'area_A'}
}
vnf_instance_updates = {
'vnf_provider': 'provider_A',
'vim_connection_info': [vim_connection_info]
}
vnf_instance_updates_with_wrong_area = {
'vnf_provider': 'provider_A',
'vim_connection_info': [vim_connection_info_wrong_area]
}
rule_area_vendor_tenant = {
vnf_lcm_policies.VNFLCM % action:
"area:%(area)s and vendor:%(vendor)s and "
"tenant:%(tenant)s"
}
rule_vendor = {
vnf_lcm_policies.VNFLCM % action: "vendor:%(vendor)s"
}
test_data = [
# 'expected_status_code': http_client.OK
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_area_A@region_A', 'VENDOR_provider_A'],
'expected_status_code': success_status_code
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@all', 'VENDOR_all', 'TENANT_all'],
'expected_status_code': success_status_code
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@region_A', 'VENDOR_all', 'TENANT_all'],
'expected_status_code': success_status_code
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_all@region_A', 'AREA_all@all', 'VENDOR_all',
'TENANT_all'],
'expected_status_code': success_status_code
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_vendor,
'roles': ['AREA_area_A@region_A', 'VENDOR_provider_A'],
'expected_status_code': success_status_code
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_vendor,
'roles': ['AREA_all@all', 'VENDOR_all', 'TENANT_all'],
'expected_status_code': success_status_code
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_vendor,
'roles': ['AREA_all@region_A', 'VENDOR_all', 'TENANT_all'],
'expected_status_code': success_status_code
},
# 'expected_status_code': http_client.FORBIDDEN
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': [],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_area_A@region_A'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['VENDOR_provider_A'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_area_A@region_A', 'VENDOR_provider_B'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_area_B@region_A', 'VENDOR_provider_A'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@region_B', 'VENDOR_provider_A'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all', 'VENDOR_provider_A'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': vnf_instance_updates,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@', 'VENDOR_provider_A'],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnf_instance_updates': vnf_instance_updates_with_wrong_area,
'rules': rule_area_vendor_tenant,
'roles': ['AREA_all@', 'VENDOR_provider_A'],
'expected_status_code': http_client.FORBIDDEN
},
]
return test_data
def make_vnf_instance_updates(
vim_type, vendor, area=None, tenant=None):
vim_connection_info = VimConnectionInfo(
id='f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
vim_id='f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
vim_type=vim_type,
access_info={"key1": 'value1', "key2": 'value2'},
)
if area:
vim_connection_info.extra = {'area': area}
vnf_instance_updates = {
'vnf_provider': vendor,
'vim_connection_info': [vim_connection_info]
}
if tenant:
vnf_instance_updates.update({'vnf_metadata': {"namespace": tenant}})
return vnf_instance_updates
def get_test_data_policy_vnf_instantiated(action, success_status_code):
rule_area_vendor_tenant = {
vnf_lcm_policies.VNFLCM % action:
"area:%(area)s and vendor:%(vendor)s and "
"tenant:%(tenant)s"
}
test_data = []
# openstack
vnf_instance_updates_1 = make_vnf_instance_updates(
'openstack', 'provider_A', area='area_A@region_A')
test_data.append({
'vnf_instance_updates': vnf_instance_updates_1,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_area_A@region_A',
'VENDOR_provider_A'
],
'expected_status_code': success_status_code
})
test_data.append({
'vnf_instance_updates': vnf_instance_updates_1,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_all@region_A',
'VENDOR_provider_A'
],
'expected_status_code': success_status_code
})
test_data.append({
'vnf_instance_updates': vnf_instance_updates_1,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_all@all',
'VENDOR_provider_A'
],
'expected_status_code': success_status_code
})
test_data.append({
'vnf_instance_updates': vnf_instance_updates_1,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_all@all',
'VENDOR_all'
],
'expected_status_code': success_status_code
})
test_data.append({
'vnf_instance_updates': vnf_instance_updates_1,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_all@all',
'VENDOR_all'
],
'expected_status_code': success_status_code
})
# wrong area role
test_data.append({
'vnf_instance_updates': vnf_instance_updates_1,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_area_B@region_A',
'VENDOR_provider_A'
],
'expected_status_code': http_client.FORBIDDEN
})
# wrong region role
test_data.append({
'vnf_instance_updates': vnf_instance_updates_1,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_area_A@region_B',
'VENDOR_provider_A'
],
'expected_status_code': http_client.FORBIDDEN
})
# wrong vendor role
test_data.append({
'vnf_instance_updates': vnf_instance_updates_1,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_area_A@region_A',
'VENDOR_provider_B'
],
'expected_status_code': http_client.FORBIDDEN
})
vnf_instance_updates_2 = make_vnf_instance_updates(
'openstack', 'provider_A')
test_data.append({
'vnf_instance_updates': vnf_instance_updates_2,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_area_A@region_A',
'VENDOR_provider_A'
],
'expected_status_code': http_client.FORBIDDEN
})
# kubernetes
vnf_instance_updates_3 = make_vnf_instance_updates(
'kubernetes', 'provider_A', area='area_A@region_A',
tenant='namespace_A')
test_data.append({
'vnf_instance_updates': vnf_instance_updates_3,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_area_A@region_A',
'VENDOR_provider_A',
'TENANT_namespace_A'
],
'expected_status_code': success_status_code
})
test_data.append({
'vnf_instance_updates': vnf_instance_updates_3,
'rules': rule_area_vendor_tenant,
'roles': [
'AREA_all@all',
'VENDOR_all',
'TENANT_all'
],
'expected_status_code': success_status_code
})
return test_data

View File

@ -26,6 +26,7 @@ import webob
from webob import exc from webob import exc
from oslo_config import cfg from oslo_config import cfg
from oslo_policy import policy as oslo_policy
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from tacker.api.views import vnf_subscriptions as vnf_subscription_view from tacker.api.views import vnf_subscriptions as vnf_subscription_view
@ -42,6 +43,7 @@ from tacker.manager import TackerManager
from tacker import objects from tacker import objects
from tacker.objects import fields from tacker.objects import fields
from tacker.objects import vnf_lcm_subscriptions as subscription_obj from tacker.objects import vnf_lcm_subscriptions as subscription_obj
from tacker import policy
from tacker.tests import constants from tacker.tests import constants
from tacker.tests.unit import base from tacker.tests.unit import base
from tacker.tests.unit.db import utils from tacker.tests.unit.db import utils
@ -261,11 +263,13 @@ class TestController(base.TestCase):
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()}) test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(objects.vnf_instance, '_vnf_instance_update')
@mock.patch.object(objects.vnf_instance, '_vnf_instance_create') @mock.patch.object(objects.vnf_instance, '_vnf_instance_create')
@mock.patch.object(objects.vnf_package_vnfd.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
def test_create_without_name_and_description( def test_create_without_name_and_description(
self, mock_get_by_id, self, mock_get_by_id,
mock_vnf_instance_create, mock_vnf_instance_create,
mock_vnf_instance_update,
mock_get_service_plugins, mock_get_service_plugins,
mock_package_save, mock_package_save,
mock_private_create_vnf, mock_private_create_vnf,
@ -285,6 +289,8 @@ class TestController(base.TestCase):
mock_vnf_instance_create.return_value =\ mock_vnf_instance_create.return_value =\
fakes.return_vnf_instance_model(**updates) fakes.return_vnf_instance_model(**updates)
mock_vnf_instance_update.return_value = \
fakes.return_vnf_instance_model(**updates)
body = {'vnfdId': uuidsentinel.vnfd_id, body = {'vnfdId': uuidsentinel.vnfd_id,
"vnfInstanceName": "SampleVnf", "vnfInstanceName": "SampleVnf",
@ -396,17 +402,20 @@ class TestController(base.TestCase):
return_value={'VNFM': test_nfvo_plugin.FakeVNFMPlugin()}) return_value={'VNFM': test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(sync_resource.SyncVnfPackage, 'create_package') @mock.patch.object(sync_resource.SyncVnfPackage, 'create_package')
@mock.patch.object(nfvo_client.VnfPackageRequest, "index") @mock.patch.object(nfvo_client.VnfPackageRequest, "index")
@mock.patch.object(objects.vnf_instance, '_vnf_instance_update')
@mock.patch.object(objects.vnf_instance, '_vnf_instance_create') @mock.patch.object(objects.vnf_instance, '_vnf_instance_create')
@mock.patch.object(objects.vnf_package_vnfd.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
def test_create_vnf_package_not_found( def test_create_vnf_package_not_found(
self, mock_get_by_id_package_vnfd, self, mock_get_by_id_package_vnfd,
mock_vnf_instance_create, mock_vnf_instance_create,
mock_vnf_instance_update,
mock_index, mock_create_pkg, mock_index, mock_create_pkg,
mock_get_service_plugins, mock_get_service_plugins,
mock_private_create_vnf, mock_private_create_vnf,
mock_vnf_package_get_by_id, mock_vnf_package_get_by_id,
mock_update_package_usage_state, mock_update_package_usage_state,
mock_get_vim): mock_get_vim):
mock_get_vim.return_value = self.vim_info
mock_get_by_id_package_vnfd.side_effect =\ mock_get_by_id_package_vnfd.side_effect =\
exceptions.VnfPackageVnfdNotFound exceptions.VnfPackageVnfdNotFound
@ -421,6 +430,8 @@ class TestController(base.TestCase):
updates = {'vnfd_id': uuidsentinel.vnfd_id} updates = {'vnfd_id': uuidsentinel.vnfd_id}
mock_vnf_instance_create.return_value =\ mock_vnf_instance_create.return_value =\
fakes.return_vnf_instance_model(**updates) fakes.return_vnf_instance_model(**updates)
mock_vnf_instance_update.return_value =\
fakes.return_vnf_instance_model(**updates)
body = {'vnfdId': uuidsentinel.vnfd_id} body = {'vnfdId': uuidsentinel.vnfd_id}
req = fake_request.HTTPRequest.blank('/vnf_instances') req = fake_request.HTTPRequest.blank('/vnf_instances')
@ -584,6 +595,9 @@ class TestController(base.TestCase):
mock_get_vnf, mock_insta_notfi_process, mock_get_vnf, mock_insta_notfi_process,
mock_get_service_plugins): mock_get_service_plugins):
vim = fakes.return_default_vim()
vim.update({'extra': {"area": "area_A@region_A"}})
mock_get_vim.return_value = vim
mock_vnf_instance_get_by_id.return_value =\ mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model() fakes.return_vnf_instance_model()
mock_vnf_package_vnfd_get_by_id.return_value = \ mock_vnf_package_vnfd_get_by_id.return_value = \
@ -595,7 +609,12 @@ class TestController(base.TestCase):
vnf_id=mock_vnf_instance_get_by_id.return_value.id, vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE') status='INACTIVE')
body = {"flavourId": "simple"} body = {"flavourId": "simple",
"vimConnectionInfo": [
{"id": uuidsentinel.vim_connection_id,
"vimId": uuidsentinel.vim_id,
"vimType": 'openstack'}
]}
body.update({"additionalParams": {"foo_number": 12}}) body.update({"additionalParams": {"foo_number": 12}})
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/instantiate' % uuidsentinel.vnf_instance_id) '/vnf_instances/%s/instantiate' % uuidsentinel.vnf_instance_id)
@ -673,6 +692,9 @@ class TestController(base.TestCase):
mock_get_vnf, mock_insta_notif_process, mock_get_vnf, mock_insta_notif_process,
mock_get_service_plugins): mock_get_service_plugins):
vim = fakes.return_default_vim()
vim.update({'extra': {"area": "area_A@region_A"}})
mock_get_vim.return_value = vim
mock_vnf_instance_get_by_id.return_value =\ mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model() fakes.return_vnf_instance_model()
mock_vnf_package_vnfd_get_by_id.return_value = \ mock_vnf_package_vnfd_get_by_id.return_value = \
@ -815,6 +837,9 @@ class TestController(base.TestCase):
mock_get_vnf, mock_insta_notif_process, mock_get_vnf, mock_insta_notif_process,
mock_get_service_plugins): mock_get_service_plugins):
vim = fakes.return_default_vim()
vim.update({'extra': {"area": "area_A@region_A"}})
mock_get_vim.return_value = vim
mock_vnf_instance_get_by_id.return_value =\ mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model() fakes.return_vnf_instance_model()
mock_vnf_package_vnfd_get_by_id.return_value = \ mock_vnf_package_vnfd_get_by_id.return_value = \
@ -4814,3 +4839,790 @@ class TestController(base.TestCase):
"<class 'webob.exc.HTTPInternalServerError'>"}} "<class 'webob.exc.HTTPInternalServerError'>"}}
expected_msg = expected_vnf['tackerFault']['message'] expected_msg = expected_vnf['tackerFault']['message']
self.assertEqual(expected_msg, resp.json['detail']) self.assertEqual(expected_msg, resp.json['detail'])
@ddt.ddt
class TestControllerEnhancedPolicy(TestController):
def setUp(self):
super(TestControllerEnhancedPolicy, self).setUp()
cfg.CONF.set_override(
"enhanced_tacker_policy", True, group='oslo_policy')
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(objects.VNF, "vnf_index_list")
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
@mock.patch.object(objects.VnfPackageVnfd, 'get_vnf_package_vnfd')
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "update")
def test_update_vnf_with_pkg_id(
self, mock_update,
mock_vnf_package_vnf_get_vnf_package_vnfd,
mock_vnf_instance_list, mock_vnf_index_list,
mock_get_service_plugins, mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnf_instance()
mock_vnf_index_list.return_value = fakes._get_vnf()
mock_vnf_instance_list.return_value = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_package_vnf_get_vnf_package_vnfd.return_value =\
fakes.return_vnf_package_vnfd()
body = {"vnfInstanceName": "new_instance_name",
"vnfInstanceDescription": "new_instance_discription",
"vnfPkgId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnfConfigurableProperties": {"test": "test_value"},
"vnfcInfoModificationsDeleteIds": ["test1"]}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
# Call Instantiate API
resp = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_update.assert_called_once()
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(objects.VNF, "vnf_index_list")
def test_update_vnf_status_err(
self,
mock_vnf_index_list,
mock_get_service_plugins,
mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnf_instance()
updates = {'status': 'ERROR'}
mock_vnf_index_list.return_value = fakes._get_vnf(**updates)
body = {"vnfInstanceName": "new_instance_name",
"vnfInstanceDescription": "new_instance_discription",
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnfConfigurableProperties": {
"test": "test_value"
},
"vnfcInfoModificationsDeleteIds": ["test1"],
"metadata": {"testkey": "test_value"},
"vimConnectionInfo": {"id": "testid"}}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
msg = _("VNF %(id)s status is %(state)s") % {
"id": constants.UUID, "state": "ERROR"}
res = self._make_problem_detail(msg %
{"state": "ERROR"}, 409, 'Conflict')
resp = req.get_response(self.app)
self.assertEqual(res.text, resp.text)
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(sync_resource.SyncVnfPackage, 'create_package')
@mock.patch.object(objects.vnf_package_vnfd.VnfPackageVnfd,
"get_vnf_package_vnfd")
@mock.patch.object(nfvo_client.VnfPackageRequest, "index")
@mock.patch.object(objects.VNF, "vnf_index_list")
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
@mock.patch.object(objects.VnfPackageVnfd, 'get_vnf_package_vnfd')
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "update")
def test_update_vnf_none_vnfd(
self,
mock_update,
mock_vnf_package_vnf_get_vnf_package_vnfd,
mock_vnf_instance_list,
mock_vnf_index_list,
mock_index,
mock_get_vnf_package_vnfd,
mock_create_package,
mock_get_service_plugins,
mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnf_instance()
mock_vnf_index_list.return_value = fakes._get_vnf()
mock_vnf_instance_list.return_value = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_package_vnf_get_vnf_package_vnfd.return_value = ""
mock_get_vnf_package_vnfd.side_effect =\
exceptions.VnfPackageVnfdNotFound
mock_create_package.return_value = fakes.return_vnf_package_vnfd()
mock_response = mock.MagicMock()
mock_response.ok = True
mock_response.json = mock.MagicMock()
mock_response.json.return_value = ['aaa', 'bbb', 'ccc']
mock_index.return_value = mock_response
body = {
"vnfInstanceName": "new_instance_name",
"vnfInstanceDescription": "new_instance_discription",
"vnfPkgId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnfConfigurableProperties": {"test": "test_value"},
"vnfcInfoModificationsDeleteIds": ["test1"]
}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
resp = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_update.assert_called_once()
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(objects.VNF, "vnf_index_list")
def test_update_vnf_none_vnf_data(
self,
mock_vnf_index_list,
mock_get_service_plugins,
mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnf_instance()
mock_vnf_index_list.return_value = None
body = {"vnfInstanceName": "new_instance_name",
"vnfInstanceDescription": "new_instance_discription",
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnfConfigurableProperties": {
"test": "test_value"
},
"vnfcInfoModificationsDeleteIds": ["test1"],
"metadata": {"testkey": "test_value"},
"vimConnectionInfo": {"id": "testid"}}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
msg = _("Can not find requested vnf data: %s") % constants.UUID
res = self._make_problem_detail(msg, 404, title='Not Found')
resp = req.get_response(self.app)
self.assertEqual(res.text, resp.text)
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(objects.VNF, "vnf_index_list")
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
def test_update_vnf_none_instance_data(
self,
mock_vnf_instance_list,
mock_vnf_index_list,
mock_get_service_plugins,
mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnf_instance()
mock_vnf_index_list.return_value = fakes._get_vnf()
mock_vnf_instance_list.return_value = ""
body = {"vnfInstanceName": "new_instance_name",
"vnfInstanceDescription": "new_instance_discription",
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnfConfigurableProperties": {
"test": "test_value"
},
"vnfcInfoModificationsDeleteIds": ["test1"],
"metadata": {"testkey": "test_value"},
"vimConnectionInfo": {"id": "testid"}}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
vnf_data = fakes._get_vnf()
msg = ("Can not find requested vnf instance data: %s") % vnf_data.get(
'vnfd_id')
res = self._make_problem_detail(msg, 404, title='Not Found')
resp = req.get_response(self.app)
self.assertEqual(res.text, resp.text)
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(objects.VNF, "vnf_index_list")
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
@mock.patch.object(objects.VnfPackageVnfd, 'get_vnf_package_vnfd')
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "update")
def test_update_vnf(
self,
mock_update,
mock_vnf_package_vnf_get_vnf_package_vnfd,
mock_vnf_instance_list,
mock_vnf_index_list,
mock_get_service_plugins,
mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnf_instance()
mock_vnf_index_list.return_value = fakes._get_vnf()
mock_vnf_instance_list.return_value = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_package_vnf_get_vnf_package_vnfd.return_value =\
fakes.return_vnf_package_vnfd()
body = {"vnfInstanceName": "new_instance_name",
"vnfInstanceDescription": "new_instance_discription",
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnfConfigurableProperties": {
"test": "test_value"
},
"vnfcInfoModificationsDeleteIds": ["test1"],
"metadata": {"testkey": "test_value"},
"vimConnectionInfo": {"id": "testid"}}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
# Call Instantiate API
resp = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_update.assert_called_once()
@ddt.data("vnfdId", "vnfPkgId")
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(sync_resource.SyncVnfPackage, 'create_package')
@mock.patch.object(objects.vnf_package_vnfd.VnfPackageVnfd,
"get_vnf_package_vnfd")
@mock.patch.object(nfvo_client.VnfPackageRequest, "index")
@mock.patch.object(objects.VNF, "vnf_index_list")
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
@mock.patch.object(objects.VnfPackageVnfd, 'get_vnf_package_vnfd')
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "update")
def test_update_none_vnf_package_info(
self, input_id,
mock_update,
mock_vnf_package_vnf_get_vnf_package_vnfd,
mock_vnf_instance_list,
mock_vnf_index_list,
mock_index,
mock_get_vnf_package_vnfd,
mock_create_package,
mock_get_service_plugins,
mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnf_instance()
mock_vnf_index_list.return_value = fakes._get_vnf()
mock_vnf_instance_list.return_value = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_package_vnf_get_vnf_package_vnfd.return_value = ""
mock_get_vnf_package_vnfd.side_effect = \
exceptions.VnfPackageVnfdNotFound
mock_create_package.return_value = fakes.return_vnf_package_vnfd()
mock_response = mock.MagicMock()
mock_response.ok = False
mock_response.json = mock.MagicMock()
mock_response.json.return_value = ['aaa', 'bbb', 'ccc']
mock_index.return_value = mock_response
body = {"vnfInstanceName": "new_instance_name",
"vnfInstanceDescription": "new_instance_discription",
input_id: "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnfConfigurableProperties": {"test": "test_value"},
"vnfcInfoModificationsDeleteIds": ["test1"]}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@ddt.data(*fakes.get_test_data_policy_vnf_not_instantiated(
'show'))
@ddt.unpack
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
def test_show_enhanced_policy_vnf_not_instantiated(
self, mock_vnf_by_id, vnf_instance_updates,
rules, roles, expected_status_code):
mock_vnf_by_id.return_value = fakes.return_vnf_instance_model(
**vnf_instance_updates)
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % uuidsentinel.instance_id)
req.method = 'GET'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_policy_vnf_instantiated(
'show', http_client.OK))
@ddt.unpack
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_show_enhanced_policy_vnf_instantiated(
self, mock_vnf_by_id, vnf_instance_updates,
rules, roles, expected_status_code):
mock_vnf_by_id.return_value = fakes.return_vnf_instance(
instantiated_state=fields.VnfInstanceState.INSTANTIATED,
**vnf_instance_updates)
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % uuidsentinel.instance_id)
req.method = 'GET'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_policy_create())
@ddt.unpack
@mock.patch.object(objects.VnfPackage, 'get_by_id')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._create_vnf')
@mock.patch.object(objects.vnf_instance, '_vnf_instance_update')
@mock.patch.object(objects.vnf_instance, '_vnf_instance_create')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
def test_create_enhanced_policy(
self,
mock_vnf_package_vnfd_get_by_id,
mock_get_vim,
mock_vnf_instance_create,
mock_vnf_instance_update,
mock_private_create_vnf,
mock_vnf_package_get_by_id,
vnfd_updates,
rules,
roles,
expected_status_code):
mock_vnf_package_vnfd_get_by_id.return_value = \
fakes.return_vnf_package_vnfd(
**vnfd_updates)
updates = {'vnfd_id': uuidsentinel.vnfd_id}
mock_vnf_instance_create.return_value = \
fakes.return_vnf_instance_model(**updates)
mock_vnf_instance_update.return_value = \
fakes.return_vnf_instance_model(**updates)
mock_get_vim.return_value = self.vim_info
body = {'vnfdId': uuidsentinel.vnfd_id,
'metadata': {"key": "value"}}
req = fake_request.HTTPRequest.blank('/vnf_instances')
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_policy_vnf_instantiated(
'terminate', http_client.ACCEPTED))
@ddt.unpack
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "terminate")
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_terminate_enhanced_policy(
self, mock_vnf_by_id, mock_get_vnf, mock_save,
mock_notification_process, mock_terminate,
vnf_instance_updates,
rules, roles, expected_status_code):
vnf_instance_obj = fakes.return_vnf_instance(
instantiated_state=fields.VnfInstanceState.INSTANTIATED,
**vnf_instance_updates)
mock_vnf_by_id.return_value = vnf_instance_obj
mock_get_vnf.return_value = \
self._get_dummy_vnf(vnf_id=vnf_instance_obj.id, status='ACTIVE')
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/terminate' % uuidsentinel.instance_id)
body = {'terminationType': 'FORCEFUL'}
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_policy_vnf_instantiated(
'heal', http_client.ACCEPTED))
@ddt.unpack
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_heal_enhanced_policy(
self, mock_vnf_by_id, mock_get_vnf, mock_save,
mock_notification_process,
vnf_instance_updates,
rules, roles, expected_status_code):
vnf_instance_obj = fakes.return_vnf_instance(
instantiated_state=fields.VnfInstanceState.INSTANTIATED,
**vnf_instance_updates)
mock_vnf_by_id.return_value = vnf_instance_obj
mock_get_vnf.return_value = \
self._get_dummy_vnf(vnf_id=vnf_instance_obj.id, status='ACTIVE')
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/heal' % uuidsentinel.instance_id)
body = {}
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_policy_vnf_instantiated(
'delete', http_client.NO_CONTENT))
@ddt.unpack
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._delete')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_delete_enhanced_policy(
self,
mock_vnf_by_id,
mock_get_vnf,
mock_notification_process,
mock_private_delete,
vnf_instance_updates,
rules, roles, expected_status_code):
vnf_instance_obj = fakes.return_vnf_instance_delete(
**vnf_instance_updates)
mock_vnf_by_id.return_value = vnf_instance_obj
mock_get_vnf.return_value = \
self._get_dummy_vnf(vnf_id=vnf_instance_obj.id, status='ACTIVE')
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % uuidsentinel.vnf_instance_id)
req.headers['Content-Type'] = 'application/json'
req.method = 'DELETE'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_policy_vnf_instantiated(
'scale', http_client.ACCEPTED))
@ddt.unpack
@mock.patch.object(objects.VnfLcmOpOcc, "create")
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(objects.VnfInstance, "get_by_id")
# @mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "_scale")
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._scale')
def test_scale_enhanced_policy(
self, mock_scale, mock_vnf_by_id,
mock_save,
mock_notification_process,
mock_get_vnf,
mock_get_service_plugins,
mock_opocc_create,
vnf_instance_updates,
rules, roles, expected_status_code):
res = webob.Response()
res.status_int = 202
location = ('Location', 'http://mock.mock')
res.headerlist.append(location)
mock_scale.return_value = res
vnf_instance_obj = fakes.return_vnf_instance(
instantiated_state=fields.VnfInstanceState.INSTANTIATED,
scale_status="scale_status",
**vnf_instance_updates)
mock_vnf_by_id.return_value = vnf_instance_obj
mock_get_vnf.return_value = fakes._get_vnf()
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/scale' % uuidsentinel.instance_id)
body = {"type": "SCALE_OUT",
"aspectId": "SP1",
"numberOfSteps": 1,
"additionalParams": {
"test": "test_value"}}
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_policy_vnf_instantiated(
'update_vnf', http_client.ACCEPTED))
@ddt.unpack
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(objects.VNF, "vnf_index_list")
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
@mock.patch.object(objects.VnfPackageVnfd, 'get_vnf_package_vnfd')
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "update")
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_update_vnf_enhanced_policy(
self,
mock_vnf_by_id,
mock_update,
mock_vnf_package_vnf_get_vnf_package_vnfd,
mock_vnf_instance_list,
mock_vnf_index_list,
mock_get_service_plugins,
vnf_instance_updates,
rules, roles, expected_status_code):
vnf_instance_obj = fakes.return_vnf_instance(
instantiated_state=fields.VnfInstanceState.INSTANTIATED,
scale_status="scale_status",
**vnf_instance_updates)
mock_vnf_by_id.return_value = vnf_instance_obj
mock_vnf_index_list.return_value = fakes._get_vnf()
mock_vnf_instance_list.return_value = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED, **vnf_instance_updates)
mock_vnf_package_vnf_get_vnf_package_vnfd.return_value =\
fakes.return_vnf_package_vnfd()
body = {"vnfInstanceName": "new_instance_name",
"vnfInstanceDescription": "new_instance_discription",
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnfConfigurableProperties": {
"test": "test_value"
},
"vnfcInfoModificationsDeleteIds": ["test1"],
"metadata": {"testkey": "test_value"},
"vimConnectionInfo": {"id": "testid"}}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
# Call Instantiate API
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code != http_client.FORBIDDEN:
mock_update.assert_called_once()
@ddt.data(*fakes.get_test_data_policy_vnf_instantiated(
'change_ext_conn', http_client.ACCEPTED))
@ddt.unpack
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "change_ext_conn")
def test_change_ext_conn_enhanced_policy(
self, mock_rpc, mock_save,
mock_vnf_by_id, mock_get_vnf,
mock_notification_process,
mock_get_service_plugins,
vnf_instance_updates,
rules, roles, expected_status_code):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED,
**vnf_instance_updates
)
mock_vnf_by_id.return_value = vnf_instance_obj
mock_get_vnf.return_value = \
self._get_dummy_vnf(vnf_id=vnf_instance_obj.id, status='ACTIVE')
body = fakes.get_change_ext_conn_request_body()
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/change_ext_conn' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code != http_client.FORBIDDEN:
mock_rpc.assert_called_once()
@ddt.data(*fakes.get_test_data_policy_instantiate())
@ddt.unpack
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.VnfLcmController.'
'_notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "instantiate")
def test_instantiate_with_vim_connection_enhanced_policy(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_insta_notif_process,
mock_get_service_plugins, vnf_instance_updates,
rules, roles, expected_status_code):
vim = fakes.return_default_vim()
vim.update({'extra': {"area": "area_A@region_A"}})
mock_get_vim.return_value = vim
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model(**vnf_instance_updates)
mock_vnf_package_vnfd_get_by_id.return_value = \
fakes.return_vnf_package_vnfd()
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple",
"vimConnectionInfo": [
{"id": uuidsentinel.vim_connection_id,
"vimId": uuidsentinel.vim_id,
"vimType": 'openstack'}
]}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/instantiate' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
# Call Instantiate API
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code != http_client.FORBIDDEN:
self.assertTrue('Location' in resp.headers.keys())
expected_location = (self.expected_location_prefix +
str(mock_insta_notif_process.return_value))
self.assertEqual(expected_location, resp.headers['location'])
mock_instantiate.assert_called_once()
mock_get_vnf.assert_called_once()
mock_insta_notif_process.assert_called_once()
@ddt.data(*fakes.get_test_data_policy_index())
@ddt.unpack
@mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
def test_index_enhanced_policy(self, mock_vnf_list, vnf_instance_list,
rules, roles, expected_vnf_inst_ids):
mock_vnf_list.return_value = vnf_instance_list
req = fake_request.HTTPRequest.blank('/vnf_instances')
req.headers['Content-Type'] = 'application/json'
req.method = 'GET'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(http_client.OK, resp.status_code)
self.assertEqual(
expected_vnf_inst_ids, [inst.get('id') for inst in resp.json])
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.VnfLcmController.'
'_notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "instantiate")
def test_instantiate_add_area(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_insta_notif_process,
mock_get_service_plugins):
vim = fakes.return_default_vim()
vim.update({'extra': {"area": "area_A@region_A"}})
mock_get_vim.return_value = vim
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
mock_vnf_package_vnfd_get_by_id.return_value = \
fakes.return_vnf_package_vnfd()
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple",
"vimConnectionInfo": [
{"id": uuidsentinel.vim_connection_id,
"vimId": uuidsentinel.vim_id,
"vimType": 'openstack'}
]}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/instantiate' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
# Call Instantiate API
resp = req.get_response(self.app)
self.assertTrue('Location' in resp.headers.keys())
expected_location = (self.expected_location_prefix +
str(mock_insta_notif_process.return_value))
self.assertEqual(expected_location, resp.headers['location'])
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_instantiate.assert_called_once()
mock_get_vnf.assert_called_once()
mock_insta_notif_process.assert_called_once()

View File

@ -16,8 +16,10 @@
from copy import deepcopy from copy import deepcopy
import datetime import datetime
from http import client as http_client
import iso8601 import iso8601
import os import os
from oslo_utils import uuidutils
import shutil import shutil
import uuid import uuid
import webob import webob
@ -30,6 +32,7 @@ from tacker.objects import vnf_deployment_flavour as vnf_deployment_flavour_obj
from tacker.objects import vnf_package as vnf_package_obj from tacker.objects import vnf_package as vnf_package_obj
from tacker.objects import vnf_package_vnfd as vnf_package_vnfd_obj from tacker.objects import vnf_package_vnfd as vnf_package_vnfd_obj
from tacker.objects import vnf_software_image as vnf_software_image_obj from tacker.objects import vnf_software_image as vnf_software_image_obj
from tacker.policies import vnf_package as vnf_package_policies
from tacker.tests import constants from tacker.tests import constants
from tacker.tests import utils from tacker.tests import utils
from tacker.tests import uuidsentinel from tacker.tests import uuidsentinel
@ -296,3 +299,128 @@ def return_vnfd_data(csar_without_tosca_meta=False):
shutil.rmtree(csar_temp_dir) shutil.rmtree(csar_temp_dir)
return file_path_and_data return file_path_and_data
def get_test_data_pkg_index():
rules = {
vnf_package_policies.VNFPKGM % 'index':
"vendor:%(vendor)s"
}
id_a = uuidutils.generate_uuid()
pkg_provider_a = return_vnfpkg_obj(
vnf_package_updates={'id': uuidutils.generate_uuid()},
vnfd_updates={
'vnf_provider': 'provider_A',
'id': id_a,
})
id_b = uuidutils.generate_uuid()
pkg_provider_b = return_vnfpkg_obj(
vnf_package_updates={'id': uuidutils.generate_uuid()},
vnfd_updates={
'vnf_provider': 'provider_B',
'id': id_b,
})
# id_c = uuidutils.generate_uuid()
updates = {'id': uuidutils.generate_uuid(),
'onboarding_state': 'CREATED',
'operational_state': 'DISABLED'}
pkg_provider_c = return_vnfpkg_obj(
vnf_package_updates=updates
)
test_data = [
{
'pkg_list': [pkg_provider_a],
'rules': rules,
'roles': ['VENDOR_provider_A'],
'expected_pkg_ids': [pkg_provider_a.id]
},
{
'pkg_list': [pkg_provider_b],
'rules': rules,
'roles': ['VENDOR_provider_B'],
'expected_pkg_ids': [pkg_provider_b.id]
},
{
'pkg_list': [pkg_provider_a, pkg_provider_b],
'rules': rules,
'roles': ['VENDOR_all'],
'expected_pkg_ids': [pkg_provider_a.id, pkg_provider_b.id]
},
{
'pkg_list': [pkg_provider_c],
'rules': rules,
'roles': [],
'expected_pkg_ids': [pkg_provider_c.id]
},
{
'pkg_list': [pkg_provider_c],
'rules': rules,
'roles': ['VENDOR_provider_A'],
'expected_pkg_ids': [pkg_provider_c.id]
},
{
'pkg_list': [pkg_provider_c],
'rules': rules,
'roles': ['VENDOR_all'],
'expected_pkg_ids': [pkg_provider_c.id]
},
]
return test_data
def get_test_data_pkg_uploaded(action, success_status_code):
rules = {
vnf_package_policies.VNFPKGM % action:
"vendor:%(vendor)s"
}
test_data = [
# 'expected_status_code': success_status_code
{
'vnfd_updates': {
'vnf_provider': 'provider_A',
},
'rules': rules,
'roles': [
'VENDOR_provider_A',
],
'expected_status_code': success_status_code
},
{
'vnfd_updates': {
'vnf_provider': 'provider_A',
},
'rules': rules,
'roles': [
'VENDOR_all',
],
'expected_status_code': success_status_code
},
# 'expected_status_code': http_client.FORBIDDEN
{
'vnfd_updates': {
'vnf_provider': 'provider_A',
},
'rules': rules,
'roles': [
'VENDOR_provider_B',
],
'expected_status_code': http_client.FORBIDDEN
},
{
'vnfd_updates': {
'vnf_provider': 'provider_A',
},
'rules': rules,
'roles': [],
'expected_status_code': http_client.FORBIDDEN
},
]
return test_data
def get_test_data_pkg_to_upload(action, success_status_code):
test_data = get_test_data_pkg_uploaded(action, success_status_code)
for item in test_data:
item['vnf_data'] = item.pop('vnfd_updates')
item['vnf_data']['provider'] = item['vnf_data'].pop('vnf_provider')
return test_data

View File

@ -24,17 +24,22 @@ import urllib
from webob import exc from webob import exc
from oslo_config import cfg from oslo_config import cfg
from oslo_policy import policy as oslo_policy
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from tacker.api.vnfpkgm.v1 import controller from tacker.api.vnfpkgm.v1 import controller
from tacker.common import csar_utils
from tacker.common import exceptions as tacker_exc from tacker.common import exceptions as tacker_exc
from tacker.conductor.conductorrpc.vnf_pkgm_rpc import VNFPackageRPCAPI from tacker.conductor.conductorrpc.vnf_pkgm_rpc import VNFPackageRPCAPI
from tacker import context
from tacker.glance_store import store as glance_store from tacker.glance_store import store as glance_store
from tacker import objects from tacker import objects
from tacker.objects import fields from tacker.objects import fields
from tacker.objects import vnf_package from tacker.objects import vnf_package
from tacker.objects.vnf_package import VnfPackagesList from tacker.objects.vnf_package import VnfPackagesList
from tacker.objects.vnf_software_image import VnfSoftwareImage from tacker.objects.vnf_software_image import VnfSoftwareImage
from tacker.policies import vnf_package as vnf_package_policies
from tacker import policy
from tacker.tests import constants from tacker.tests import constants
from tacker.tests.unit import base from tacker.tests.unit import base
from tacker.tests.unit import fake_request from tacker.tests.unit import fake_request
@ -679,25 +684,25 @@ class TestController(base.TestCase):
@mock.patch.object(objects.VnfPackage, "get_by_id") @mock.patch.object(objects.VnfPackage, "get_by_id")
def test_delete_with_operational_state_enabled(self, mock_vnf_by_id): def test_delete_with_operational_state_enabled(self, mock_vnf_by_id):
vnfpkg_updates = {
'operational_state': fields.PackageOperationalStateType.ENABLED}
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj(
vnf_package_updates=vnfpkg_updates)
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnfpkgm/v1/vnf_packages/%s' % constants.UUID) '/vnfpkgm/v1/vnf_packages/%s' % constants.UUID)
vnf_package_dict = fakes.fake_vnf_package()
vnf_package_dict['operational_state'] = \
fields.PackageOperationalStateType.ENABLED
vnf_package = objects.VnfPackage(**vnf_package_dict)
mock_vnf_by_id.return_value = vnf_package
self.assertRaises(exc.HTTPConflict, self.controller.delete, self.assertRaises(exc.HTTPConflict, self.controller.delete,
req, constants.UUID) req, constants.UUID)
@mock.patch.object(vnf_package.VnfPackage, "get_by_id") @mock.patch.object(vnf_package.VnfPackage, "get_by_id")
def test_delete_with_usage_state_in_use(self, mock_vnf_by_id): def test_delete_with_usage_state_in_use(self, mock_vnf_by_id):
vnfpkg_updates = {
'usage_state': fields.PackageUsageStateType.IN_USE}
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj(
vnf_package_updates=vnfpkg_updates)
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnfpkgm/v1/vnf_packages/%s' % constants.UUID) '/vnfpkgm/v1/vnf_packages/%s' % constants.UUID)
vnf_package_dict = fakes.fake_vnf_package()
vnf_package_dict['usage_state'] = \
fields.PackageUsageStateType.IN_USE
vnf_package = objects.VnfPackage(**vnf_package_dict)
mock_vnf_by_id.return_value = vnf_package
self.assertRaises(exc.HTTPConflict, self.controller.delete, self.assertRaises(exc.HTTPConflict, self.controller.delete,
req, constants.UUID) req, constants.UUID)
@ -1370,3 +1375,297 @@ class TestController(base.TestCase):
self.controller.fetch_vnf_package_artifacts, self.controller.fetch_vnf_package_artifacts,
req, constants.UUID, req, constants.UUID,
constants.ARTIFACT_PATH) constants.ARTIFACT_PATH)
@ddt.ddt
class TestControllerEnhancedPolicy(TestController):
def setUp(self):
super(TestControllerEnhancedPolicy, self).setUp()
cfg.CONF.set_override(
"enhanced_tacker_policy", True, group='oslo_policy')
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
@mock.patch.object(glance_store, 'store_csar')
@mock.patch.object(VNFPackageRPCAPI, "upload_vnf_package_content")
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(vnf_package.VnfPackage, "save")
def test_upload_vnf_package_content(self, mock_vnf_pack_save,
mock_vnf_by_id,
mock_upload_vnf_package_content,
mock_glance_store,
mock_load_csar,
mock_load_csar_data):
updates = {'onboarding_state': 'CREATED',
'operational_state': 'DISABLED'}
vnf_package_dict = fakes.fake_vnf_package(updates)
vnf_package_obj = objects.VnfPackage(**vnf_package_dict)
mock_vnf_by_id.return_value = vnf_package_obj
mock_vnf_pack_save.return_value = vnf_package_obj
mock_glance_store.return_value = 'location', 0, 'checksum', \
'multihash', 'loc_meta'
mock_load_csar.return_value = '/var/lib/tacker/5f5d99c6-844a-4c3' \
'1-9e6d-ab21b87dcfff.zip'
mock_load_csar_data.return_value = (
{'provider': 'company'}, mock.ANY, mock.ANY)
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s/package_content'
% constants.UUID)
req.headers['Content-Type'] = 'application/zip'
req.method = 'PUT'
req.body = jsonutils.dump_as_bytes(mock.mock_open())
resp = req.get_response(self.app)
mock_glance_store.assert_called()
self.assertEqual(http_client.ACCEPTED, resp.status_code)
@ddt.data(*fakes.get_test_data_pkg_to_upload('upload_package_content',
http_client.ACCEPTED))
@ddt.unpack
@mock.patch.object(glance_store, 'delete_csar')
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
@mock.patch.object(glance_store, 'store_csar')
@mock.patch.object(VNFPackageRPCAPI, "upload_vnf_package_content")
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(vnf_package.VnfPackage, "save")
def test_upload_vnf_package_content_enhanced_policy(
self, mock_vnf_pack_save,
mock_vnf_by_id,
mock_upload_vnf_package_content,
mock_glance_store,
mock_load_csar,
mock_load_csar_data,
mock_delete_csar,
vnf_data, rules, roles, expected_status_code):
updates = {'onboarding_state': 'CREATED',
'operational_state': 'DISABLED'}
vnf_package_dict = fakes.fake_vnf_package(updates)
vnf_package_obj = objects.VnfPackage(**vnf_package_dict)
mock_vnf_by_id.return_value = vnf_package_obj
mock_vnf_pack_save.return_value = vnf_package_obj
mock_glance_store.return_value = 'location', 0, 'checksum', \
'multihash', 'loc_meta'
mock_load_csar.return_value = '/var/lib/tacker/5f5d99c6-844a-4c3' \
'1-9e6d-ab21b87dcfff.zip'
mock_load_csar_data.return_value = (
vnf_data, mock.ANY, mock.ANY)
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s/package_content'
% constants.UUID)
req.headers['Content-Type'] = 'application/zip'
req.method = 'PUT'
req.body = jsonutils.dump_as_bytes(mock.mock_open())
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
mock_glance_store.assert_called()
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code == http_client.FORBIDDEN:
mock_delete_csar.assert_called()
@ddt.data(*fakes.get_test_data_pkg_uploaded('show', http_client.OK))
@ddt.unpack
@mock.patch.object(VnfSoftwareImage, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
def test_show_enhanced_policy(
self, mock_vnf_by_id, mock_sw_image_by_id,
vnfd_updates, rules, roles, expected_status_code):
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj(
vnfd_updates=vnfd_updates)
mock_sw_image_by_id.return_value = fakes.return_software_image()
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s' % constants.UUID)
req.method = 'GET'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@mock.patch.object(VnfSoftwareImage, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@ddt.data(['VENDOR_provider_A'], [])
def test_show_enhanced_policy_created(
self, roles, mock_vnf_by_id, mock_sw_image_by_id):
updates = {'onboarding_state': 'CREATED',
'operational_state': 'DISABLED'}
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj(
vnf_package_updates=updates)
mock_sw_image_by_id.return_value = fakes.return_software_image()
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s' % constants.UUID)
req.method = 'GET'
rules = {
vnf_package_policies.VNFPKGM % 'show':
"vendor:%(vendor)s"
}
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(http_client.OK, resp.status_code)
@ddt.data(
*fakes.get_test_data_pkg_uploaded('delete', http_client.NO_CONTENT))
@ddt.unpack
@mock.patch.object(vnf_package.VnfPackage, "destroy")
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(VNFPackageRPCAPI, "delete_vnf_package")
def test_delete_enhanced_policy(
self, mock_delete_rpc, mock_vnf_by_id, mock_vnf_pack_destroy,
vnfd_updates, rules, roles, expected_status_code):
vnfpkg_updates = {'operational_state': 'DISABLED'}
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj(
vnf_package_updates=vnfpkg_updates, vnfd_updates=vnfd_updates)
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s' % constants.UUID)
req.headers['Content-Type'] = 'application/json'
req.method = 'DELETE'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_pkg_uploaded('patch', http_client.OK))
@ddt.unpack
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(vnf_package.VnfPackage, "save")
def test_patch_enhanced_policy(self, mock_save, mock_vnf_by_id,
vnfd_updates, rules, roles, expected_status_code):
vnf_package_updates = {'operational_state': 'DISABLED'}
mock_vnf_by_id.return_value = \
fakes.return_vnfpkg_obj(
vnf_package_updates=vnf_package_updates,
vnfd_updates=vnfd_updates
)
req_body = {"operationalState": "ENABLED",
"userDefinedData": {"testKey1": "val01",
"testKey2": "val02", "testkey3": "val03"}}
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s'
% constants.UUID)
req.headers['Content-Type'] = 'application/json'
req.method = 'PATCH'
req.body = jsonutils.dump_as_bytes(req_body)
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context(
'fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
if expected_status_code != http_client.FORBIDDEN:
self.assertEqual(req_body, jsonutils.loads(resp.body))
@ddt.data(*fakes.get_test_data_pkg_uploaded(
'get_vnf_package_vnfd', http_client.OK))
@ddt.unpack
@mock.patch.object(VNFPackageRPCAPI, "get_vnf_package_vnfd")
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
def test_get_vnf_package_vnfd_enhanced_policy(
self, mock_vnf_by_id, mock_get_vnf_package_vnfd,
vnfd_updates, rules, roles, expected_status_code):
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj(
vnfd_updates=vnfd_updates)
mock_get_vnf_package_vnfd.return_value = fakes.return_vnfd_data()
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s/vnfd'
% constants.UUID)
req.headers['Accept'] = 'application/zip'
req.method = 'GET'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context('fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_pkg_uploaded('fetch_package_content',
http_client.ACCEPTED))
@ddt.unpack
@mock.patch.object(controller.VnfPkgmController, "_download")
@mock.patch.object(controller.VnfPkgmController, "_get_range_from_request")
@mock.patch.object(glance_store, 'get_csar_size')
@mock.patch.object(controller.VnfPkgmController, '_get_vnf_package')
@mock.patch.object(vnf_package.VnfPackage, 'save')
def test_fetch_vnf_package_content_enhanced_policy(
self,
mock_save,
mock_get_vnf_package,
mock_get_csar_size,
mock_get_range,
mock_download,
vnfd_updates, rules, roles, expected_status_code):
mock_get_vnf_package.return_value = fakes.return_vnfpkg_obj(
vnfd_updates=vnfd_updates)
mock_get_csar_size.return_value = 1000
mock_get_range.return_value = "10-20, 21-30"
mock_download.return_value = "Response"
request = fake_request.HTTPRequest.blank(
'/vnf_packages/%s/package_content' % constants.UUID)
request.method = 'GET'
request.headers["Range"] = 'bytes=10-20,21-30'
request.response = ""
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context('fake', 'fake', roles=roles)
resp = request.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_pkg_uploaded('fetch_artifact',
http_client.OK))
@ddt.unpack
@mock.patch.object(controller.VnfPkgmController, "_get_csar_path")
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
def test_fetch_vnf_package_artifacts_enhanced_policy(
self, mock_vnf_by_id, mock_get_csar_path,
vnfd_updates, rules, roles, expected_status_code):
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj(
vnfd_updates=vnfd_updates)
base_path = os.path.dirname(os.path.abspath(__file__))
extract_path = os.path.join(base_path, '../../etc/samples/'
'sample_vnf_package_csar_in_meta_and_manifest')
mock_get_csar_path.return_value = extract_path
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s/artifacts/%s'
% (constants.UUID, constants.ARTIFACT_PATH))
req.method = 'GET'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context('fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(expected_status_code, resp.status_code)
@ddt.data(*fakes.get_test_data_pkg_index())
@ddt.unpack
@mock.patch.object(VnfPackagesList, "get_by_marker_filter")
def test_index_enhanced_policy(
self, mock_vnf_list, pkg_list, rules, roles, expected_pkg_ids):
mock_vnf_list.return_value = pkg_list
req = fake_request.HTTPRequest.blank('/vnf_packages')
req.headers['Content-Type'] = 'application/json'
req.method = 'GET'
policy.set_rules(oslo_policy.Rules.from_dict(rules), overwrite=True)
ctx = context.Context('fake', 'fake', roles=roles)
resp = req.get_response(fakes.wsgi_app_v1(fake_auth_context=ctx))
self.assertEqual(http_client.OK, resp.status_code)
self.assertEqual(
expected_pkg_ids, [pkg.get('id') for pkg in resp.json])

11
tox.ini
View File

@ -101,6 +101,17 @@ setenv = {[testenv]setenv}
commands = commands =
stestr --test-path=./tacker/tests/functional/sol_v2_az_retry run --slowest --concurrency 1 {posargs} stestr --test-path=./tacker/tests/functional/sol_v2_az_retry run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-enhanced-policy-sol]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_enhanced_policy/sol run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-enhanced-policy-sol-kubernetes]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_enhanced_policy/sol_kubernetes run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-https-v2] [testenv:dsvm-functional-sol-https-v2]
setenv = {[testenv]setenv} setenv = {[testenv]setenv}