Add VNF package update API

Added API to update information of VNF package.

Implemented below API:-

* PATCH /vnf_packages/{vnfPkgId}

Co-authored-by: Shubham Potale <shubham.potale@nttdata.com>
Change-Id: I6d60e87b48a6703362dcd30975f300f524f8ca7a
Implements: bp enhance-vnf-package-support-part1
This commit is contained in:
ajay.parja 2019-10-16 21:04:41 +05:30 committed by tpatil
parent 067d00371b
commit c74cad521c
17 changed files with 577 additions and 61 deletions

View File

@ -498,6 +498,19 @@ tenant_id_opt:
in: body
required: false
type: string
update_operational_state:
description: |
New value of the operational state of the on-boarded instance of the VNF
package. Valid values are "ENABLED" and "DISABLED". See note.
in: body
required: false
type: string
update_user_defined_data:
description: |
User defined data to be updated. For existing keys, the value is replaced.
in: body
required: false
type: object
updated_at:
description: |
The date and time when the resource was updated.
@ -506,6 +519,19 @@ updated_at:
in: body
required: true
type: string
updated_operational_state:
description: |
Updated value of the operational state of the on-boarded instance of
the VNF package.
in: body
required: false
type: string
updated_user_defined_data:
description: |
Updated value of user defined data.
in: body
required: false
type: object
usageState:
description: |
Usage state of the VNF package.

View File

@ -0,0 +1,7 @@
{
"operationalState": "DISABLED",
"userDefinedData": {
"key1": "value1",
"key2": "value2"
}
}

View File

@ -0,0 +1,6 @@
{
"operationalState":"DISABLED",
"userDefinedData":{
"abc":"xyz"
}
}

View File

@ -247,3 +247,61 @@ Request Parameters
- addressInformation: addressInformation
- userName: userName
- password: password
Update VNF Package Information
==============================
.. rest_method:: PATCH /vnfpkgm/v1/vnf_packages/{vnf_package_id}
Updates the information of a VNF package.
Response Codes
--------------
.. rest_status_code:: success status.yaml
- 200
.. rest_status_code:: error status.yaml
- 400
- 401
- 403
- 404
- 409
Request Parameters
------------------
.. rest_parameters:: parameters.yaml
- vnf_package_id: vnf_package_id_path
- operationalState: update_operational_state
- userDefinedData: update_user_defined_data
.. note::
At least one of the "operationalState" or "userDefinedData" parameters
shall be present. If the VNF package is not on-boarded, the operation is
used only to update existing or add additional user defined data using the
"userDefinedData" attribute. If user passes existing user defined data
with exact same key/values pairs, then it would return 400 error.
Request Example
---------------
.. literalinclude:: samples/vnf_packages/vnf-packages-patch-request.json
:language: javascript
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- operationalState: updated_operational_state
- userDefinedData: updated_user_defined_data
Response Example
----------------
.. literalinclude:: samples/vnf_packages/vnf-packages-patch-response.json
:language: javascript

View File

@ -19,6 +19,7 @@ Schema for vnf packages create API.
"""
from tacker.api.validation import parameter_types
from tacker.objects.fields import PackageOperationalStateType
create = {
'type': 'object',
@ -48,3 +49,21 @@ upload_from_uri = {
'required': ['addressInformation'],
'additionalProperties': False,
}
"""
Schema for vnf packages update API.
"""
patch = {
'type': 'object',
'properties': {
'operationalState': {
'type': 'string',
'enum': list(PackageOperationalStateType.ALL),
},
'userDefinedData': parameter_types.keyvalue_pairs,
},
'anyOf': [{'required': ['operationalState']},
{'required': ['userDefinedData']}],
'additionalProperties': False
}

View File

@ -92,6 +92,19 @@ class ViewBuilder(object):
return vnf_package_response
def _get_modified_user_data(self, old_user_data, new_user_data):
# Checking for the new keys
user_data_response = {k: new_user_data[k] for k
in set(new_user_data) - set(old_user_data)}
# Checking for updation in values of existing keys
for old_key, old_value in old_user_data.items():
if old_key in new_user_data.keys() and \
new_user_data[old_key] != old_user_data[old_key]:
user_data_response[old_key] = new_user_data[old_key]
return user_data_response
def create(self, request, vnf_package):
return self._get_vnf_package(vnf_package)
@ -103,3 +116,14 @@ class ViewBuilder(object):
def index(self, request, vnf_packages):
return {'vnf_packages': [self._get_vnf_package(
vnf_package) for vnf_package in vnf_packages]}
def patch(self, vnf_package, new_vnf_package):
response = {}
if vnf_package.operational_state != new_vnf_package.operational_state:
response['operationalState'] = new_vnf_package.operational_state
if vnf_package.user_data != new_vnf_package.user_data:
updated_user_data = self._get_modified_user_data(
vnf_package.user_data, new_vnf_package.user_data)
response['userDefinedData'] = updated_user_data
return response

View File

@ -48,6 +48,20 @@ class VnfPkgmController(wsgi.Controller):
self.rpc_api = vnf_pkgm_rpc.VNFPackageRPCAPI()
glance_store.initialize_glance_store()
def _get_vnf_package(self, id, request):
# check if id is of type uuid format
if not uuidutils.is_uuid_like(id):
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
try:
vnf_package = vnf_package_obj.VnfPackage.get_by_id(
request.context, id)
except exceptions.VnfPackageNotFound:
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
return vnf_package
@wsgi.response(http_client.CREATED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
@validation.schema(vnf_packages.create)
@ -108,17 +122,7 @@ class VnfPkgmController(wsgi.Controller):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'delete')
# check if id is of type uuid format
if not uuidutils.is_uuid_like(id):
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
try:
vnf_package = vnf_package_obj.VnfPackage.get_by_id(
request.context, id)
except exceptions.VnfPackageNotFound:
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
vnf_package = self._get_vnf_package(id, request)
if (vnf_package.operational_state ==
fields.PackageOperationalStateType.ENABLED or
@ -143,17 +147,7 @@ class VnfPkgmController(wsgi.Controller):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'upload_package_content')
# check if id is of type uuid format
if not uuidutils.is_uuid_like(id):
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
try:
vnf_package = vnf_package_obj.VnfPackage.get_by_id(
request.context, id)
except exceptions.VnfPackageNotFound:
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
vnf_package = self._get_vnf_package(id, request)
if vnf_package.onboarding_state != \
fields.PackageOnboardingStateType.CREATED:
@ -197,10 +191,7 @@ class VnfPkgmController(wsgi.Controller):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'upload_from_uri')
# check if id is of type uuid format
if not uuidutils.is_uuid_like(id):
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
vnf_package = self._get_vnf_package(id, request)
url = body['addressInformation']
try:
@ -213,13 +204,6 @@ class VnfPkgmController(wsgi.Controller):
if hasattr(data_iter, 'close'):
data_iter.close()
try:
vnf_package = vnf_package_obj.VnfPackage.get_by_id(
request.context, id)
except exceptions.VnfPackageNotFound:
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
if vnf_package.onboarding_state != \
fields.PackageOnboardingStateType.CREATED:
msg = _("VNF Package %(id)s onboarding state is not "
@ -238,6 +222,59 @@ class VnfPkgmController(wsgi.Controller):
user_name=body.get('userName'),
password=body.get('password'))
@wsgi.response(http_client.OK)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
http_client.NOT_FOUND, http_client.CONFLICT))
@validation.schema(vnf_packages.patch)
def patch(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'patch')
old_vnf_package = self._get_vnf_package(id, request)
vnf_package = old_vnf_package.obj_clone()
user_data = body.get('userDefinedData')
operational_state = body.get('operationalState')
if operational_state:
if vnf_package.onboarding_state == \
fields.PackageOnboardingStateType.ONBOARDED:
if vnf_package.operational_state == operational_state:
msg = _("VNF Package %(id)s is already in "
"%(operationState)s operational state") % {
"id": id,
"operationState": vnf_package.operational_state}
raise webob.exc.HTTPConflict(explanation=msg)
else:
# update vnf_package operational state,
# if vnf_package Onboarding State is ONBOARDED
vnf_package.operational_state = operational_state
else:
if not user_data:
msg = _("Updating operational state is not allowed for VNF"
" Package %(id)s when onboarding state is not "
"%(onboarded)s")
raise webob.exc.HTTPBadRequest(
explanation=msg % {"id": id, "onboarded": fields.
PackageOnboardingStateType.ONBOARDED})
# update user data
if user_data:
for key, value in list(user_data.items()):
if vnf_package.user_data.get(key) == value:
del user_data[key]
if not user_data:
msg = _("The userDefinedData provided in update request is as"
" the existing userDefinedData of vnf package %(id)s."
" Nothing to update.")
raise webob.exc.HTTPConflict(
explanation=msg % {"id": id})
vnf_package.user_data = user_data
vnf_package.save()
return self._view_builder.patch(old_vnf_package, vnf_package)
def create_resource():
body_deserializers = {

View File

@ -58,7 +58,8 @@ class VnfpkgmAPIRouter(wsgi.Router):
methods, controller, default_resource)
# Allowed methods on /vnf_packages/{id} resource
methods = {"DELETE": "delete", "GET": "show"}
methods = {"DELETE": "delete", "GET": "show",
"PATCH": "patch"}
self._setup_route(mapper, "/vnf_packages/{id}",
methods, controller, default_resource)

View File

@ -249,3 +249,8 @@ class LimitExceeded(TackerException):
self.retry_after = (int(kwargs['retry']) if kwargs.get('retry')
else None)
super(LimitExceeded, self).__init__(*args, **kwargs)
class UserDataUpdateCreateFailed(TackerException):
msg_fmt = _("User data for VNF package %(id)s cannot be updated "
"or created after %(retries)d retries.")

View File

@ -1 +1 @@
9d425296f2c3
abbef484b34c

View File

@ -0,0 +1,40 @@
# Copyright 2019 OpenStack Foundation
#
# 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.
#
"""modify_unique_constraint_on_vnf_packages_user_data
Revision ID: abbef484b34c
Revises: 9d425296f2c3
Create Date: 2019-11-18 19:34:26.853715
"""
# revision identifiers, used by Alembic.
revision = 'abbef484b34c'
down_revision = '9d425296f2c3'
from alembic import op
def upgrade(active_plugins=None, options=None):
op.drop_constraint(
constraint_name='uniq_vnf_packages_user_data0idid0key0deleted',
table_name='vnf_packages_user_data',
type_='unique')
op.create_unique_constraint(
constraint_name='uniq_vnf_packages_user_data0package_uuid0key0deleted',
table_name='vnf_packages_user_data',
columns=['package_uuid', 'key', 'deleted'])

View File

@ -12,7 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_db import exception as db_exc
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import timeutils
from oslo_utils import uuidutils
from oslo_versionedobjects import base as ovoo_base
@ -37,17 +39,69 @@ LOG = logging.getLogger(__name__)
def _add_user_defined_data(context, package_uuid, user_data,
max_retries=10):
for attempt in range(max_retries):
with db_api.context_manager.writer.using(context):
try:
with db_api.context_manager.writer.using(context):
new_entries = []
for key, value in user_data.items():
new_entries.append({"key": key,
"value": value,
"package_uuid": package_uuid})
if new_entries:
context.session.execute(
models.VnfPackageUserData.__table__.insert(None),
new_entries)
new_entries = []
for key, value in user_data.items():
new_entries.append({"key": key,
"value": value,
"package_uuid": package_uuid})
if new_entries:
context.session.execute(
models.VnfPackageUserData.__table__.insert(None),
new_entries)
return user_data
except db_exc.DBDuplicateEntry:
# a concurrent transaction has been committed,
# try again unless this was the last attempt
with excutils.save_and_reraise_exception() as context:
if attempt < max_retries - 1:
context.reraise = False
else:
raise exceptions.UserDataUpdateCreateFailed(
id=package_uuid, retries=max_retries)
def _vnf_package_user_data_get_query(context, package_uuid, model):
return api.model_query(context, model, read_deleted="no", project_only=True).\
filter_by(package_uuid=package_uuid)
@db_api.context_manager.writer
def _update_user_defined_data(context, package_uuid, user_data):
model = models.VnfPackageUserData
user_data = user_data.copy()
session = context.session
with session.begin(subtransactions=True):
# Get existing user_data
db_user_data = _vnf_package_user_data_get_query(context, package_uuid,
model).all()
save = []
skip = []
# We only want to send changed user_data.
for row in db_user_data:
if row.key in user_data:
value = user_data.pop(row.key)
if row.value != value:
# ORM objects will not be saved until we do the bulk save
row.value = value
save.append(row)
continue
skip.append(row)
# We also want to save non-existent user_data
save.extend(model(key=key, value=value, package_uuid=package_uuid)
for key, value in user_data.items())
# Do a bulk save
if save:
session.bulk_save_objects(save, update_changed_only=True)
# Construct result dictionary with current user_data
save.extend(skip)
result = {row['key']: row['value'] for row in save}
return result
@db_api.context_manager.reader
@ -111,13 +165,21 @@ def _vnf_package_list_by_filters(context, read_deleted=None, **filters):
@db_api.context_manager.writer
def _vnf_package_update(context, package_uuid, values, columns_to_join=None):
vnf_package = _vnf_package_get_by_id(context, package_uuid,
columns_to_join=columns_to_join)
vnf_package.update(values)
def _update_vnf_package_except_user_data(context, vnf_package):
vnf_package.save(session=context.session)
def _vnf_package_update(context, package_uuid, values, columns_to_join=None):
user_data = values.pop('user_data', None)
if user_data:
_update_user_defined_data(context, package_uuid, user_data)
vnf_package = _vnf_package_get_by_id(
context, package_uuid, columns_to_join=columns_to_join)
if values:
vnf_package.update(values)
_update_vnf_package_except_user_data(context, vnf_package)
return vnf_package

View File

@ -84,6 +84,17 @@ rules = [
'upload_from_uri'
}
]),
policy.DocumentedRuleDefault(
name=VNFPKGM % 'patch',
check_str=base.RULE_ADMIN_OR_OWNER,
description="update information of vnf package.",
operations=[
{
'method': 'PATCH',
'path': '/vnf_packages/{vnf_package_id}'
}
]),
]

View File

@ -119,7 +119,7 @@ class VnfPackageTest(base.BaseTackerTest):
body = jsonutils.dumps({"userDefinedData": {"foo": "bar"}})
vnf_package = self._create_vnf_package(body)
file_path = self._get_csar_file_path("sample_vnf_package_csar.zip")
with open(file_path, 'r') as file_object:
with open(file_path, 'rb') as file_object:
resp, resp_body = self.http_client.do_request(
'{base_path}/{id}/package_content'.format(
id=vnf_package['id'],
@ -132,3 +132,40 @@ class VnfPackageTest(base.BaseTackerTest):
self._delete_vnf_package(vnf_package['id'])
self._wait_for_delete(vnf_package['id'])
def test_patch_in_onboarded_state(self):
user_data = jsonutils.dumps(
{"userDefinedData": {"key1": "val1", "key2": "val2",
"key3": "val3"}})
vnf_package = self._create_vnf_package(user_data)
update_req_body = jsonutils.dumps(
{"operationalState": "DISABLED",
"userDefinedData": {"key1": "changed_val1",
"key2": "val2", "new_key": "new_val"}})
expected_result = {"operationalState": "DISABLED",
"userDefinedData": {
"key1": "changed_val1", "new_key": "new_val"}}
file_path = self._get_csar_file_path("sample_vnf_package_csar.zip")
with open(file_path, 'rb') as file_object:
resp, resp_body = self.http_client.do_request(
'{base_path}/{id}/package_content'.format(
id=vnf_package['id'],
base_path=self.base_url),
"PUT", body=file_object, content_type='application/zip')
self.assertEqual(202, resp.status_code)
self._wait_for_onboard(vnf_package['id'])
# Update vnf package which is onboarded
resp, resp_body = self.http_client.do_request(
'{base_path}/{id}'.format(id=vnf_package['id'],
base_path=self.base_url),
"PATCH", content_type='application/json', body=update_req_body)
self.assertEqual(200, resp.status_code)
self.assertEqual(expected_result, resp_body)
self._delete_vnf_package(vnf_package['id'])
self._wait_for_delete(vnf_package['id'])

View File

@ -49,9 +49,10 @@ class TestVnfPackage(SqlTestCase):
vnf_package_db = models.VnfPackage()
vnf_package_db.update(fakes.fake_vnf_package())
vnf_package_db.save(self.context.session)
expected_result = {'abc': 'xyz'}
result = vnf_package._add_user_defined_data(
self.context, vnf_package_db.id, vnf_package_db.user_data)
self.assertEqual(None, result)
self.assertEqual(expected_result, result)
def test_vnf_package_get_by_id(self):
result = vnf_package._vnf_package_get_by_id(
@ -75,7 +76,7 @@ class TestVnfPackage(SqlTestCase):
update = {'user_data': {'test': 'xyz'}}
result = vnf_package._vnf_package_update(
self.context, self.vnf_package.id, update)
self.assertEqual({'test': 'xyz'}, result.user_data)
self.assertEqual({'test': 'xyz', 'abc': 'xyz'}, result.metadetails)
def test_destroy_vnf_package(self):
vnf_package._destroy_vnf_package(self.context,
@ -92,3 +93,27 @@ class TestVnfPackage(SqlTestCase):
self.context, vnf_pack_list_obj, response, None)
self.assertIsInstance(result, objects.VnfPackagesList)
self.assertTrue(result.objects[0].id)
def test_patch_user_data_existing_key_with_same_value(self):
update = {'user_data': {'abc': 'xyz'}}
result = vnf_package._vnf_package_update(
self.context, self.vnf_package.id, update)
self.assertEqual({'abc': 'xyz'}, result.metadetails)
def test_patch_user_data_existing_key_new_value(self):
update = {'user_data': {'abc': 'val1'}}
result = vnf_package._vnf_package_update(
self.context, self.vnf_package.id, update)
self.assertEqual({'abc': 'val1'}, result.metadetails)
def test_patch_user_data_with_new_key_value(self):
update = {'user_data': {'test': '123'}}
result = vnf_package._vnf_package_update(
self.context, self.vnf_package.id, update)
self.assertEqual({'test': '123', 'abc': 'xyz'}, result.metadetails)
def test_patch_user_data_add_key_value_and_modify_value(self):
update = {'user_data': {'test': 'val1', 'abc': 'val2'}}
result = vnf_package._vnf_package_update(
self.context, self.vnf_package.id, update)
self.assertEqual({'test': 'val1', 'abc': 'val2'}, result.metadetails)

View File

@ -97,16 +97,43 @@ class InjectContext(wsgi.Middleware):
return self.application
def return_vnf_package():
model_obj = models.VnfPackage()
model_obj.update(fake_vnf_package())
def fake_vnf_package_user_data(**updates):
vnf_package_user_data = {
'key': 'key',
'value': 'value',
'package_uuid': constants.UUID,
'id': constants.UUID,
}
if updates:
vnf_package_user_data.update(updates)
return vnf_package_user_data
def return_vnf_package_user_data(**updates):
model_obj = models.VnfPackageUserData()
model_obj.update(fake_vnf_package_user_data(**updates))
return model_obj
def return_vnfpkg_obj():
def return_vnf_package(**updates):
model_obj = models.VnfPackage()
if 'user_data' in updates:
metadata = []
for key, value in updates.pop('user_data').items():
vnf_package_user_data = return_vnf_package_user_data(
**{'key': key, 'value': value})
metadata.extend([vnf_package_user_data])
model_obj._metadata = metadata
model_obj.update(fake_vnf_package(**updates))
return model_obj
def return_vnfpkg_obj(**updates):
vnf_package = vnf_package_obj.VnfPackage._from_db_object(
context, vnf_package_obj.VnfPackage(),
return_vnf_package(), expected_attrs=None)
return_vnf_package(**updates), expected_attrs=None)
return vnf_package

View File

@ -158,7 +158,6 @@ class TestController(base.TestCase):
file_path = "tacker/tests/etc/samples/test_data.zip"
file_obj = open(file_path, "rb")
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj()
mock_vnf_pack_save.return_value = fakes.return_vnfpkg_obj()
mock_glance_store.return_value = 'location', 'size', 'checksum',\
'multihash', 'loc_meta'
req = fake_request.HTTPRequest.blank(
@ -224,7 +223,6 @@ class TestController(base.TestCase):
mock_url_open):
body = {"addressInformation": "http://test_data.zip"}
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj()
mock_vnf_pack_save.return_value = fakes.return_vnfpkg_obj()
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s/package_content/upload_from_uri'
% constants.UUID)
@ -274,7 +272,10 @@ class TestController(base.TestCase):
self.controller.upload_vnf_package_from_uri,
req, constants.UUID, body=body)
def test_upload_vnf_package_from_uri_with_invalid_url(self):
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
def test_upload_vnf_package_from_uri_with_invalid_url(
self, mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj()
body = {"addressInformation": "http://test_data.zip"}
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s/package_content/upload_from_uri'
@ -282,3 +283,133 @@ class TestController(base.TestCase):
self.assertRaises(exc.HTTPBadRequest,
self.controller.upload_vnf_package_from_uri,
req, constants.UUID, body=body)
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(vnf_package.VnfPackage, "save")
def test_patch(self, mock_save, mock_vnf_by_id):
update_onboarding_state = {'onboarding_state': 'ONBOARDED'}
mock_vnf_by_id.return_value = \
fakes.return_vnfpkg_obj(**update_onboarding_state)
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)
resp = req.get_response(self.app)
self.assertEqual(http_client.OK, resp.status_code)
self.assertEqual(req_body, jsonutils.loads(resp.body))
def test_patch_with_empty_body(self):
body = {}
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(body)
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
def test_patch_with_invalid_operational_state(self):
body = {"operationalState": "DISABLE"}
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(body)
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(vnf_package.VnfPackage, "save")
def test_patch_update_existing_user_data(self, mock_save, mock_vnf_by_id):
fake_obj = fakes.return_vnfpkg_obj(
**{'user_data': {"testKey1": "val01", "testKey2": "val02",
"testKey3": "val03"}})
mock_vnf_by_id.return_value = fake_obj
req_body = {"userDefinedData": {"testKey1": "changed_val01",
"testKey2": "changed_val02",
"testKey3": "changed_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)
resp = req.get_response(self.app)
self.assertEqual(http_client.OK, resp.status_code)
self.assertEqual(req_body, jsonutils.loads(resp.body))
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(vnf_package.VnfPackage, "save")
def test_patch_failed_with_same_user_data(self, mock_save,
mock_vnf_by_id):
body = {"userDefinedData": {"testKey1": "val01",
"testKey2": "val02", "testkey3": "val03"}}
fake_obj = fakes.return_vnfpkg_obj(
**{'user_data': body["userDefinedData"]})
mock_vnf_by_id.return_value = fake_obj
req = fake_request.HTTPRequest.blank('/vnf_packages/%s'
% constants.UUID)
self.assertRaises(exc.HTTPConflict,
self.controller.patch,
req, constants.UUID, body=body)
def test_patch_with_invalid_uuid(self):
body = {"operationalState": "ENABLED"}
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s'
% constants.INVALID_UUID)
exception = self.assertRaises(exc.HTTPNotFound,
self.controller.patch,
req, constants.INVALID_UUID, body=body)
self.assertEqual(
"Can not find requested vnf package: %s" % constants.INVALID_UUID,
exception.explanation)
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
def test_patch_with_non_existing_vnf_package(self, mock_vnf_by_id):
body = {"operationalState": "ENABLED"}
msg = _("Can not find requested vnf package: %s") % constants.UUID
mock_vnf_by_id.side_effect = exc.HTTPNotFound(explanation=msg)
req = fake_request.HTTPRequest.blank(
'/vnf_packages/%s' % constants.UUID)
exception = self.assertRaises(
exc.HTTPNotFound, self.controller.patch,
req, constants.UUID, body=body)
self.assertEqual(
"Can not find requested vnf package: %s" % constants.UUID,
exception.explanation)
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
def test_patch_failed_with_same_operational_state(self, mock_vnf_by_id):
update_operational_state = {'onboarding_state': 'ONBOARDED'}
vnf_obj = fakes.return_vnfpkg_obj(**update_operational_state)
mock_vnf_by_id.return_value = vnf_obj
body = {"operationalState": "DISABLED",
"userDefinedData": {"testKey1": "val01",
"testKey2": "val02", "testkey3": "val03"}}
req = fake_request.HTTPRequest.blank('/vnf_packages/%s'
% constants.UUID)
self.assertRaises(exc.HTTPConflict,
self.controller.patch,
req, constants.UUID, body=body)
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
def test_patch_not_in_onboarded_state(self, mock_vnf_by_id):
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj()
body = {"operationalState": "DISABLED"}
req = fake_request.HTTPRequest.blank('/vnf_packages/%s'
% constants.UUID)
self.assertRaises(exc.HTTPBadRequest,
self.controller.patch,
req, constants.UUID, body=body)