Enhancement of get VNF LCM operation occurrence
- Added support for getting Individual VNF LCM operation occurrence by its ID - The query information is enhanced by improving filtering expressions and operators - Attributes are also added including (but not limited to): * grant_id * _links * >retry * >fail * changedExtConnectivity - This patch has 2 BP features (Get VNFM LCM Operation Occurence and attributes of Change External Connectivity are also added). Since both features are related, they are merged to one patch. This is why this patch have 2 BPs. - Filtering for the following attributes: operationParams, error, resourceChanges and changedInfo is only limited to the parent attribute. Currently, child attributes/nested attributes are not searchable. Implements: blueprint support-fundamental-lcm Implements: blueprint support-change-external-connectivity Spec: https://specs.openstack.org/openstack/tacker-specs/specs/wallaby/support-fundamental-vnf-lcm-based-on-ETSI-NFV.html Spec: https://specs.openstack.org/openstack/tacker-specs/specs/wallaby/support-change-external-VNF-connectivity-operation.html Change-Id: Ie9b07c203807d08857be65298d9128b026a8fd37
This commit is contained in:
parent
3717b4ee1a
commit
87ea725784
@ -0,0 +1,82 @@
|
||||
[
|
||||
{
|
||||
"id": "d85c6ae4-af16-42c0-96fc-82f7c014c468",
|
||||
"operationState": "COMPLETED",
|
||||
"stateEnteredTime": "2020-08-02T06:50:50.883373",
|
||||
"startTime": "2020-08-02T06:41:34.883483",
|
||||
"vnfInstanceId": "0b7b95a9-21d5-4ac4-80c8-9ae9f7323787",
|
||||
"grantId": "3432cebe-db0a-11e8-9023-005056317abe",
|
||||
"operation": "INSTANTIATE",
|
||||
"isAutomaticInvocation": false,
|
||||
"operationParams": "{
|
||||
'flavourId': 'default',
|
||||
'instantiationLevelId': 'vnf-min',
|
||||
}",
|
||||
"isCancelPending": false,
|
||||
"resourceChanges": {
|
||||
"affectedVnfcs": [
|
||||
{
|
||||
"id": "36e24439-829c-4803-a413-385cd658d544",
|
||||
"vduId": "VDU",
|
||||
"changeType": "ADDED",
|
||||
"computeResource": {
|
||||
"vimConnectionId": "f26f181d-7891-4720-b022-b074ec1733ef",
|
||||
"resourceId": "e0510ba9-3a53-4fcf-9dcc-58dea5c048b0",
|
||||
"vimLevelResourceType": "OS::Nova::Server",
|
||||
},
|
||||
"affectedVnfcCpIds": [
|
||||
"VDU1_CP0",
|
||||
"VDU1_CP1"
|
||||
],
|
||||
"addedStorageResourceIds": [
|
||||
"81ae44f6-b65b-47aa-a578-e53b7a50a574"
|
||||
]
|
||||
}
|
||||
],
|
||||
"affectedVirtualLinks": [
|
||||
{
|
||||
"id": "9836f7f2-5af4-4df5-a89f-933479448ef7",
|
||||
"vnfVirtualLinkDescId": "internalNW",
|
||||
"changeType": "ADDED",
|
||||
"networkResource": {
|
||||
"vimConnectionId": "f26f181d-7891-4720-b022-b074ec1733ef",
|
||||
"resourceId": "400692e5-b2db-478e-acb1-b77a92635ec6",
|
||||
"vimLevelResourceType": "OS::Neutron::Net"
|
||||
}
|
||||
}
|
||||
],
|
||||
"affectedVirtualStorages": [
|
||||
{
|
||||
"id": "81ae44f6-b65b-47aa-a578-e53b7a50a574",
|
||||
"virtualStorageDescId": "Storage",
|
||||
"changeType": "ADDED",
|
||||
"storageResource": {
|
||||
"vimConnectionId": "f26f181d-7891-4720-b022-b074ec1733ef",
|
||||
"resourceId": "842f527e-0092-4f11-aede-f981ba4fd884",
|
||||
"vimLevelResourceType": "OS::Cinder::Volume"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "http://sample.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468"
|
||||
},
|
||||
"vnfInstance": {
|
||||
"href": "http://sample.com/vnflcm/v1/vnf_instances/0b7b95a9-21d5-4ac4-80c8-9ae9f7323787"
|
||||
},
|
||||
"grant":{
|
||||
"href":"http://sample.com/grant/v1/grants/3432cebe-db0a-11e8-9023-005056317abe"
|
||||
},
|
||||
"retry":{
|
||||
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/retry"
|
||||
},
|
||||
"rollback":{
|
||||
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/rollback"
|
||||
},
|
||||
"fail":{
|
||||
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/fail"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
@ -4,6 +4,7 @@
|
||||
"stateEnteredTime": "2020-08-02T06:50:50.883373",
|
||||
"startTime": "2020-08-02T06:41:34.883483",
|
||||
"vnfInstanceId": "0b7b95a9-21d5-4ac4-80c8-9ae9f7323787",
|
||||
"grantId": "3432cebe-db0a-11e8-9023-005056317abe",
|
||||
"operation": "INSTANTIATE",
|
||||
"isAutomaticInvocation": false,
|
||||
"operationParams": "{
|
||||
@ -65,6 +66,15 @@
|
||||
},
|
||||
"grant": {
|
||||
"href": "/grant/v1/grants/3432cebe-db0a-11e8-9023-005056317abe"
|
||||
},
|
||||
"retry":{
|
||||
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/retry"
|
||||
},
|
||||
"rollback":{
|
||||
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/rollback"
|
||||
},
|
||||
"fail":{
|
||||
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/fail"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -712,6 +712,116 @@ Response Parameters
|
||||
- stateEnteredTime: state_entered_time
|
||||
- startTime: start_time
|
||||
- vnfInstanceId: vnf_lcm_vnf_instance_id
|
||||
- grantId: grant_id
|
||||
- operation: operation
|
||||
- isAutomaticInvocation: is_automatic_invocation
|
||||
- operationParams: operation_params
|
||||
- isCancelPending: is_cancel_pending
|
||||
- error: error
|
||||
- title: error_title
|
||||
- status: error_status
|
||||
- detail: error_detail
|
||||
- resourceChanges: resource_changes
|
||||
- affectedVnfcs: affected_vnfcs
|
||||
- id: affected_vnfcs_id
|
||||
- vduId: affected_vnfcs_vdu_id
|
||||
- changeType: affected_vnfcs_change_type
|
||||
- computeResource: vnfc_resource_info_compute_resource
|
||||
- vimConnectionId: vim_connection_id
|
||||
- resourceId: resource_handle_resource_id
|
||||
- vimLevelResourceType: resource_handle_vim_level_resource_type
|
||||
- affectedVnfcCpIds: affected_vnfc_cp_ids
|
||||
- addedStorageResourceIds: added_storage_resource_ids
|
||||
- removedStorageResourceIds: removed_storage_resource_ids
|
||||
- affectedVirtualLinks: affected_virtual_links
|
||||
- id: affected_virtual_links_id
|
||||
- vnfVirtualLinkDescId: vnf_virtual_link_resource_info_vnf_virtual_link_desc_id
|
||||
- changeType: affected_virtual_links_change_type
|
||||
- networkResource: vnf_virtual_link_resource_info_network_resource
|
||||
- vimConnectionId: vim_connection_id
|
||||
- resourceId: resource_handle_resource_id
|
||||
- vimLevelResourceType: resource_handle_vim_level_resource_type
|
||||
- affectedVirtualStorages: affected_virtual_storages
|
||||
- id: affected_virtual_storages_id
|
||||
- virtualStorageDescId: affected_virtual_storages_virtual_storage_desc_id
|
||||
- changeType: affected_virtual_storages_change_type
|
||||
- storageResource: virtual_storage_resource_info_storage_resource
|
||||
- vimConnectionId: vim_connection_id
|
||||
- resourceId: resource_handle_resource_id
|
||||
- vimLevelResourceType: resource_handle_vim_level_resource_type
|
||||
- changedInfo: changed_info
|
||||
- vnfInstanceName: changed_info_vnf_instance_name
|
||||
- vnfInstanceDescription: changed_info_vnf_instance_description
|
||||
- metadata: changed_info_metadata
|
||||
- vimConnectionInfo: changed_info_vim_connection_info
|
||||
- id: vim_connection_info_id
|
||||
- vimId: vim_connection_info_vim_id
|
||||
- vimType: vim_connection_info_vim_type
|
||||
- interfaceInfo: vim_connection_info_interface_info
|
||||
- endpoint: vim_connection_info_interface_info_endpoint
|
||||
- accessInfo: vim_connection_info_access_info
|
||||
- username: vim_connection_info_access_info_username
|
||||
- region: vim_connection_info_access_info_region
|
||||
- password: vim_connection_info_access_info_password
|
||||
- tenant: vim_connection_info_access_info_tenant
|
||||
- vnfPkgId: changed_info_vnf_pkg_id
|
||||
- vnfdId: changed_info_vnfd_id
|
||||
- vnfProvider: changed_info_vnf_provider
|
||||
- vnfProductName: changed_info_vnf_product_name
|
||||
- vnfSotwareVersion: changed_info_vnf_sotware_version
|
||||
- vnfdVersion: changed_info_vnfd_version
|
||||
- changedExtConnectivity: changed_ext_connectivity
|
||||
- id: changed_ext_connectivity_id
|
||||
- resourceHandle: resource_handle
|
||||
- vimConnectionId: vim_connection_id
|
||||
- resourceId: resource_handle_resource_id
|
||||
- vimLevelResourceType: resource_handle_vim_level_resource_type
|
||||
- extLinkPorts: ext_link_ports
|
||||
- id: ext_link_port_id
|
||||
- resourceHandle: resource_handle
|
||||
- vimConnectionId: vim_connection_id
|
||||
- resourceId: resource_handle_resource_id
|
||||
- vimLevelResourceType: resource_handle_vim_level_resource_type
|
||||
- cpInstanceId: cp_instance_id
|
||||
- _links: vnf_instance_links
|
||||
|
||||
Response Example
|
||||
----------------
|
||||
|
||||
.. literalinclude:: samples/vnflcm/show-vnflcm-operation-occurrence-response.json
|
||||
:language: javascript
|
||||
|
||||
List VNF LCM operation occurrence
|
||||
=================================
|
||||
|
||||
.. rest_method:: GET /vnflcm/v1/vnf_lcm_op_occs
|
||||
|
||||
The API consumer can use this method to query status information about multiple VNF lifecycle management operation
|
||||
occurrences.
|
||||
|
||||
Response Codes
|
||||
--------------
|
||||
|
||||
.. rest_status_code:: success status.yaml
|
||||
|
||||
- 200
|
||||
|
||||
.. rest_status_code:: error status.yaml
|
||||
|
||||
- 400
|
||||
- 403
|
||||
|
||||
Response Parameters
|
||||
-------------------
|
||||
|
||||
.. rest_parameters:: parameters_vnflcm.yaml
|
||||
|
||||
- id: vnf_lcm_op_occ_id_response
|
||||
- operationState: operation_state
|
||||
- stateEnteredTime: state_entered_time
|
||||
- startTime: start_time
|
||||
- vnfInstanceId: vnf_lcm_vnf_instance_id
|
||||
- grantId: grant_id
|
||||
- operation: operation
|
||||
- isAutomaticInvocation: is_automatic_invocation
|
||||
- operationParams: operation_params
|
||||
@ -774,7 +884,7 @@ Response Parameters
|
||||
Response Example
|
||||
----------------
|
||||
|
||||
.. literalinclude:: samples/vnflcm/show-vnflcm-operation-occurrence-response.json
|
||||
.. literalinclude:: samples/vnflcm/list-vnflcm-operation-occurrence-response.json
|
||||
:language: javascript
|
||||
|
||||
Roll back a VNF lifecycle operation
|
||||
|
@ -163,6 +163,18 @@ def _parse_filter(filter_rule):
|
||||
try:
|
||||
tokens = filter_rule.split(',')
|
||||
filter_type = None
|
||||
|
||||
# TODO(esto-aln): This condition and the lines below will be removed
|
||||
# if JSON will be supported via OR Mapping. Currently this condition
|
||||
# allows support of JSON strings '"{...}"' for filtering
|
||||
if (tokens[2].startswith("'\"{") and
|
||||
tokens[len(tokens) - 1].endswith("}\"'")):
|
||||
tokens[2] = ','.join(tokens[2:])
|
||||
|
||||
# retain first 3 indices and remove the rest
|
||||
# to process as string
|
||||
tokens = tokens[0:3]
|
||||
|
||||
if len(tokens) >= 3:
|
||||
if tokens[0] in _filters.SUPPORTED_OP_ONE:
|
||||
filter_type = 'simple_filter_expr_one'
|
||||
|
@ -89,6 +89,8 @@ class ViewBuilder(base.BaseViewBuilder):
|
||||
|
||||
return vim_connections
|
||||
|
||||
# TODO(esto-aln): This method will be transferred to
|
||||
# tacker/api/views/vnf_lcm_op_occs.py in the future
|
||||
def _get_lcm_op_occs_links(self, vnf_lcm_op_occs):
|
||||
_links = {
|
||||
"self": {
|
||||
@ -101,6 +103,12 @@ class ViewBuilder(base.BaseViewBuilder):
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.vnf_instance_id}
|
||||
},
|
||||
"retry": {
|
||||
"href":
|
||||
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/retry'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.id}
|
||||
},
|
||||
"rollback": {
|
||||
"href":
|
||||
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/rollback'
|
||||
@ -111,6 +119,12 @@ class ViewBuilder(base.BaseViewBuilder):
|
||||
"href": '%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/grant'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.id}
|
||||
},
|
||||
"fail": {
|
||||
"href":
|
||||
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/fail'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.id}
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,11 +143,13 @@ class ViewBuilder(base.BaseViewBuilder):
|
||||
vnf_instance_dict.update(links)
|
||||
return vnf_instance_dict
|
||||
|
||||
# TODO(esto-aln): This method will be transferred to
|
||||
# tacker/api/views/vnf_lcm_op_occs.py in the future
|
||||
def _get_vnf_lcm_op_occs(self, vnf_lcm_op_occs):
|
||||
vnf_lcm_op_occs_dict = vnf_lcm_op_occs.to_dict()
|
||||
vnf_lcm_op_occs_dict.pop('error_point')
|
||||
vnf_lcm_op_occs_dict = utils.convert_snakecase_to_camelcase(
|
||||
vnf_lcm_op_occs_dict)
|
||||
vnf_lcm_op_occs_dict.pop('errorPoint')
|
||||
|
||||
links = self._get_lcm_op_occs_links(vnf_lcm_op_occs)
|
||||
|
||||
@ -257,5 +273,7 @@ class ViewBuilder(base.BaseViewBuilder):
|
||||
def subscription_show(self, vnf_lcm_subscriptions):
|
||||
return self._get_vnf_lcm_subscription(vnf_lcm_subscriptions)
|
||||
|
||||
# TODO(esto-aln): This method will be transferred to
|
||||
# tacker/api/views/vnf_lcm_op_occs.py in the future
|
||||
def show_lcm_op_occs(self, vnf_lcm_op_occs):
|
||||
return self._get_vnf_lcm_op_occs(vnf_lcm_op_occs)
|
||||
|
118
tacker/api/views/vnf_lcm_op_occs.py
Normal file
118
tacker/api/views/vnf_lcm_op_occs.py
Normal file
@ -0,0 +1,118 @@
|
||||
# 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_log import log as logging
|
||||
|
||||
from tacker.api import views as base
|
||||
from tacker.common import utils
|
||||
import tacker.conf
|
||||
from tacker.objects import vnf_lcm_op_occs as _vnf_lcm_op_occs
|
||||
|
||||
CONF = tacker.conf.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ViewBuilder(base.BaseViewBuilder):
|
||||
|
||||
FLATTEN_ATTRIBUTES = _vnf_lcm_op_occs.VnfLcmOpOcc.FLATTEN_ATTRIBUTES
|
||||
COMPLEX_ATTRIBUTES = _vnf_lcm_op_occs.VnfLcmOpOcc.COMPLEX_ATTRIBUTES
|
||||
FLATTEN_COMPLEX_ATTRIBUTES = [key for key in FLATTEN_ATTRIBUTES.keys()
|
||||
if '/' in key]
|
||||
|
||||
def _get_lcm_op_occs_links(self, vnf_lcm_op_occs):
|
||||
_links = {
|
||||
"self": {
|
||||
"href": '%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.id}
|
||||
},
|
||||
"vnfInstance": {
|
||||
"href": '%(endpoint)s/vnflcm/v1/vnf_instances/%(id)s'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.vnf_instance_id}
|
||||
},
|
||||
"retry": {
|
||||
"href":
|
||||
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/retry'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.id}
|
||||
},
|
||||
"rollback": {
|
||||
"href":
|
||||
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/rollback'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.id}
|
||||
},
|
||||
"grant": {
|
||||
"href": '%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/grant'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.id}
|
||||
},
|
||||
"fail": {
|
||||
"href":
|
||||
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/fail'
|
||||
% {"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||
"id": vnf_lcm_op_occs.id}
|
||||
}
|
||||
}
|
||||
|
||||
return {"_links": _links}
|
||||
|
||||
def _get_vnf_lcm_op_occs_list(self, vnf_lcm_op_occs, include_fields=None):
|
||||
vnf_lcm_op_occs_dict = vnf_lcm_op_occs.to_dict(
|
||||
include_fields=include_fields)
|
||||
|
||||
vnf_lcm_op_occs_dict = utils.convert_snakecase_to_camelcase(
|
||||
vnf_lcm_op_occs_dict)
|
||||
vnf_lcm_op_occs_dict.pop('errorPoint', None)
|
||||
|
||||
links = self._get_lcm_op_occs_links(vnf_lcm_op_occs)
|
||||
|
||||
vnf_lcm_op_occs_dict.update(links)
|
||||
return vnf_lcm_op_occs_dict
|
||||
|
||||
def index(self, request, vnf_lcm_op_occs, all_fields=True,
|
||||
exclude_fields=None, fields=None, exclude_default=False):
|
||||
|
||||
# Find out which fields are to be returned in the response.
|
||||
if all_fields:
|
||||
include_fields = set(self.FLATTEN_ATTRIBUTES.keys())
|
||||
if fields:
|
||||
fields = set(fields.split(','))
|
||||
attributes = set(self.COMPLEX_ATTRIBUTES).intersection(fields)
|
||||
for attribute in attributes:
|
||||
add_fields = set([key for key in self.FLATTEN_ATTRIBUTES.
|
||||
keys() if key.startswith(attribute)])
|
||||
fields = fields.union(add_fields)
|
||||
|
||||
include_fields = set(
|
||||
_vnf_lcm_op_occs.VnfLcmOpOcc.SIMPLE_ATTRIBUTES).union(fields)
|
||||
elif exclude_default:
|
||||
include_fields = set(
|
||||
_vnf_lcm_op_occs.VnfLcmOpOcc.SIMPLE_ATTRIBUTES)
|
||||
elif exclude_fields:
|
||||
exclude_fields = set(exclude_fields.split(','))
|
||||
exclude_additional_attributes = set(
|
||||
self.COMPLEX_ATTRIBUTES).intersection(exclude_fields)
|
||||
for attribute in exclude_additional_attributes:
|
||||
fields = set([key for key in self.FLATTEN_ATTRIBUTES.keys()
|
||||
if key.startswith(attribute)])
|
||||
exclude_fields = exclude_fields.union(fields)
|
||||
|
||||
include_fields = set(self.FLATTEN_ATTRIBUTES.keys()) - \
|
||||
exclude_fields
|
||||
|
||||
return [
|
||||
self._get_vnf_lcm_op_occs_list(
|
||||
vnf_lcm_op_occ, include_fields=include_fields)
|
||||
for vnf_lcm_op_occ in vnf_lcm_op_occs]
|
@ -42,6 +42,7 @@ from tacker._i18n import _
|
||||
from tacker.api.schemas import vnf_lcm
|
||||
from tacker.api import validation
|
||||
from tacker.api.views import vnf_lcm as vnf_lcm_view
|
||||
from tacker.api.views import vnf_lcm_op_occs as vnf_op_occs_view
|
||||
from tacker.api.vnflcm.v1 import sync_resource
|
||||
from tacker.common import exceptions
|
||||
from tacker.common import utils
|
||||
@ -52,6 +53,7 @@ from tacker.extensions import vnfm
|
||||
from tacker import manager
|
||||
from tacker import objects
|
||||
from tacker.objects import fields
|
||||
from tacker.objects import vnf_lcm_op_occs as vnf_lcm_op_occs_obj
|
||||
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
|
||||
from tacker.plugins.common import constants
|
||||
from tacker.policies import vnf_lcm as vnf_lcm_policies
|
||||
@ -185,6 +187,7 @@ class VnfLcmController(wsgi.Controller):
|
||||
super(VnfLcmController, self).__init__()
|
||||
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
|
||||
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
|
||||
self._view_builder_op_occ = vnf_op_occs_view.ViewBuilder()
|
||||
|
||||
def _get_vnf_instance_href(self, vnf_instance):
|
||||
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance.id
|
||||
@ -1503,6 +1506,40 @@ class VnfLcmController(wsgi.Controller):
|
||||
return self._make_problem_detail(error_msg,
|
||||
500, title='Internal Server Error')
|
||||
|
||||
@wsgi.response(http_client.OK)
|
||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
||||
def list_lcm_op_occs(self, request):
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_lcm_policies.VNFLCM % 'list_lcm_op_occs')
|
||||
|
||||
all_fields = request.GET.get('all_fields')
|
||||
exclude_default = request.GET.get('exclude_default')
|
||||
fields = request.GET.get('fields')
|
||||
exclude_fields = request.GET.get('exclude_fields')
|
||||
filters = request.GET.get('filter')
|
||||
if not (all_fields or fields or exclude_fields):
|
||||
exclude_default = True
|
||||
|
||||
self._view_builder_op_occ.validate_attribute_fields(
|
||||
all_fields=all_fields, fields=fields,
|
||||
exclude_fields=exclude_fields,
|
||||
exclude_default=exclude_default)
|
||||
|
||||
filters = self._view_builder_op_occ.validate_filter(filters)
|
||||
|
||||
try:
|
||||
vnf_lcm_op_occs = \
|
||||
vnf_lcm_op_occs_obj.VnfLcmOpOccList.get_by_filters(
|
||||
request.context, read_deleted='no', filters=filters)
|
||||
except Exception as e:
|
||||
LOG.exception(traceback.format_exc())
|
||||
return self._make_problem_detail(
|
||||
str(e), 500, title='Internal Server Error')
|
||||
|
||||
return self._view_builder_op_occ.index(request, vnf_lcm_op_occs,
|
||||
all_fields=all_fields, exclude_fields=exclude_fields,
|
||||
fields=fields, exclude_default=exclude_default)
|
||||
|
||||
def _make_problem_detail(
|
||||
self,
|
||||
detail,
|
||||
|
@ -138,3 +138,15 @@ class VnflcmAPIRouter(wsgi.Router):
|
||||
methods = {"GET": "subscription_show", "DELETE": "delete_subscription"}
|
||||
self._setup_route(mapper, "/subscriptions/{subscriptionId}",
|
||||
methods, controller, default_resource)
|
||||
|
||||
# {apiRoot}/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry resource
|
||||
methods = {"POST": "retry"}
|
||||
self._setup_route(mapper,
|
||||
"/vnf_lcm_op_occs/{id}/retry",
|
||||
methods, controller, default_resource)
|
||||
|
||||
# Allowed methods on
|
||||
# {apiRoot}/vnflcm/v1/vnf_lcm_op_occs resource
|
||||
methods = {"GET": "list_lcm_op_occs"}
|
||||
self._setup_route(mapper, "/vnf_lcm_op_occs",
|
||||
methods, controller, default_resource)
|
||||
|
@ -305,6 +305,7 @@ class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,
|
||||
vnf_instance_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('vnf_instances.id'),
|
||||
nullable=False)
|
||||
grant_id = sa.Column(sa.String(36), nullable=True)
|
||||
state_entered_time = sa.Column(sa.DateTime(), nullable=False)
|
||||
start_time = sa.Column(sa.DateTime(), nullable=False)
|
||||
operation_state = sa.Column(sa.String(length=255), nullable=False)
|
||||
@ -315,6 +316,7 @@ class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,
|
||||
error = sa.Column(sa.JSON(), nullable=True)
|
||||
resource_changes = sa.Column(sa.JSON(), nullable=True)
|
||||
changed_info = sa.Column(sa.JSON(), nullable=True)
|
||||
changed_ext_connectivity = sa.Column(sa.JSON(), nullable=True)
|
||||
error_point = sa.Column(sa.Integer, nullable=False)
|
||||
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
# flake8: noqa: E402
|
||||
|
||||
"""add_column_to_vnf_lcm_op_occs
|
||||
|
||||
Revision ID: 3adac34764da
|
||||
Revises: 62d18199909e
|
||||
Create Date: 2021-02-16 16:19:12.100380
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3adac34764da'
|
||||
down_revision = '7186440a306b'
|
||||
|
||||
from alembic import op
|
||||
|
||||
import sqlalchemy as sa
|
||||
from tacker.db import migration
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
op.add_column('vnf_lcm_op_occs',
|
||||
sa.Column('grant_id', sa.VARCHAR(length=36), nullable=True))
|
||||
op.add_column('vnf_lcm_op_occs',
|
||||
sa.Column('changed_ext_connectivity', sa.JSON(), nullable=True))
|
@ -1 +1 @@
|
||||
7186440a306b
|
||||
3adac34764da
|
||||
|
@ -225,8 +225,7 @@ class LcmOccsOperationState(BaseTackerEnum):
|
||||
FAILED_TEMP = 'FAILED_TEMP'
|
||||
FAILED = 'FAILED'
|
||||
|
||||
ALL = (STARTING, PROCESSING, COMPLETED,
|
||||
FAILED_TEMP, FAILED)
|
||||
ALL = (STARTING, PROCESSING, COMPLETED, FAILED_TEMP, FAILED)
|
||||
|
||||
|
||||
class LcmOccsOperationType(BaseTackerEnum):
|
||||
|
@ -15,10 +15,13 @@ from datetime import datetime
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import timeutils
|
||||
from oslo_versionedobjects import base as ovoo_base
|
||||
from sqlalchemy import exc
|
||||
from sqlalchemy.orm import joinedload
|
||||
from sqlalchemy_filters import apply_filters
|
||||
|
||||
from tacker.common import exceptions
|
||||
from tacker.common import utils
|
||||
from tacker.db import api as db_api
|
||||
from tacker.db.db_sqlalchemy import api
|
||||
from tacker.db.db_sqlalchemy import models
|
||||
@ -55,11 +58,34 @@ def _vnf_lcm_op_occ_update(context, values):
|
||||
if values.changed_info:
|
||||
update.update({'changed_info': jsonutils.dumps(
|
||||
values.changed_info.to_dict())})
|
||||
if 'changed_ext_connectivity' in values:
|
||||
if values.changed_ext_connectivity:
|
||||
update.update({'changed_ext_connectivity': jsonutils.dumps(
|
||||
[chg_ext_conn.to_dict() for chg_ext_conn in
|
||||
values.changed_ext_connectivity])})
|
||||
api.model_query(context, models.VnfLcmOpOccs). \
|
||||
filter_by(id=values.id). \
|
||||
update(update, synchronize_session=False)
|
||||
|
||||
|
||||
def _make_vnf_lcm_op_occs_list(context, op_occ_list,
|
||||
db_op_occ_list):
|
||||
lcm_op_occ_class = VnfLcmOpOcc
|
||||
|
||||
op_occ_list.objects = []
|
||||
for db_op_occ in db_op_occ_list:
|
||||
if(db_op_occ['changed_info'] and
|
||||
isinstance(db_op_occ['changed_info'], str)):
|
||||
db_op_occ['changed_info'] = jsonutils.loads(
|
||||
db_op_occ['changed_info'])
|
||||
vnf_lcm_op_occ_obj = lcm_op_occ_class._from_db_object(
|
||||
context, lcm_op_occ_class(context), db_op_occ)
|
||||
op_occ_list.objects.append(vnf_lcm_op_occ_obj)
|
||||
|
||||
op_occ_list.obj_reset_changes()
|
||||
return op_occ_list
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _vnf_lcm_op_occs_get_by_id(context, vnf_lcm_op_occ_id):
|
||||
|
||||
@ -91,6 +117,19 @@ def _vnf_lcm_op_occs_get_by_vnf_instance_id(context, vnf_instance_id):
|
||||
return result
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _vnf_lcm_op_occs_get_by_filters(context, read_deleted=None,
|
||||
filters=None):
|
||||
|
||||
query = api.model_query(context, models.VnfLcmOpOccs,
|
||||
read_deleted=read_deleted, project_only=True)
|
||||
|
||||
if filters:
|
||||
query = apply_filters(query, filters)
|
||||
|
||||
return query.all()
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _vnf_notify_get_by_id(context, vnf_instance_id, columns_to_join=None):
|
||||
|
||||
@ -170,6 +209,7 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
'state_entered_time': fields.DateTimeField(nullable=False),
|
||||
'start_time': fields.DateTimeField(nullable=False),
|
||||
'vnf_instance_id': fields.StringField(nullable=False),
|
||||
'grant_id': fields.StringField(nullable=True),
|
||||
'operation': fields.StringField(nullable=False),
|
||||
'is_automatic_invocation': fields.BooleanField(default=False),
|
||||
'operation_params': fields.StringField(nullable=True),
|
||||
@ -180,9 +220,40 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
'ResourceChanges', nullable=True, default=None),
|
||||
'changed_info': fields.ObjectField(
|
||||
'VnfInfoModifications', nullable=True, default=None),
|
||||
'changed_ext_connectivity': fields.ListOfObjectsField(
|
||||
'ExtVirtualLinkInfo', nullable=True, default=[]),
|
||||
'error_point': fields.IntegerField(nullable=True, default=0)
|
||||
}
|
||||
|
||||
ALL_ATTRIBUTES = {
|
||||
'id': ('id', 'string', 'VnfLcmOpOccs'),
|
||||
'operationState': ('operation_state', 'string', 'VnfLcmOpOccs'),
|
||||
'stateEnteredTime':
|
||||
('state_entered_time', 'datetime', 'VnfLcmOpOccs'),
|
||||
'startTime': ('start_time', 'datetime', 'VnfLcmOpOccs'),
|
||||
'vnfInstanceId': ('vnf_instance_id', 'string', 'VnfLcmOpOccs'),
|
||||
'grantId': ('grant_id', 'string', 'VnfLcmOpOccs'),
|
||||
'operation': ('operation', 'string', 'VnfLcmOpOccs'),
|
||||
'isAutomaticInvocation':
|
||||
('is_automatic_invocation', 'boolean', 'VnfLcmOpOccs'),
|
||||
'isCancelPending': ('is_cancel_pending', 'string', 'VnfLcmOpOccs'),
|
||||
'errorPoint': ('error_point', 'number', 'VnfLcmOpOccs'),
|
||||
'operationParams': ('operation_params', 'string', 'VnfLcmOpOccs'),
|
||||
'error': ('error', 'string', 'VnfLcmOpOccs'),
|
||||
'resourceChanges': ('resource_changes', 'string', 'VnfLcmOpOccs'),
|
||||
'changedInfo': ('changed_info', 'string', 'VnfLcmOpOccs')
|
||||
}
|
||||
|
||||
FLATTEN_ATTRIBUTES = utils.flatten_dict(ALL_ATTRIBUTES.copy())
|
||||
|
||||
SIMPLE_ATTRIBUTES = ['id', 'operationState', 'stateEnteredTime',
|
||||
'startTime', 'vnfInstanceId', 'grantId', 'operation',
|
||||
'isAutomaticInvocation',
|
||||
'isCancelPending', 'errorPoint']
|
||||
|
||||
COMPLEX_ATTRIBUTES = ['error', 'resourceChanges', 'changedInfo',
|
||||
'operationParams', 'changedExtConnectivity']
|
||||
|
||||
@base.remotable
|
||||
def create(self):
|
||||
updates = self.obj_clone()
|
||||
@ -197,7 +268,9 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
def _from_db_object(context, vnf_lcm_op_occ_obj, db_vnf_lcm_op_occ):
|
||||
|
||||
special_fields = ['error',
|
||||
'resource_changes', 'changed_info']
|
||||
'resource_changes',
|
||||
'changed_info',
|
||||
'changed_ext_connectivity']
|
||||
for key in vnf_lcm_op_occ_obj.fields:
|
||||
if key in special_fields:
|
||||
continue
|
||||
@ -214,6 +287,12 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
changed_info = VnfInfoModifications.obj_from_primitive(
|
||||
db_vnf_lcm_op_occ['changed_info'], context)
|
||||
vnf_lcm_op_occ_obj.changed_info = changed_info
|
||||
if db_vnf_lcm_op_occ['changed_ext_connectivity']:
|
||||
changed_ext_conn = \
|
||||
[objects.ExtVirtualLinkInfo.obj_from_primitive(
|
||||
chg_ext_conn, context) for chg_ext_conn in
|
||||
db_vnf_lcm_op_occ['changed_ext_connectivity']]
|
||||
vnf_lcm_op_occ_obj.changed_ext_connectivity = changed_ext_conn
|
||||
|
||||
vnf_lcm_op_occ_obj._context = context
|
||||
vnf_lcm_op_occ_obj.obj_reset_changes()
|
||||
@ -238,6 +317,11 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
obj_data = VnfInfoModifications._from_dict(
|
||||
primitive.get('changed_info'))
|
||||
primitive.update({'changed_info': obj_data})
|
||||
if 'changed_ext_connectivity' in primitive.keys():
|
||||
obj_data = [objects.ExtVirtualLinkInfo.obj_from_primitive(
|
||||
chg_ext_conn, context) for chg_ext_conn in
|
||||
primitive.get('changed_ext_connectivity')]
|
||||
primitive.update({'changed_ext_connectivity': obj_data})
|
||||
vnf_lcm_op_occ = VnfLcmOpOcc._from_dict(primitive)
|
||||
|
||||
return vnf_lcm_op_occ
|
||||
@ -252,6 +336,7 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
state_entered_time = data_dict.get('state_entered_time')
|
||||
start_time = data_dict.get('start_time')
|
||||
vnf_instance_id = data_dict.get('vnf_instance_id')
|
||||
grant_id = data_dict.get('grant_id')
|
||||
operation = data_dict.get('operation')
|
||||
is_automatic_invocation = data_dict.get('is_automatic_invocation')
|
||||
operation_params = data_dict.get('operation_params')
|
||||
@ -259,12 +344,14 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
error = data_dict.get('error')
|
||||
resource_changes = data_dict.get('resource_changes')
|
||||
changed_info = data_dict.get('changed_info')
|
||||
changed_ext_connectivity = data_dict.get('changed_ext_connectivity')
|
||||
error_point = data_dict.get('error_point')
|
||||
|
||||
obj = cls(operation_state=operation_state,
|
||||
state_entered_time=state_entered_time,
|
||||
start_time=start_time,
|
||||
vnf_instance_id=vnf_instance_id,
|
||||
grant_id=grant_id,
|
||||
operation=operation,
|
||||
is_automatic_invocation=is_automatic_invocation,
|
||||
operation_params=operation_params,
|
||||
@ -272,28 +359,76 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
error=error,
|
||||
resource_changes=resource_changes,
|
||||
changed_info=changed_info,
|
||||
changed_ext_connectivity=changed_ext_connectivity,
|
||||
error_point=error_point
|
||||
)
|
||||
|
||||
return obj
|
||||
|
||||
def to_dict(self):
|
||||
data = {'id': self.id,
|
||||
'operation_state': self.operation_state,
|
||||
'state_entered_time': self.state_entered_time,
|
||||
'start_time': self.start_time,
|
||||
'vnf_instance_id': self.vnf_instance_id,
|
||||
'operation': self.operation,
|
||||
'is_automatic_invocation': self.is_automatic_invocation,
|
||||
'operation_params': self.operation_params,
|
||||
'is_cancel_pending': self.is_cancel_pending,
|
||||
'error_point': self.error_point}
|
||||
def _get_error(self, include_fields=None):
|
||||
key = 'error'
|
||||
if key in include_fields:
|
||||
return {key: self.error.to_dict()}
|
||||
|
||||
def _get_resource_changes(self, include_fields=None):
|
||||
key = 'resourceChanges'
|
||||
if key in include_fields:
|
||||
return {key: self.resource_changes.to_dict()}
|
||||
|
||||
def _get_changed_info(self, include_fields=None):
|
||||
key = 'changedInfo'
|
||||
if key in include_fields:
|
||||
return {key: self.changed_info.to_dict()}
|
||||
|
||||
def _get_operation_params(self, include_fields=None):
|
||||
key = 'operationParams'
|
||||
if key in include_fields:
|
||||
return {key: self.operation_params}
|
||||
|
||||
def _get_changed_ext_connectivity(self, include_fields=None):
|
||||
key = 'changedExtConnectivity'
|
||||
return {key: [chg_ext_conn.to_dict() for chg_ext_conn in
|
||||
self.changed_ext_connectivity]}
|
||||
|
||||
def to_dict(self, include_fields=None):
|
||||
data = dict()
|
||||
if not include_fields:
|
||||
include_fields = set(self.FLATTEN_ATTRIBUTES.keys())
|
||||
|
||||
# add simple fields
|
||||
to_fields = set(self.SIMPLE_ATTRIBUTES).intersection(include_fields)
|
||||
for field in to_fields:
|
||||
data[field] = getattr(self, self.FLATTEN_ATTRIBUTES[field][0])
|
||||
|
||||
# add complex attributes
|
||||
if self.error:
|
||||
data.update({'error': self.error.to_dict()})
|
||||
error = self._get_error(include_fields=include_fields)
|
||||
if error:
|
||||
data.update(error)
|
||||
|
||||
if self.resource_changes:
|
||||
data.update({'resource_changes': self.resource_changes.to_dict()})
|
||||
resource_changes = self._get_resource_changes(
|
||||
include_fields=include_fields)
|
||||
if resource_changes:
|
||||
data.update(resource_changes)
|
||||
|
||||
if self.changed_info:
|
||||
data.update({'changed_info': self.changed_info.to_dict()})
|
||||
changed_info = self._get_changed_info(
|
||||
include_fields=include_fields)
|
||||
if changed_info:
|
||||
data.update(changed_info)
|
||||
|
||||
if self.operation_params:
|
||||
operation_params = self._get_operation_params(
|
||||
include_fields=include_fields)
|
||||
if operation_params:
|
||||
data.update(operation_params)
|
||||
|
||||
if self.changed_ext_connectivity:
|
||||
changed_ext_connectivity = self._get_changed_ext_connectivity(
|
||||
include_fields=include_fields)
|
||||
if changed_ext_connectivity:
|
||||
data.update(changed_ext_connectivity)
|
||||
|
||||
return data
|
||||
|
||||
@ -309,6 +444,22 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
|
||||
return cls._from_db_object(context, cls(), db_vnf_lcm_op_occs)
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class VnfLcmOpOccList(ovoo_base.ObjectListBase, base.TackerObject):
|
||||
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('VnfLcmOpOcc')
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_filters(cls, context, read_deleted=None, filters=None):
|
||||
db_vnf_lcm_op_occs = _vnf_lcm_op_occs_get_by_filters(
|
||||
context, read_deleted=read_deleted, filters=filters)
|
||||
return _make_vnf_lcm_op_occs_list(context, cls(), db_vnf_lcm_op_occs)
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class ResourceChanges(base.TackerObject,
|
||||
base.TackerPersistentObject):
|
||||
|
@ -99,6 +99,17 @@ rules = [
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFLCM % 'list_lcm_op_occs',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Query VNF LCM operation occurrence",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/vnflcm/v1/vnf_lcm_op_occs'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFLCM % 'index',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
|
@ -487,6 +487,14 @@ class BaseVnfLcmTest(base.BaseTackerTest):
|
||||
|
||||
return resp, response_body
|
||||
|
||||
def _list_op_occs(self, filter_string=''):
|
||||
show_url = os.path.join(
|
||||
self.base_vnf_lcm_op_occs_url)
|
||||
resp, response_body = self.http_client.do_request(
|
||||
show_url + filter_string, "GET")
|
||||
|
||||
return resp, response_body
|
||||
|
||||
def _wait_terminate_vnf_instance(self, id, timeout=None):
|
||||
start_time = int(time.time())
|
||||
|
||||
|
@ -778,6 +778,8 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Get vnflcmOpOccId to retry.
|
||||
- Retry instantiation operation.
|
||||
- Get opOccs information.
|
||||
- Get opOccs list.
|
||||
- Delete VNF instance.
|
||||
- Delete subscription.
|
||||
"""
|
||||
# Create subscription and register it.
|
||||
@ -855,6 +857,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
|
||||
self._assert_occ_show(resp, op_occs_info)
|
||||
|
||||
# occ-list
|
||||
resp, op_occs_info = self._list_op_occs()
|
||||
self._assert_occ_list(resp, op_occs_info)
|
||||
|
||||
# Delete VNF
|
||||
resp, _ = self._delete_vnf_instance(vnf_instance_id)
|
||||
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
|
||||
@ -877,7 +883,9 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Get vnfcmOpOccId to retry.
|
||||
- Retry Scale-Out operation.
|
||||
- Get opOccs information.
|
||||
- Terminate VNF.
|
||||
- Get opOccs list.
|
||||
- Terminate VNF instance.
|
||||
- Delete VNF instance.
|
||||
- Delete subscription.
|
||||
"""
|
||||
# Create subscription and register it.
|
||||
@ -964,6 +972,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
|
||||
self._assert_occ_show(resp, op_occs_info)
|
||||
|
||||
# occ-list
|
||||
resp, op_occs_info = self._list_op_occs()
|
||||
self._assert_occ_list(resp, op_occs_info)
|
||||
|
||||
# Terminate VNF
|
||||
stack = self._get_heat_stack(vnf_instance_id)
|
||||
resources_list = self._get_heat_resource_list(stack.id)
|
||||
@ -1000,6 +1012,7 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Get vnflcmOpOccId to rollback.
|
||||
- Rollback instantiation operation.
|
||||
- Get opOccs information.
|
||||
- Get opOccs list
|
||||
- Delete subscription.
|
||||
"""
|
||||
# Create subscription and register it.
|
||||
@ -1072,6 +1085,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
|
||||
self._assert_occ_show(resp, op_occs_info)
|
||||
|
||||
# occ-list
|
||||
resp, op_occs_info = self._list_op_occs()
|
||||
self._assert_occ_list(resp, op_occs_info)
|
||||
|
||||
# Delete VNF
|
||||
resp, _ = self._delete_vnf_instance(vnf_instance_id)
|
||||
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
|
||||
@ -1094,6 +1111,7 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Get vnfcmOpOccId to rollback.
|
||||
- Rollback Scale-Out operation.
|
||||
- Get opOccs information.
|
||||
- get opOccs List.
|
||||
- Terminate VNF.
|
||||
- Delete subscription.
|
||||
"""
|
||||
@ -1176,6 +1194,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
|
||||
self._assert_occ_show(resp, op_occs_info)
|
||||
|
||||
# occ-list
|
||||
resp, op_occs_info = self._list_op_occs()
|
||||
self._assert_occ_list(resp, op_occs_info)
|
||||
|
||||
# Terminate VNF
|
||||
stack = self._get_heat_stack(vnf_instance_id)
|
||||
resources_list = self._get_heat_resource_list(stack.id)
|
||||
@ -1283,6 +1305,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
|
||||
self._assert_occ_show(resp, op_occs_info)
|
||||
|
||||
# occ-list
|
||||
resp, op_occs_info = self._list_op_occs()
|
||||
self._assert_occ_list(resp, op_occs_info)
|
||||
|
||||
# Delete Stack
|
||||
stack = self._get_heat_stack(vnf_instance_id)
|
||||
self._delete_heat_stack(stack.id)
|
||||
@ -1391,6 +1417,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
|
||||
self._assert_occ_show(resp, op_occs_info)
|
||||
|
||||
# occ-list
|
||||
resp, op_occs_info = self._list_op_occs()
|
||||
self._assert_occ_list(resp, op_occs_info)
|
||||
|
||||
# Terminate VNF
|
||||
stack = self._get_heat_stack(vnf_instance_id)
|
||||
resources_list = self._get_heat_resource_list(stack.id)
|
||||
@ -1562,3 +1592,24 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
self.assertIsNotNone(_links.get('vnfInstance').get('href'))
|
||||
self.assertIsNotNone(_links.get('grant'))
|
||||
self.assertIsNotNone(_links.get('grant').get('href'))
|
||||
|
||||
def _assert_occ_list(self, resp, op_occs_list):
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# Only check required parameters.
|
||||
for op_occs_info in op_occs_list:
|
||||
self.assertIsNotNone(op_occs_info.get('id'))
|
||||
self.assertIsNotNone(op_occs_info.get('operationState'))
|
||||
self.assertIsNotNone(op_occs_info.get('stateEnteredTime'))
|
||||
self.assertIsNotNone(op_occs_info.get('vnfInstanceId'))
|
||||
self.assertIsNotNone(op_occs_info.get('operation'))
|
||||
self.assertIsNotNone(op_occs_info.get('isAutomaticInvocation'))
|
||||
self.assertIsNotNone(op_occs_info.get('isCancelPending'))
|
||||
|
||||
_links = op_occs_info.get('_links')
|
||||
self.assertIsNotNone(_links.get('self'))
|
||||
self.assertIsNotNone(_links.get('self').get('href'))
|
||||
self.assertIsNotNone(_links.get('vnfInstance'))
|
||||
self.assertIsNotNone(_links.get('vnfInstance').get('href'))
|
||||
self.assertIsNotNone(_links.get('grant'))
|
||||
self.assertIsNotNone(_links.get('grant').get('href'))
|
||||
|
@ -512,3 +512,23 @@ def get_vnf(vnfd_id, vim_id):
|
||||
'placement_attr': "test_placement_attr",
|
||||
'vim_id': vim_id
|
||||
}
|
||||
|
||||
|
||||
def get_changed_ext_conn_data():
|
||||
return [{
|
||||
"id": uuidsentinel.change_ext_conn_id,
|
||||
"resource_handle": {
|
||||
"vim_connection_id": uuidsentinel.vim_connection_id,
|
||||
"resource_id": uuidsentinel.vl_resource_id,
|
||||
"vim_level_resource_type": "OS::Neutron::Net",
|
||||
},
|
||||
"ext_link_ports": [{
|
||||
"id": uuidsentinel.ext_link_ports_id,
|
||||
"resource_handle": {
|
||||
"vim_connection_id": uuidsentinel.vim_connection_id,
|
||||
"resource_id": uuidsentinel.port_resource_id,
|
||||
"vim_level_resource_type": "OS::Neutron::Port",
|
||||
},
|
||||
"cp_instance_id": uuidsentinel.cp_instance_id,
|
||||
}]
|
||||
}]
|
||||
|
@ -90,10 +90,13 @@ class TestVnfLcmOpOcc(SqlTestCase):
|
||||
problem_obj.detail = 'test_err'
|
||||
changed_info = objects.vnf_lcm_op_occs.VnfInfoModifications(
|
||||
context=self.context, **fakes.get_changed_info_data())
|
||||
changed_ext_conn = [objects.ExtVirtualLinkInfo.obj_from_primitive(
|
||||
i, self.context) for i in fakes.get_changed_ext_conn_data()]
|
||||
vnf_lcm_op_occs.operation_state = 'FAILED_TEMP'
|
||||
vnf_lcm_op_occs.error = problem_obj
|
||||
vnf_lcm_op_occs.id = uuidsentinel.vnf_lcm_op_occs_id
|
||||
vnf_lcm_op_occs.changed_info = changed_info
|
||||
vnf_lcm_op_occs.changed_ext_connectivity = changed_ext_conn
|
||||
vnf_lcm_op_occs.save()
|
||||
|
||||
self.assertEqual('FAILED_TEMP', vnf_lcm_op_occs.operation_state)
|
||||
|
@ -1265,20 +1265,28 @@ VNFLCMOPOCC_RESPONSE = {
|
||||
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_instances/'
|
||||
'f26f181d-7891-4720-b022-b074ec1733ef'
|
||||
},
|
||||
"retry": {
|
||||
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_lcm_op_occs/'
|
||||
'f26f181d-7891-4720-b022-b074ec1733ef/retry'
|
||||
},
|
||||
"rollback": {
|
||||
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_lcm_op_occs/'
|
||||
'f26f181d-7891-4720-b022-b074ec1733ef/rollback'
|
||||
},
|
||||
"grant": {
|
||||
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_lcm_op_occs/'
|
||||
'f26f181d-7891-4720-b022-b074ec1733ef/grant'
|
||||
}},
|
||||
'f26f181d-7891-4720-b022-b074ec1733ef/grant',
|
||||
},
|
||||
"fail": {
|
||||
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_lcm_op_occs/'
|
||||
'f26f181d-7891-4720-b022-b074ec1733ef/fail'}},
|
||||
'operationState': 'COMPLETED',
|
||||
'stateEnteredTime': datetime.datetime(1900, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'startTime': datetime.datetime(1900, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'vnfInstanceId': 'f26f181d-7891-4720-b022-b074ec1733ef',
|
||||
'grantId': 'f26f181d-7891-4720-b022-b074ec1733ef',
|
||||
'operation': 'MODIFY_INFO',
|
||||
'isAutomaticInvocation': False,
|
||||
'operationParams': '{"is_reverse": False, "is_auto": False}',
|
||||
@ -1336,7 +1344,24 @@ VNFLCMOPOCC_RESPONSE = {
|
||||
'vnfProductName': 'fake_vnf_product_name',
|
||||
'vnfSoftwareVersion': 'fake_vnf_software_version',
|
||||
'vnfdVersion': 'fake_vnfd_version'
|
||||
}
|
||||
},
|
||||
"changedExtConnectivity": [{
|
||||
"id": constants.UUID,
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": constants.UUID,
|
||||
"resourceId": constants.UUID,
|
||||
"vimLevelResourceType": "OS::Neutron::Net",
|
||||
},
|
||||
"extLinkPorts": [{
|
||||
"id": constants.UUID,
|
||||
"resourceHandle": {
|
||||
"vimConnectionId": constants.UUID,
|
||||
"resourceId": constants.UUID,
|
||||
"vimLevelResourceType": "OS::Neutron::Port",
|
||||
},
|
||||
"cpInstanceId": constants.UUID,
|
||||
}]
|
||||
}]
|
||||
}
|
||||
|
||||
VNFLCMOPOCC_INDEX_RESPONSE = [VNFLCMOPOCC_RESPONSE]
|
||||
@ -1436,6 +1461,28 @@ def fake_vnf_lcm_op_occs():
|
||||
}
|
||||
changed_info_obj = objects.VnfInfoModifications(**changed_info)
|
||||
|
||||
changed_ext_connectivity = [{
|
||||
"id": constants.UUID,
|
||||
"resource_handle": {
|
||||
"vim_connection_id": constants.UUID,
|
||||
"resource_id": constants.UUID,
|
||||
"vim_level_resource_type": "OS::Neutron::Net",
|
||||
},
|
||||
"ext_link_ports": [{
|
||||
"id": constants.UUID,
|
||||
"resource_handle": {
|
||||
"vim_connection_id": constants.UUID,
|
||||
"resource_id": constants.UUID,
|
||||
"vim_level_resource_type": "OS::Neutron::Port",
|
||||
},
|
||||
"cp_instance_id": constants.UUID,
|
||||
}]
|
||||
}]
|
||||
changed_ext_connectivity_obj = \
|
||||
[objects.ExtVirtualLinkInfo.obj_from_primitive(
|
||||
chg_ext_conn, context) for chg_ext_conn in
|
||||
changed_ext_connectivity]
|
||||
|
||||
dt = datetime.datetime(1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
vnf_lcm_op_occs = {
|
||||
'id': constants.UUID,
|
||||
@ -1443,13 +1490,15 @@ def fake_vnf_lcm_op_occs():
|
||||
'state_entered_time': dt,
|
||||
'start_time': dt,
|
||||
'vnf_instance_id': constants.UUID,
|
||||
'grant_id': constants.UUID,
|
||||
'operation': 'MODIFY_INFO',
|
||||
'is_automatic_invocation': False,
|
||||
'operation_params': '{"is_reverse": False, "is_auto": False}',
|
||||
'is_cancel_pending': False,
|
||||
'error': error_obj,
|
||||
'resource_changes': resource_changes_obj,
|
||||
'changed_info': changed_info_obj
|
||||
'changed_info': changed_info_obj,
|
||||
'changed_ext_connectivity': changed_ext_connectivity_obj,
|
||||
}
|
||||
|
||||
return vnf_lcm_op_occs
|
||||
@ -1484,3 +1533,10 @@ def vnf_data(status='ACTIVE'):
|
||||
name='test',
|
||||
status=status,
|
||||
vim_id=uuidsentinel.vim_id)
|
||||
|
||||
|
||||
def return_vnf_lcm_opoccs_list():
|
||||
vnf_lcm_op_occs = fake_vnf_lcm_op_occs()
|
||||
obj = objects.VnfLcmOpOcc(**vnf_lcm_op_occs)
|
||||
|
||||
return [obj]
|
||||
|
@ -3366,3 +3366,141 @@ class TestController(base.TestCase):
|
||||
|
||||
resp = req.get_response(self.app)
|
||||
self.assertEqual(http_client.INTERNAL_SERVER_ERROR, resp.status_code)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
def test_op_occ_list(self, mock_op_occ_list):
|
||||
req = fake_request.HTTPRequest.blank('/vnflcm/v1/vnf_lcm_op_occs')
|
||||
|
||||
complex_attributes = [
|
||||
'error',
|
||||
'resourceChanges',
|
||||
'operationParams',
|
||||
'changedInfo']
|
||||
|
||||
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
|
||||
expected_result = fakes.index_response(
|
||||
remove_attrs=complex_attributes)
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
@ddt.data(
|
||||
{'filter': '(eq,id,f26f181d-7891-4720-b022-b074ec1733ef)'},
|
||||
{'filter': '(neq,operationState,COMPLETED)'},
|
||||
{'filter': '(gte,stateEnteredTime,2020-03-14 04:10:15+00:00)'},
|
||||
{'filter': '(eq,isAutomaticInvocation,False)'},
|
||||
{'filter': '(lt,errorPoint,4)'},
|
||||
{'filter':
|
||||
"""(eq,error,'"{"title": "ERROR",
|
||||
"status": 500, "detail": "ERROR"}"')"""},
|
||||
{'filter':
|
||||
"""(in,changedInfo,'"{"vnfInstanceName": "test"}"')"""},
|
||||
{'filter':
|
||||
"""(neq,operationParams,'"{"terminationType": "FORCEFUL"}"')"""},
|
||||
)
|
||||
def test_op_occ_filter_attributes(self, filter_params,
|
||||
mock_op_occ_list):
|
||||
query = urllib.parse.urlencode(filter_params)
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
|
||||
|
||||
complex_attributes = [
|
||||
'error',
|
||||
'resourceChanges',
|
||||
'operationParams',
|
||||
'changedInfo']
|
||||
|
||||
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
|
||||
expected_result = fakes.index_response(
|
||||
remove_attrs=complex_attributes)
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
def test_op_occ_filter_attributes_invalid_filter(self, mock_op_occ_list):
|
||||
query = urllib.parse.urlencode({'filter': '(lt,non_existing,4)'})
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
|
||||
|
||||
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
self.assertRaises(
|
||||
exceptions.ValidationError, self.controller.list_lcm_op_occs, req)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
def test_op_occ_attribute_selector_all_fields(self, mock_op_occ_list):
|
||||
params = {'all_fields': 'True'}
|
||||
query = urllib.parse.urlencode(params)
|
||||
req = fake_request.HTTPRequest.blank('/vnflcm/v1/vnf_lcm_op_occs?' +
|
||||
query)
|
||||
|
||||
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
|
||||
expected_result = fakes.index_response()
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
@ddt.data(
|
||||
{'fields': 'error'},
|
||||
{'fields': 'resourceChanges'},
|
||||
{'fields': 'operationParams'},
|
||||
{'fields': 'changedInfo'}
|
||||
)
|
||||
def test_op_occ_attribute_selector_fields(self, filter_params,
|
||||
mock_op_occ_list):
|
||||
query = urllib.parse.urlencode(filter_params)
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
|
||||
|
||||
complex_attributes = [
|
||||
'error',
|
||||
'resourceChanges',
|
||||
'operationParams',
|
||||
'changedInfo']
|
||||
|
||||
remove_attributes = [
|
||||
x for x in complex_attributes if x != filter_params['fields']]
|
||||
|
||||
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
|
||||
expected_result = fakes.index_response(remove_attrs=remove_attributes)
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
@ddt.data(
|
||||
{'exclude_fields': 'error'},
|
||||
{'exclude_fields': 'resourceChanges'},
|
||||
{'exclude_fields': 'operationParams'},
|
||||
{'exclude_fields': 'changedInfo'}
|
||||
)
|
||||
def test_op_occ_attribute_selector_exclude_fields(self, filter_params,
|
||||
mock_op_occ_list):
|
||||
query = urllib.parse.urlencode(filter_params)
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
|
||||
|
||||
remove_attributes = [filter_params['exclude_fields']]
|
||||
|
||||
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
|
||||
expected_result = fakes.index_response(remove_attrs=remove_attributes)
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
def test_op_occ_attribute_selector_fields_error(self, mock_op_occ_list):
|
||||
query = urllib.parse.urlencode({'fields': 'non_existent_column'})
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
|
||||
|
||||
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
self.assertRaises(
|
||||
exceptions.ValidationError, self.controller.list_lcm_op_occs, req)
|
||||
|
Loading…
Reference in New Issue
Block a user