Support Subscription for LCM notifications by ETSI
Supported for Flow of managing subscriptions as defined in ETSI SOL003. Supported POST/DELETE/GET(List)/GET(Individual) LccnSubscription. * POST /vnflcm/{apiMajorVersion}/subscriptions Implements: blueprint support-etsi-nfv-specs Spec: https://specs.openstack.org/openstack/tacker-specs/specs/victoria/support-notification-api-based-on-etsi-nfv-sol.html Change-Id: Ia97dfdc2ea7ed1b11d519ae62d07a37896a80a35
This commit is contained in:
parent
35d15a089f
commit
5baeeefedf
@ -1,4 +1,11 @@
|
|||||||
# variables in header
|
# variables in header
|
||||||
|
|
||||||
|
subscription_id:
|
||||||
|
description: |
|
||||||
|
Identifier of the subscription.
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
vnf_instance_id:
|
vnf_instance_id:
|
||||||
description: |
|
description: |
|
||||||
Identifier of the VNF instance.
|
Identifier of the VNF instance.
|
||||||
@ -7,6 +14,104 @@ vnf_instance_id:
|
|||||||
type: string
|
type: string
|
||||||
|
|
||||||
# variables in body
|
# variables in body
|
||||||
|
authentication:
|
||||||
|
description: |
|
||||||
|
Authentication parameters to configure the use of
|
||||||
|
Authorization when sending notifications
|
||||||
|
corresponding to this subscription.
|
||||||
|
This attribute shall only be present if the subscriber
|
||||||
|
requires authorization of notifications.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: object
|
||||||
|
authentication_auth_type:
|
||||||
|
description: |
|
||||||
|
Defines the types of Authentication/Authorization which
|
||||||
|
the API consumer is willing to accept when receiving a
|
||||||
|
notification.
|
||||||
|
Permitted values:
|
||||||
|
|
||||||
|
BASIC: In every HTTP request to the
|
||||||
|
notification endpoint, use HTTP Basic
|
||||||
|
authentication with the client credentials.
|
||||||
|
|
||||||
|
OAUTH2_CLIENT_CREDENTIALS: In every
|
||||||
|
HTTP request to the notification endpoint, use
|
||||||
|
an OAuth 2.0 bearer token, obtained using the
|
||||||
|
client credentials grant type.
|
||||||
|
|
||||||
|
TLS_CERT: Every HTTP request to the
|
||||||
|
notification endpoint is sent over a mutually
|
||||||
|
authenticated TLS session, i.e. not only the
|
||||||
|
server is authenticated, but also the client is
|
||||||
|
authenticated during the TLS tunnel setup.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
authentication_params_basic:
|
||||||
|
description: |
|
||||||
|
Parameters for authentication/authorization using BASIC.
|
||||||
|
Shall be present if authType is "BASIC" and the
|
||||||
|
contained information has not been provisioned out of
|
||||||
|
band. Shall be absent otherwise.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: object
|
||||||
|
authentication_params_basic_password:
|
||||||
|
description: |
|
||||||
|
Password to be used in HTTP Basic authentication.
|
||||||
|
Shall be present if it has not been provisioned out of band.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
authentication_params_basic_user_name:
|
||||||
|
description: |
|
||||||
|
Username to be used in HTTP Basic authentication.
|
||||||
|
Shall be present if it has not been provisioned out of band.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
authentication_params_oauth2_client_credentials:
|
||||||
|
description: |
|
||||||
|
Parameters for authentication/authorization using
|
||||||
|
OAUTH2_CLIENT_CREDENTIALS.
|
||||||
|
Shall be present if authType is
|
||||||
|
"OAUTH2_CLIENT_CREDENTIALS" and the contained
|
||||||
|
information has not been provisioned out of band.
|
||||||
|
Shall be absent otherwise.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: object
|
||||||
|
authentication_params_oauth2_client_credentials_client_id:
|
||||||
|
description: |
|
||||||
|
Client identifier to be used in the access token request
|
||||||
|
of the OAuth 2.0 client credentials grant type. Shall be
|
||||||
|
present if it has not been provisioned out of band.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
authentication_params_oauth2_client_credentials_client_password:
|
||||||
|
description: |
|
||||||
|
Client password to be used in the access token request
|
||||||
|
of the OAuth 2.0 client credentials grant type. Shall be
|
||||||
|
present if it has not been provisioned out of band.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
authentication_params_oauth2_client_credentials_token_endpoint:
|
||||||
|
description: |
|
||||||
|
The token endpoint from which the access token can be
|
||||||
|
obtained. Shall be present if it has not been provisioned
|
||||||
|
out of band.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
callback_uri:
|
||||||
|
description: |
|
||||||
|
The URI of the endpoint to send the notification to.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
cause:
|
cause:
|
||||||
description: |
|
description: |
|
||||||
Indicates the reason why a healing procedure is required.
|
Indicates the reason why a healing procedure is required.
|
||||||
@ -222,6 +327,40 @@ ext_virtual_links_resource_id:
|
|||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
filter:
|
||||||
|
description: |
|
||||||
|
Filter settings for this subscription, to define the
|
||||||
|
subset of all notifications this subscription relates
|
||||||
|
to. A particular notification is sent to the subscriber
|
||||||
|
if the filter matches, or if there is no filter.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: object
|
||||||
|
filter_notification_types:
|
||||||
|
description: |
|
||||||
|
Match particular notification types.
|
||||||
|
Permitted values:
|
||||||
|
|
||||||
|
VnfLcmOperationOccurrenceNotification
|
||||||
|
|
||||||
|
VnfIdentifierCreationNotification
|
||||||
|
|
||||||
|
VnfIdentifierDeletionNotification
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
filter_operation_types:
|
||||||
|
description: |
|
||||||
|
Match particular VNF lifecycle operation types for
|
||||||
|
the notification of type
|
||||||
|
VnfLcmOperationOccurrenceNotification.
|
||||||
|
May be present if the "notificationTypes" attribute
|
||||||
|
contains the value
|
||||||
|
"VnfLcmOperationOccurrenceNotification", and
|
||||||
|
shall be absent otherwise.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
fixed_addresses:
|
fixed_addresses:
|
||||||
description: |
|
description: |
|
||||||
Fixed addresses to assign (from the subnet defined by "subnetId"
|
Fixed addresses to assign (from the subnet defined by "subnetId"
|
||||||
@ -408,6 +547,12 @@ subnet_id:
|
|||||||
in: body
|
in: body
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
subscription_id_response:
|
||||||
|
description: |
|
||||||
|
Identifier of this subscription resource.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
termination_type:
|
termination_type:
|
||||||
description: |
|
description: |
|
||||||
Indicates whether forceful or graceful termination is requested.
|
Indicates whether forceful or graceful termination is requested.
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"filter": {
|
||||||
|
"notificationTypes": [
|
||||||
|
"VnfLcmOperationOccurrenceNotification"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"callbackUri": "http://sample1.com/notification"
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"id": "76057f8e65ab37fb82d9382dfc3f3c8b",
|
||||||
|
"filter": {
|
||||||
|
"notificationTypes": [
|
||||||
|
"VnfLcmOperationOccurrenceNotification"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"callbackUri": "http://sample1.com/notification",
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://sample1.com/vnflcm/v1/subscriptions/76057f8e65ab37fb82d9382dfc3f3c8b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "76057f8e65ab37fb82d9382dfc3f3c8b",
|
||||||
|
"filter": {
|
||||||
|
"notificationTypes": [
|
||||||
|
"VnfLcmOperationOccurrenceNotification"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"callbackUri": "http://sample1.com/notification",
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://sample1.com/vnflcm/v1/subscriptions/76057f8e65ab37fb82d9382dfc3f3c8b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4845ac30eab62a0b0b4edc00fbb930ee",
|
||||||
|
"filter": {
|
||||||
|
"notificationTypes": [
|
||||||
|
"VnfLcmOperationOccurrenceNotification",
|
||||||
|
"VnfIdentifierCreationNotification",
|
||||||
|
"VnfIdentifierDeletionNotification"
|
||||||
|
],
|
||||||
|
"notificationTypes": [
|
||||||
|
"SCALE",
|
||||||
|
"HEAL"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"callbackUri": "http://sample2.com/notification",
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://sample2.com/vnflcm/v1/subscriptions/4845ac30eab62a0b0b4edc00fbb930ee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"id": "76057f8e65ab37fb82d9382dfc3f3c8b",
|
||||||
|
"filter": {
|
||||||
|
"notificationTypes": [
|
||||||
|
"VnfLcmOperationOccurrenceNotification"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"callbackUri": "http://sample1.com/notification",
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://sample1.com/vnflcm/v1/subscriptions/76057f8e65ab37fb82d9382dfc3f3c8b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,11 @@
|
|||||||
The response is about a redirection hint. The header of the response
|
The response is about a redirection hint. The header of the response
|
||||||
usually contains a 'location' value where requesters can check to track
|
usually contains a 'location' value where requesters can check to track
|
||||||
the real location of the resource.
|
the real location of the resource.
|
||||||
|
303:
|
||||||
|
default: |
|
||||||
|
The server is redirecting the user agent to a different resource, as indicated
|
||||||
|
by a URI in the Location header field, which is intended to provide an indirect
|
||||||
|
response to the original request.
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# Error Codes #
|
# Error Codes #
|
||||||
|
@ -569,3 +569,192 @@ Response Example
|
|||||||
|
|
||||||
.. literalinclude:: samples/vnflcm/list-vnf-instance-response.json
|
.. literalinclude:: samples/vnflcm/list-vnf-instance-response.json
|
||||||
:language: javascript
|
:language: javascript
|
||||||
|
|
||||||
|
Create a new subscription
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. rest_method:: POST /vnflcm/v1/subscriptions
|
||||||
|
|
||||||
|
The POST method creates a new subscription.
|
||||||
|
|
||||||
|
As the result of successfully executing this method, a new "Individual subscription" resource
|
||||||
|
shall have been created. This method shall not trigger any notification.
|
||||||
|
|
||||||
|
Creation of two "Individual subscription" resources with the same callbackURI and the same filter can result in
|
||||||
|
performance degradation and will provide duplicates of notifications to the NFVO, and might make sense only in very
|
||||||
|
rare use cases. Consequently, the VNFM may either allow creating an "Individual subscription" resource if another
|
||||||
|
Individual subscription resource with the same filter and callbackUri already exists (in which case it shall return the
|
||||||
|
201 Created response code), or may decide to not create a duplicate "Individual subscription" resource (in which case
|
||||||
|
it shall return a "303 See Other" response code referencing the existing "Individual subscription" resource with the same
|
||||||
|
filter and callbackUri).
|
||||||
|
|
||||||
|
Response Codes
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. rest_status_code:: success status.yaml
|
||||||
|
|
||||||
|
- 201
|
||||||
|
|
||||||
|
.. rest_status_code:: error status.yaml
|
||||||
|
|
||||||
|
- 303
|
||||||
|
- 400
|
||||||
|
- 401
|
||||||
|
- 403
|
||||||
|
|
||||||
|
Request Parameters
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters_vnflcm.yaml
|
||||||
|
|
||||||
|
- filter: filter
|
||||||
|
- notificationTypes: filter_notification_types
|
||||||
|
- operationTypes: filter_operation_types
|
||||||
|
- callbackUri : callback_uri
|
||||||
|
- authentication: authentication
|
||||||
|
- authType: authentication_auth_type
|
||||||
|
- paramsBasic: authentication_params_basic
|
||||||
|
- userName: authentication_params_basic_user_name
|
||||||
|
- password: authentication_params_basic_password
|
||||||
|
- paramsOauth2ClientCredentials: authentication_params_oauth2_client_credentials
|
||||||
|
- clientId: authentication_params_oauth2_client_credentials_client_id
|
||||||
|
- clientPassword: authentication_params_oauth2_client_credentials_client_password
|
||||||
|
- tokenEndpoint: authentication_params_oauth2_client_credentials_token_endpoint
|
||||||
|
|
||||||
|
Request Example
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. literalinclude:: samples/vnflcm/create-subscription-request.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
|
Response Parameters
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters_vnflcm.yaml
|
||||||
|
|
||||||
|
- id: subscription_id_response
|
||||||
|
- filter: filter
|
||||||
|
- notificationTypes: filter_notification_types
|
||||||
|
- operationTypes: filter_operation_types
|
||||||
|
- callbackUri: callback_uri
|
||||||
|
- _links: vnf_instance_links
|
||||||
|
|
||||||
|
Response Example
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. literalinclude:: samples/vnflcm/create-subscription-response.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
|
Delete a subscription
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. rest_method:: DELETE /vnflcm/v1/subscriptions/{subscriptionId}
|
||||||
|
|
||||||
|
The DELETE method terminates an individual subscription.
|
||||||
|
|
||||||
|
As the result of successfully executing this method, the "Individual subscription" resource shall not exist any longer.
|
||||||
|
This means that no notifications for that subscription shall be sent to the formerly-subscribed API consumer.
|
||||||
|
|
||||||
|
Response Codes
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. rest_status_code:: success status.yaml
|
||||||
|
|
||||||
|
- 204
|
||||||
|
|
||||||
|
.. rest_status_code:: error status.yaml
|
||||||
|
|
||||||
|
- 401
|
||||||
|
- 403
|
||||||
|
- 404
|
||||||
|
|
||||||
|
Request Parameters
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters_vnflcm.yaml
|
||||||
|
|
||||||
|
- subscriptionId: subscription_id
|
||||||
|
|
||||||
|
Show subscription
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. rest_method:: GET /vnflcm/v1/subscriptions/{subscriptionId}
|
||||||
|
|
||||||
|
The GET method retrieves information about a subscription by reading an "Individual subscription" resource.
|
||||||
|
|
||||||
|
Response Codes
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. rest_status_code:: success status.yaml
|
||||||
|
|
||||||
|
- 200
|
||||||
|
|
||||||
|
.. rest_status_code:: error status.yaml
|
||||||
|
|
||||||
|
- 401
|
||||||
|
- 403
|
||||||
|
- 404
|
||||||
|
|
||||||
|
Request Parameters
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters_vnflcm.yaml
|
||||||
|
|
||||||
|
- subscriptionId: subscription_id
|
||||||
|
|
||||||
|
Response Parameters
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters_vnflcm.yaml
|
||||||
|
|
||||||
|
- id: subscription_id_response
|
||||||
|
- filter: filter
|
||||||
|
- notificationTypes: filter_notification_types
|
||||||
|
- operationTypes: filter_operation_types
|
||||||
|
- callbackUri: callback_uri
|
||||||
|
- _links: vnf_instance_links
|
||||||
|
|
||||||
|
Response Example
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. literalinclude:: samples/vnflcm/show-subscription-response.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
|
List subscription
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. rest_method:: GET /vnflcm/v1/subscriptions
|
||||||
|
|
||||||
|
The GET method queries the list of active subscriptions of the functional block that invokes the method.
|
||||||
|
It can be used e.g. for resynchronization after error situations.
|
||||||
|
|
||||||
|
Response Codes
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. rest_status_code:: success status.yaml
|
||||||
|
|
||||||
|
- 200
|
||||||
|
|
||||||
|
.. rest_status_code:: error status.yaml
|
||||||
|
|
||||||
|
- 400
|
||||||
|
- 401
|
||||||
|
- 403
|
||||||
|
|
||||||
|
Response Parameters
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters_vnflcm.yaml
|
||||||
|
|
||||||
|
- id: subscription_id_response
|
||||||
|
- filter: filter
|
||||||
|
- notificationTypes: filter_notification_types
|
||||||
|
- operationTypes: filter_operation_types
|
||||||
|
- callbackUri: callback_uri
|
||||||
|
- _links: vnf_instance_links
|
||||||
|
|
||||||
|
Response Example
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. literalinclude:: samples/vnflcm/list-subscription-response.json
|
||||||
|
:language: javascript
|
||||||
|
@ -227,3 +227,14 @@ heal = {
|
|||||||
},
|
},
|
||||||
'additionalProperties': False,
|
'additionalProperties': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register_subscription = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'filter': parameter_types.keyvalue_pairs,
|
||||||
|
'callbackUri': {'type': 'string', 'maxLength': 255},
|
||||||
|
'authentication': parameter_types.keyvalue_pairs,
|
||||||
|
},
|
||||||
|
'required': ['callbackUri'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
}
|
||||||
|
@ -13,11 +13,20 @@
|
|||||||
# 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 json
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from tacker.api import views as base
|
from tacker.api import views as base
|
||||||
from tacker.common import utils
|
from tacker.common import utils
|
||||||
|
import tacker.conf
|
||||||
from tacker.objects import fields
|
from tacker.objects import fields
|
||||||
from tacker.objects import vnf_instance as _vnf_instance
|
from tacker.objects import vnf_instance as _vnf_instance
|
||||||
|
|
||||||
|
CONF = tacker.conf.CONF
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ViewBuilder(base.BaseViewBuilder):
|
class ViewBuilder(base.BaseViewBuilder):
|
||||||
|
|
||||||
@ -108,3 +117,113 @@ class ViewBuilder(base.BaseViewBuilder):
|
|||||||
def index(self, vnf_instances):
|
def index(self, vnf_instances):
|
||||||
return [self._get_vnf_instance_info(vnf_instance)
|
return [self._get_vnf_instance_info(vnf_instance)
|
||||||
for vnf_instance in vnf_instances]
|
for vnf_instance in vnf_instances]
|
||||||
|
|
||||||
|
def _get_subscription_links(self, vnf_lcm_subscription):
|
||||||
|
if isinstance(vnf_lcm_subscription.id, str):
|
||||||
|
decode_id = vnf_lcm_subscription.id
|
||||||
|
else:
|
||||||
|
decode_id = vnf_lcm_subscription.id.decode()
|
||||||
|
return {
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": '%(endpoint)s/vnflcm/v1/subscriptions/%(id)s' %
|
||||||
|
{
|
||||||
|
"endpoint": CONF.vnf_lcm.endpoint_url,
|
||||||
|
"id": decode_id}}}}
|
||||||
|
|
||||||
|
def _basic_subscription_info(self, vnf_lcm_subscription, filter=None):
|
||||||
|
if not filter:
|
||||||
|
if 'filter' in vnf_lcm_subscription:
|
||||||
|
filter_dict = json.loads(vnf_lcm_subscription.filter)
|
||||||
|
return {
|
||||||
|
'id': vnf_lcm_subscription.id.decode(),
|
||||||
|
'filter': filter_dict,
|
||||||
|
'callbackUri': vnf_lcm_subscription.callback_uri.decode(),
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'id': vnf_lcm_subscription.id.decode(),
|
||||||
|
'callbackUri': vnf_lcm_subscription.callback_uri.decode(),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'id': vnf_lcm_subscription.id,
|
||||||
|
'filter': filter,
|
||||||
|
'callbackUri': vnf_lcm_subscription.callback_uri,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _subscription_filter(
|
||||||
|
self,
|
||||||
|
subscription_data,
|
||||||
|
nextpage_opaque_marker,
|
||||||
|
paging):
|
||||||
|
# filter processing
|
||||||
|
lcmsubscription = []
|
||||||
|
|
||||||
|
# last_flg is True if nextpage_opaque_marker is set
|
||||||
|
last_flg = False
|
||||||
|
|
||||||
|
start_num = CONF.vnf_lcm.subscription_num * (paging - 1)
|
||||||
|
# Subscription_data counter for comparing
|
||||||
|
# subscription_data and start_num
|
||||||
|
wk_counter = 0
|
||||||
|
for cnt, line in enumerate(subscription_data):
|
||||||
|
LOG.debug("cnt %d,line %s" % (cnt, line))
|
||||||
|
|
||||||
|
if start_num > wk_counter:
|
||||||
|
wk_counter = wk_counter + 1
|
||||||
|
else:
|
||||||
|
if (CONF.vnf_lcm.subscription_num > len(
|
||||||
|
lcmsubscription) and nextpage_opaque_marker):
|
||||||
|
# add lcmsubscription
|
||||||
|
vnf_subscription_res = self._basic_subscription_info(
|
||||||
|
line)
|
||||||
|
links = self._get_subscription_links(line)
|
||||||
|
vnf_subscription_res.update(links)
|
||||||
|
lcmsubscription.append(vnf_subscription_res)
|
||||||
|
if CONF.vnf_lcm.subscription_num == len(
|
||||||
|
lcmsubscription):
|
||||||
|
if cnt == len(subscription_data) - 1:
|
||||||
|
last_flg = True
|
||||||
|
break
|
||||||
|
elif not nextpage_opaque_marker:
|
||||||
|
# add lcmsubscription
|
||||||
|
vnf_subscription_res = self._basic_subscription_info(
|
||||||
|
line)
|
||||||
|
links = self._get_subscription_links(line)
|
||||||
|
vnf_subscription_res.update(links)
|
||||||
|
lcmsubscription.append(vnf_subscription_res)
|
||||||
|
if CONF.vnf_lcm.subscription_num < len(
|
||||||
|
lcmsubscription):
|
||||||
|
return 400, False
|
||||||
|
if cnt == len(subscription_data) - 1:
|
||||||
|
last_flg = True
|
||||||
|
|
||||||
|
LOG.debug("len(lcmsubscription) %s" % len(lcmsubscription))
|
||||||
|
LOG.debug(
|
||||||
|
"CONF.vnf_lcm.subscription_num %s" %
|
||||||
|
CONF.vnf_lcm.subscription_num)
|
||||||
|
|
||||||
|
return lcmsubscription, last_flg
|
||||||
|
|
||||||
|
def _get_vnf_lcm_subscription(self, vnf_lcm_subscription, filter=None):
|
||||||
|
vnf_lcm_subscription_response = self._basic_subscription_info(
|
||||||
|
vnf_lcm_subscription, filter)
|
||||||
|
|
||||||
|
links = self._get_subscription_links(vnf_lcm_subscription)
|
||||||
|
vnf_lcm_subscription_response.update(links)
|
||||||
|
|
||||||
|
return vnf_lcm_subscription_response
|
||||||
|
|
||||||
|
def subscription_create(self, vnf_lcm_subscription, filter):
|
||||||
|
return self._get_vnf_lcm_subscription(vnf_lcm_subscription, filter)
|
||||||
|
|
||||||
|
def subscription_list(
|
||||||
|
self,
|
||||||
|
vnf_lcm_subscriptions,
|
||||||
|
nextpage_opaque_marker,
|
||||||
|
paging):
|
||||||
|
return self._subscription_filter(
|
||||||
|
vnf_lcm_subscriptions, nextpage_opaque_marker, paging)
|
||||||
|
|
||||||
|
def subscription_show(self, vnf_lcm_subscriptions):
|
||||||
|
return self._get_vnf_lcm_subscription(vnf_lcm_subscriptions)
|
||||||
|
@ -13,25 +13,42 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import traceback
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from six.moves import http_client
|
from six.moves import http_client
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
|
from tacker._i18n import _
|
||||||
from tacker.api.schemas import vnf_lcm
|
from tacker.api.schemas import vnf_lcm
|
||||||
from tacker.api import validation
|
from tacker.api import validation
|
||||||
from tacker.api.views import vnf_lcm as vnf_lcm_view
|
from tacker.api.views import vnf_lcm as vnf_lcm_view
|
||||||
from tacker.common import exceptions
|
from tacker.common import exceptions
|
||||||
from tacker.common import utils
|
from tacker.common import utils
|
||||||
from tacker.conductor.conductorrpc import vnf_lcm_rpc
|
from tacker.conductor.conductorrpc import vnf_lcm_rpc
|
||||||
|
import tacker.conf
|
||||||
from tacker.extensions import nfvo
|
from tacker.extensions import nfvo
|
||||||
|
from tacker import manager
|
||||||
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.policies import vnf_lcm as vnf_lcm_policies
|
from tacker.policies import vnf_lcm as vnf_lcm_policies
|
||||||
from tacker.vnfm import vim_client
|
from tacker.vnfm import vim_client
|
||||||
from tacker import wsgi
|
from tacker import wsgi
|
||||||
|
|
||||||
|
CONF = tacker.conf.CONF
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def check_vnf_state(action, instantiation_state=None, task_state=(None,)):
|
def check_vnf_state(action, instantiation_state=None, task_state=(None,)):
|
||||||
"""Decorator to check vnf states are valid for particular action.
|
"""Decorator to check vnf states are valid for particular action.
|
||||||
@ -70,11 +87,32 @@ def check_vnf_state(action, instantiation_state=None, task_state=(None,)):
|
|||||||
|
|
||||||
class VnfLcmController(wsgi.Controller):
|
class VnfLcmController(wsgi.Controller):
|
||||||
|
|
||||||
|
notification_type_list = ['VnfLcmOperationOccurrenceNotification',
|
||||||
|
'VnfIdentifierCreationNotification',
|
||||||
|
'VnfIdentifierDeletionNotification']
|
||||||
|
operation_type_list = ['INSTANTIATE',
|
||||||
|
'SCALE',
|
||||||
|
'SCALE_TO_LEVEL',
|
||||||
|
'CHANGE_FLAVOUR',
|
||||||
|
'TERMINATE',
|
||||||
|
'HEAL',
|
||||||
|
'OPERATE',
|
||||||
|
'CHANGE_EXT_CONN',
|
||||||
|
'MODIFY_INFO']
|
||||||
|
operation_state_list = ['STARTING',
|
||||||
|
'PROCESSING',
|
||||||
|
'COMPLETED',
|
||||||
|
'FAILED_TEMP',
|
||||||
|
'FAILED',
|
||||||
|
'ROLLING_BACK',
|
||||||
|
'ROLLED_BACK']
|
||||||
|
|
||||||
_view_builder_class = vnf_lcm_view.ViewBuilder
|
_view_builder_class = vnf_lcm_view.ViewBuilder
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(VnfLcmController, self).__init__()
|
super(VnfLcmController, self).__init__()
|
||||||
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
|
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
|
||||||
|
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
|
||||||
|
|
||||||
def _get_vnf_instance_href(self, vnf_instance):
|
def _get_vnf_instance_href(self, vnf_instance):
|
||||||
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance.id
|
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance.id
|
||||||
@ -349,6 +387,193 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
vnf_instance = self._get_vnf_instance(context, id)
|
vnf_instance = self._get_vnf_instance(context, id)
|
||||||
self._heal(context, vnf_instance, body)
|
self._heal(context, vnf_instance, body)
|
||||||
|
|
||||||
|
@wsgi.response(http_client.CREATED)
|
||||||
|
@validation.schema(vnf_lcm.register_subscription)
|
||||||
|
def register_subscription(self, request, body):
|
||||||
|
subscription_request_data = body
|
||||||
|
if subscription_request_data.get('filter'):
|
||||||
|
# notificationTypes check
|
||||||
|
notification_types = subscription_request_data.get(
|
||||||
|
"filter").get("notificationTypes")
|
||||||
|
for notification_type in notification_types:
|
||||||
|
if notification_type not in self.notification_type_list:
|
||||||
|
msg = (
|
||||||
|
_("notificationTypes value mismatch: %s") %
|
||||||
|
notification_type)
|
||||||
|
return self._make_problem_detail(
|
||||||
|
msg, 400, title='Bad Request')
|
||||||
|
|
||||||
|
# operationTypes check
|
||||||
|
operation_types = subscription_request_data.get(
|
||||||
|
"filter").get("operationTypes")
|
||||||
|
for operation_type in operation_types:
|
||||||
|
if operation_type not in self.operation_type_list:
|
||||||
|
msg = (
|
||||||
|
_("operationTypes value mismatch: %s") %
|
||||||
|
operation_type)
|
||||||
|
return self._make_problem_detail(
|
||||||
|
msg, 400, title='Bad Request')
|
||||||
|
|
||||||
|
subscription_id = uuidutils.generate_uuid()
|
||||||
|
|
||||||
|
vnf_lcm_subscription = subscription_obj.LccnSubscriptionRequest(
|
||||||
|
context=request.context)
|
||||||
|
vnf_lcm_subscription.id = subscription_id
|
||||||
|
vnf_lcm_subscription.callback_uri = subscription_request_data.get(
|
||||||
|
'callbackUri')
|
||||||
|
vnf_lcm_subscription.subscription_authentication = \
|
||||||
|
subscription_request_data.get('subscriptionAuthentication')
|
||||||
|
LOG.debug("filter %s " % subscription_request_data.get('filter'))
|
||||||
|
LOG.debug(
|
||||||
|
"filter type %s " %
|
||||||
|
type(
|
||||||
|
subscription_request_data.get('filter')))
|
||||||
|
filter_uni = subscription_request_data.get('filter')
|
||||||
|
filter = ast.literal_eval(str(filter_uni).replace("u'", "'"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
vnf_lcm_subscription = vnf_lcm_subscription.create(filter)
|
||||||
|
LOG.debug("vnf_lcm_subscription %s" % vnf_lcm_subscription)
|
||||||
|
except exceptions.SeeOther as e:
|
||||||
|
if re.search("^303", str(e)):
|
||||||
|
res = self._make_problem_detail(
|
||||||
|
"See Other", 303, title='See Other')
|
||||||
|
link = (
|
||||||
|
'LINK',
|
||||||
|
CONF.vnf_lcm.endpoint_url +
|
||||||
|
"/vnflcm/v1/subscriptions/" +
|
||||||
|
str(e)[
|
||||||
|
3:])
|
||||||
|
res.headerlist.append(link)
|
||||||
|
return res
|
||||||
|
else:
|
||||||
|
LOG.error(traceback.format_exc())
|
||||||
|
return self._make_problem_detail(
|
||||||
|
str(e), 500, title='Internal Server Error')
|
||||||
|
|
||||||
|
result = self._view_builder.subscription_create(vnf_lcm_subscription,
|
||||||
|
filter)
|
||||||
|
location = result.get('_links', {}).get('self', {}).get('href')
|
||||||
|
headers = {"location": location}
|
||||||
|
return wsgi.ResponseObject(result, headers=headers)
|
||||||
|
|
||||||
|
@wsgi.response(http_client.OK)
|
||||||
|
def subscription_show(self, request, subscriptionId):
|
||||||
|
try:
|
||||||
|
vnf_lcm_subscriptions = (
|
||||||
|
subscription_obj.LccnSubscriptionRequest.
|
||||||
|
vnf_lcm_subscriptions_show(request.context, subscriptionId))
|
||||||
|
if not vnf_lcm_subscriptions:
|
||||||
|
msg = (
|
||||||
|
_("Can not find requested vnf lcm subscriptions: %s") %
|
||||||
|
subscriptionId)
|
||||||
|
return self._make_problem_detail(msg, 404, title='Not Found')
|
||||||
|
except Exception as e:
|
||||||
|
return self._make_problem_detail(
|
||||||
|
str(e), 500, title='Internal Server Error')
|
||||||
|
|
||||||
|
return self._view_builder.subscription_show(vnf_lcm_subscriptions)
|
||||||
|
|
||||||
|
@wsgi.response(http_client.OK)
|
||||||
|
def subscription_list(self, request):
|
||||||
|
nextpage_opaque_marker = ""
|
||||||
|
paging = 1
|
||||||
|
|
||||||
|
re_url = request.path_url
|
||||||
|
query_params = request.query_string
|
||||||
|
if query_params:
|
||||||
|
query_params = parse.unquote(query_params)
|
||||||
|
LOG.debug("query_params %s" % query_params)
|
||||||
|
if query_params:
|
||||||
|
query_param_list = query_params.split('&')
|
||||||
|
for query_param in query_param_list:
|
||||||
|
query_param_key_value = query_param.split('=')
|
||||||
|
if len(query_param_key_value) != 2:
|
||||||
|
msg = _("Request query parameter error")
|
||||||
|
return self._make_problem_detail(
|
||||||
|
msg, 400, title='Bad Request')
|
||||||
|
if query_param_key_value[0] == 'nextpage_opaque_marker':
|
||||||
|
nextpage_opaque_marker = query_param_key_value[1]
|
||||||
|
if query_param_key_value[0] == 'page':
|
||||||
|
paging = int(query_param_key_value[1])
|
||||||
|
|
||||||
|
try:
|
||||||
|
vnf_lcm_subscriptions = (
|
||||||
|
subscription_obj.LccnSubscriptionRequest.
|
||||||
|
vnf_lcm_subscriptions_list(request.context))
|
||||||
|
LOG.debug("vnf_lcm_subscriptions %s" % vnf_lcm_subscriptions)
|
||||||
|
subscription_data, last = self._view_builder.subscription_list(
|
||||||
|
vnf_lcm_subscriptions, nextpage_opaque_marker, paging)
|
||||||
|
LOG.debug("last %s" % last)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(traceback.format_exc())
|
||||||
|
return self._make_problem_detail(
|
||||||
|
str(e), 500, title='Internal Server Error')
|
||||||
|
|
||||||
|
if subscription_data == 400:
|
||||||
|
msg = _("Number of records exceeds nextpage_opaque_marker")
|
||||||
|
return self._make_problem_detail(msg, 400, title='Bad Request')
|
||||||
|
|
||||||
|
# make response
|
||||||
|
res = webob.Response(content_type='application/json')
|
||||||
|
res.body = jsonutils.dump_as_bytes(subscription_data)
|
||||||
|
res.status_int = 200
|
||||||
|
if nextpage_opaque_marker:
|
||||||
|
if not last:
|
||||||
|
ln = '<%s?page=%s>;rel="next"; title*="next chapter"' % (
|
||||||
|
re_url, paging + 1)
|
||||||
|
# Regarding the setting in http header related to
|
||||||
|
# nextpage control, RFC8288 and NFV-SOL013
|
||||||
|
# specifications have not been confirmed.
|
||||||
|
# Therefore, it is implemented by setting "page",
|
||||||
|
# which is a general control method of WebAPI,
|
||||||
|
# as "URI-Reference" of Link header.
|
||||||
|
|
||||||
|
links = ('Link', ln)
|
||||||
|
res.headerlist.append(links)
|
||||||
|
LOG.debug("subscription_list res %s" % res)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
@wsgi.response(http_client.NO_CONTENT)
|
||||||
|
def delete_subscription(self, request, subscriptionId):
|
||||||
|
try:
|
||||||
|
vnf_lcm_subscription = \
|
||||||
|
subscription_obj.LccnSubscriptionRequest.destroy(
|
||||||
|
request.context, subscriptionId)
|
||||||
|
if vnf_lcm_subscription == 404:
|
||||||
|
msg = (
|
||||||
|
_("Can not find requested vnf lcm subscription: %s") %
|
||||||
|
subscriptionId)
|
||||||
|
return self._make_problem_detail(msg, 404, title='Not Found')
|
||||||
|
except Exception as e:
|
||||||
|
return self._make_problem_detail(
|
||||||
|
str(e), 500, title='Internal Server Error')
|
||||||
|
|
||||||
|
# Generate a response when an error occurs as a problem_detail object
|
||||||
|
def _make_problem_detail(
|
||||||
|
self,
|
||||||
|
detail,
|
||||||
|
status,
|
||||||
|
title=None,
|
||||||
|
type=None,
|
||||||
|
instance=None):
|
||||||
|
'''This process returns the problem_detail to the caller'''
|
||||||
|
LOG.error(detail)
|
||||||
|
res = webob.Response(content_type='application/problem+json')
|
||||||
|
problem_details = {}
|
||||||
|
if type:
|
||||||
|
problem_details['type'] = type
|
||||||
|
if title:
|
||||||
|
problem_details['title'] = title
|
||||||
|
problem_details['detail'] = detail
|
||||||
|
problem_details['status'] = status
|
||||||
|
if instance:
|
||||||
|
problem_details['instance'] = instance
|
||||||
|
res.text = json.dumps(problem_details)
|
||||||
|
res.status_int = status
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
def create_resource():
|
def create_resource():
|
||||||
return wsgi.Resource(VnfLcmController())
|
return wsgi.Resource(VnfLcmController())
|
||||||
|
@ -90,3 +90,11 @@ class VnflcmAPIRouter(wsgi.Router):
|
|||||||
self._setup_route(mapper,
|
self._setup_route(mapper,
|
||||||
"/vnf_instances/{id}/heal",
|
"/vnf_instances/{id}/heal",
|
||||||
methods, controller, default_resource)
|
methods, controller, default_resource)
|
||||||
|
|
||||||
|
methods = {"GET": "subscription_list", "POST": "register_subscription"}
|
||||||
|
self._setup_route(mapper, "/subscriptions",
|
||||||
|
methods, controller, default_resource)
|
||||||
|
|
||||||
|
methods = {"GET": "subscription_show", "DELETE": "delete_subscription"}
|
||||||
|
self._setup_route(mapper, "/subscriptions/{subscriptionId}",
|
||||||
|
methods, controller, default_resource)
|
||||||
|
@ -345,3 +345,7 @@ class LimitExceeded(TackerException):
|
|||||||
class UserDataUpdateCreateFailed(TackerException):
|
class UserDataUpdateCreateFailed(TackerException):
|
||||||
msg_fmt = _("User data for VNF package %(id)s cannot be updated "
|
msg_fmt = _("User data for VNF package %(id)s cannot be updated "
|
||||||
"or created after %(retries)d retries.")
|
"or created after %(retries)d retries.")
|
||||||
|
|
||||||
|
|
||||||
|
class SeeOther(TackerException):
|
||||||
|
code = 303
|
||||||
|
@ -18,12 +18,14 @@ 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 vnf_lcm
|
||||||
from tacker.conf import vnf_package
|
from tacker.conf import vnf_package
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
|
CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
|
||||||
|
|
||||||
vnf_package.register_opts(CONF)
|
vnf_package.register_opts(CONF)
|
||||||
|
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)
|
||||||
|
47
tacker/conf/vnf_lcm.py
Normal file
47
tacker/conf/vnf_lcm.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
OPTS = [
|
||||||
|
cfg.StrOpt(
|
||||||
|
'endpoint_url',
|
||||||
|
default='http://localhost:9890/',
|
||||||
|
help="endpoint_url"),
|
||||||
|
cfg.IntOpt(
|
||||||
|
'subscription_num',
|
||||||
|
default=100,
|
||||||
|
help="Number of subscriptions"),
|
||||||
|
cfg.IntOpt(
|
||||||
|
'retry_num',
|
||||||
|
default=3,
|
||||||
|
help="Number of retry"),
|
||||||
|
cfg.IntOpt(
|
||||||
|
'retry_wait',
|
||||||
|
default=10,
|
||||||
|
help="Retry interval(sec)")]
|
||||||
|
|
||||||
|
vnf_lcm_group = cfg.OptGroup('vnf_lcm',
|
||||||
|
title='vnf_lcm options',
|
||||||
|
help="Vnflcm options group")
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(vnf_lcm_group)
|
||||||
|
conf.register_opts(OPTS, group=vnf_lcm_group)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {vnf_lcm_group: OPTS}
|
@ -246,3 +246,54 @@ class VnfResource(model_base.BASE, models.SoftDeleteMixin,
|
|||||||
resource_type = sa.Column(sa.String(255), nullable=False)
|
resource_type = sa.Column(sa.String(255), nullable=False)
|
||||||
resource_identifier = sa.Column(sa.String(255), nullable=False)
|
resource_identifier = sa.Column(sa.String(255), nullable=False)
|
||||||
resource_status = sa.Column(sa.String(255), nullable=False)
|
resource_status = sa.Column(sa.String(255), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class VnfLcmSubscriptions(model_base.BASE, models.SoftDeleteMixin,
|
||||||
|
models.TimestampMixin):
|
||||||
|
"""Contains all info about vnf LCM Subscriptions."""
|
||||||
|
|
||||||
|
__tablename__ = 'vnf_lcm_subscriptions'
|
||||||
|
id = sa.Column(sa.String(36), nullable=False, primary_key=True)
|
||||||
|
callback_uri = sa.Column(sa.String(255), nullable=False)
|
||||||
|
subscription_authentication = sa.Column(sa.JSON, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class VnfLcmFilters(model_base.BASE):
|
||||||
|
"""Contains all info about vnf LCM filters."""
|
||||||
|
|
||||||
|
__tablename__ = 'vnf_lcm_filters'
|
||||||
|
__maxsize__ = 65536
|
||||||
|
id = sa.Column(sa.Integer, nullable=True, primary_key=True)
|
||||||
|
subscription_uuid = sa.Column(sa.String(36),
|
||||||
|
sa.ForeignKey('vnf_lcm_subscriptions.id'),
|
||||||
|
nullable=False)
|
||||||
|
filter = sa.Column(sa.JSON, nullable=False)
|
||||||
|
notification_types = sa.Column(sa.VARBINARY(255), nullable=True)
|
||||||
|
notification_types_len = sa.Column(sa.Integer, nullable=True)
|
||||||
|
operation_types = sa.Column(
|
||||||
|
sa.LargeBinary(
|
||||||
|
length=__maxsize__),
|
||||||
|
nullable=True)
|
||||||
|
operation_types_len = sa.Column(sa.Integer, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,
|
||||||
|
models.TimestampMixin):
|
||||||
|
"""VNF LCM OP OCCS Fields"""
|
||||||
|
|
||||||
|
__tablename__ = 'vnf_lcm_op_occs'
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||||
|
vnf_instance_id = sa.Column(sa.String(36),
|
||||||
|
sa.ForeignKey('vnf_instances.id'),
|
||||||
|
nullable=False)
|
||||||
|
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)
|
||||||
|
operation = sa.Column(sa.String(length=255), nullable=False)
|
||||||
|
is_automatic_invocation = sa.Column(sa.Boolean, nullable=False)
|
||||||
|
operation_params = sa.Column(sa.JSON(), nullable=True)
|
||||||
|
is_cancel_pending = sa.Column(sa.Boolean(), nullable=False)
|
||||||
|
error = sa.Column(sa.JSON(), nullable=True)
|
||||||
|
resource_changes = sa.Column(sa.JSON(), nullable=True)
|
||||||
|
changed_info = sa.Column(sa.JSON(), nullable=True)
|
||||||
|
error_point = sa.Column(sa.Integer, nullable=False)
|
||||||
|
@ -1 +1 @@
|
|||||||
8a7ca803e0d0
|
c47a733f425a
|
@ -0,0 +1,92 @@
|
|||||||
|
# Copyright 2020 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
# flake8: noqa: E402
|
||||||
|
|
||||||
|
"""add_vnflcm_subscription
|
||||||
|
|
||||||
|
Revision ID: c47a733f425a
|
||||||
|
Revises: 8a7ca803e0d0
|
||||||
|
Create Date: 2020-08-27 14:18:43.907565
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'c47a733f425a'
|
||||||
|
down_revision = '8a7ca803e0d0'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import Boolean
|
||||||
|
|
||||||
|
from tacker.db import types
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(active_plugins=None, options=None):
|
||||||
|
op.create_table(
|
||||||
|
'vnf_lcm_subscriptions',
|
||||||
|
sa.Column('id', types.Uuid(length=36), nullable=False),
|
||||||
|
sa.Column('callback_uri', sa.String(length=255), nullable=False),
|
||||||
|
sa.Column('subscription_authentication', sa.JSON(), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||||
|
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('deleted', Boolean, default=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
mysql_engine='InnoDB'
|
||||||
|
)
|
||||||
|
|
||||||
|
noti_str = "json_unquote(json_extract('filter','$.notificationTypes'))"
|
||||||
|
sta_str = "json_unquote(json_extract('filter','$.operationStates'))"
|
||||||
|
op.create_table(
|
||||||
|
'vnf_lcm_filters',
|
||||||
|
sa.Column('id', sa.Integer, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('subscription_uuid', sa.String(length=36), nullable=False),
|
||||||
|
sa.Column('filter', sa.JSON(), nullable=False),
|
||||||
|
sa.Column('notification_types',
|
||||||
|
sa.LargeBinary(length=65536),
|
||||||
|
sa.Computed(noti_str)),
|
||||||
|
sa.Column('notification_types_len',
|
||||||
|
sa.Integer,
|
||||||
|
sa.Computed("ifnull(json_length('notification_types'),0)")),
|
||||||
|
sa.Column('operation_states',
|
||||||
|
sa.LargeBinary(length=65536),
|
||||||
|
sa.Computed(sta_str)),
|
||||||
|
sa.Column('operation_states_len',
|
||||||
|
sa.Integer,
|
||||||
|
sa.Computed("ifnull(json_length('operation_states'),0)")),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.ForeignKeyConstraint(['subscription_uuid'],
|
||||||
|
['vnf_lcm_subscriptions.id'], ),
|
||||||
|
mysql_engine='InnoDB'
|
||||||
|
)
|
||||||
|
|
||||||
|
op.create_table(
|
||||||
|
'vnf_lcm_op_occs',
|
||||||
|
sa.Column('id', types.Uuid(length=36), nullable=False),
|
||||||
|
sa.Column('operation_state', sa.String(length=255), nullable=False),
|
||||||
|
sa.Column('state_entered_time', sa.DateTime(), nullable=False),
|
||||||
|
sa.Column('start_time', sa.DateTime(), nullable=False),
|
||||||
|
sa.Column('vnf_instance_id', types.Uuid(length=36), nullable=False),
|
||||||
|
sa.Column('operation', sa.String(length=255), nullable=False),
|
||||||
|
sa.Column('is_automatic_invocation', sa.Boolean, nullable=False),
|
||||||
|
sa.Column('operation_params', sa.JSON(), nullable=True),
|
||||||
|
sa.Column('error', sa.JSON(), nullable=True),
|
||||||
|
sa.Column('deleted', Boolean, default=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.ForeignKeyConstraint(['vnf_instance_id'],
|
||||||
|
['vnf_instances.id'], ),
|
||||||
|
mysql_engine='InnoDB'
|
||||||
|
)
|
@ -36,3 +36,4 @@ def register_all():
|
|||||||
__import__('tacker.objects.vnf_resources')
|
__import__('tacker.objects.vnf_resources')
|
||||||
__import__('tacker.objects.terminate_vnf_req')
|
__import__('tacker.objects.terminate_vnf_req')
|
||||||
__import__('tacker.objects.vnf_artifact')
|
__import__('tacker.objects.vnf_artifact')
|
||||||
|
__import__('tacker.objects.vnf_lcm_subscriptions')
|
||||||
|
335
tacker/objects/vnf_lcm_subscriptions.py
Normal file
335
tacker/objects/vnf_lcm_subscriptions.py
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
# 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 oslo_utils import timeutils
|
||||||
|
from sqlalchemy.sql import text
|
||||||
|
|
||||||
|
from tacker.common import exceptions
|
||||||
|
import tacker.conf
|
||||||
|
from tacker.db import api as db_api
|
||||||
|
from tacker.db.db_sqlalchemy import api
|
||||||
|
from tacker.db.db_sqlalchemy import models
|
||||||
|
from tacker.objects import base
|
||||||
|
from tacker.objects import fields
|
||||||
|
|
||||||
|
_NO_DATA_SENTINEL = object()
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
CONF = tacker.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
|
def _make_list(value):
|
||||||
|
if isinstance(value, list):
|
||||||
|
res = ""
|
||||||
|
for i in range(len(value)):
|
||||||
|
t = "\"{}\"".format(value[i])
|
||||||
|
if i == 0:
|
||||||
|
res = str(t)
|
||||||
|
else:
|
||||||
|
res = "{0},{1}".format(res, t)
|
||||||
|
|
||||||
|
res = "[{}]".format(res)
|
||||||
|
else:
|
||||||
|
res = "[\"{}\"]".format(str(value))
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@db_api.context_manager.reader
|
||||||
|
def _vnf_lcm_subscriptions_get(context,
|
||||||
|
notification_type,
|
||||||
|
operation_type=None
|
||||||
|
):
|
||||||
|
|
||||||
|
if notification_type == 'VnfLcmOperationOccurrenceNotification':
|
||||||
|
sql = (
|
||||||
|
"select"
|
||||||
|
" t1.id,t1.callback_uri,t1.subscription_authentication,t2.filter "
|
||||||
|
" from "
|
||||||
|
" vnf_lcm_subscriptions t1, "
|
||||||
|
" (select distinct subscription_uuid,filter from vnf_lcm_filters "
|
||||||
|
" where "
|
||||||
|
" (notification_types_len = 0 \
|
||||||
|
or JSON_CONTAINS(notification_types, '" +
|
||||||
|
_make_list(notification_type) +
|
||||||
|
"')) "
|
||||||
|
" and "
|
||||||
|
" (operation_types_len = 0 or JSON_CONTAINS(operation_types, '" +
|
||||||
|
_make_list(operation_type) +
|
||||||
|
"')) "
|
||||||
|
" order by "
|
||||||
|
" notification_types_len desc,"
|
||||||
|
" operation_types_len desc"
|
||||||
|
") t2 "
|
||||||
|
" where "
|
||||||
|
" t1.id=t2.subscription_uuid "
|
||||||
|
" and t1.deleted=0")
|
||||||
|
else:
|
||||||
|
sql = (
|
||||||
|
"select"
|
||||||
|
" t1.id,t1.callback_uri,t1.subscription_authentication,t2.filter "
|
||||||
|
" from "
|
||||||
|
" vnf_lcm_subscriptions t1, "
|
||||||
|
" (select distinct subscription_uuid,filter from vnf_lcm_filters "
|
||||||
|
" where "
|
||||||
|
" (notification_types_len = 0 or \
|
||||||
|
JSON_CONTAINS(notification_types, '" +
|
||||||
|
_make_list(notification_type) +
|
||||||
|
"')) "
|
||||||
|
" order by "
|
||||||
|
" notification_types_len desc,"
|
||||||
|
" operation_types_len desc"
|
||||||
|
") t2 "
|
||||||
|
" where "
|
||||||
|
" t1.id=t2.subscription_uuid "
|
||||||
|
" and t1.deleted=0")
|
||||||
|
|
||||||
|
result_list = []
|
||||||
|
result = context.session.execute(sql)
|
||||||
|
for line in result:
|
||||||
|
result_list.append(line)
|
||||||
|
return result_list
|
||||||
|
|
||||||
|
|
||||||
|
@db_api.context_manager.reader
|
||||||
|
def _vnf_lcm_subscriptions_show(context, subscriptionId):
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
"select "
|
||||||
|
"t1.id,t1.callback_uri,t2.filter "
|
||||||
|
"from vnf_lcm_subscriptions t1, "
|
||||||
|
"(select distinct subscription_uuid,filter from vnf_lcm_filters) t2 "
|
||||||
|
"where t1.id = t2.subscription_uuid "
|
||||||
|
"and deleted = 0 "
|
||||||
|
"and t1.id = :subsc_id")
|
||||||
|
try:
|
||||||
|
result = context.session.execute(sql, {'subsc_id': subscriptionId})
|
||||||
|
except exceptions.NotFound:
|
||||||
|
return ''
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@db_api.context_manager.reader
|
||||||
|
def _vnf_lcm_subscriptions_all(context):
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
"select "
|
||||||
|
"t1.id,t1.callback_uri,t2.filter "
|
||||||
|
"from vnf_lcm_subscriptions t1, "
|
||||||
|
"(select distinct subscription_uuid,filter from vnf_lcm_filters) t2 "
|
||||||
|
"where t1.id = t2.subscription_uuid "
|
||||||
|
"and deleted = 0 ")
|
||||||
|
result_list = []
|
||||||
|
try:
|
||||||
|
result = context.session.execute(sql)
|
||||||
|
for line in result:
|
||||||
|
result_list.append(line)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return result_list
|
||||||
|
|
||||||
|
|
||||||
|
@db_api.context_manager.reader
|
||||||
|
def _get_by_subscriptionid(context, subscriptionsId):
|
||||||
|
|
||||||
|
sql = text("select id "
|
||||||
|
"from vnf_lcm_subscriptions "
|
||||||
|
"where id = :subsc_id "
|
||||||
|
"and deleted = 0 ")
|
||||||
|
try:
|
||||||
|
result = context.session.execute(sql, {'subsc_id': subscriptionsId})
|
||||||
|
except exceptions.NotFound:
|
||||||
|
return ''
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@db_api.context_manager.reader
|
||||||
|
def _vnf_lcm_subscriptions_id_get(context,
|
||||||
|
callbackUri,
|
||||||
|
notification_type=None,
|
||||||
|
operation_type=None
|
||||||
|
):
|
||||||
|
|
||||||
|
sql = ("select "
|
||||||
|
"t1.id "
|
||||||
|
"from "
|
||||||
|
"vnf_lcm_subscriptions t1, "
|
||||||
|
"(select subscription_uuid from vnf_lcm_filters "
|
||||||
|
"where ")
|
||||||
|
|
||||||
|
if notification_type:
|
||||||
|
sql = (sql + " JSON_CONTAINS(notification_types, '" +
|
||||||
|
_make_list(notification_type) + "') ")
|
||||||
|
else:
|
||||||
|
sql = sql + " notification_types_len=0 "
|
||||||
|
sql = sql + "and "
|
||||||
|
|
||||||
|
if operation_type:
|
||||||
|
sql = sql + " JSON_CONTAINS(operation_types, '" + \
|
||||||
|
_make_list(operation_type) + "') "
|
||||||
|
else:
|
||||||
|
sql = sql + " operation_types_len=0 "
|
||||||
|
sql = (
|
||||||
|
sql +
|
||||||
|
") t2 where t1.id=t2.subscription_uuid and t1.callback_uri= '" +
|
||||||
|
callbackUri +
|
||||||
|
"' and t1.deleted=0 ")
|
||||||
|
LOG.debug("sql[%s]" % sql)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = context.session.execute(sql)
|
||||||
|
return result
|
||||||
|
except exceptions.NotFound:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
def _add_filter_data(context, subscription_id, filter):
|
||||||
|
with db_api.context_manager.writer.using(context):
|
||||||
|
|
||||||
|
new_entries = []
|
||||||
|
new_entries.append({"subscription_uuid": subscription_id,
|
||||||
|
"filter": filter})
|
||||||
|
|
||||||
|
context.session.execute(
|
||||||
|
models.VnfLcmFilters.__table__.insert(None),
|
||||||
|
new_entries)
|
||||||
|
|
||||||
|
|
||||||
|
@db_api.context_manager.writer
|
||||||
|
def _vnf_lcm_subscriptions_create(context, values, filter):
|
||||||
|
with db_api.context_manager.writer.using(context):
|
||||||
|
|
||||||
|
new_entries = []
|
||||||
|
if 'subscription_authentication' in values:
|
||||||
|
new_entries.append({"id": values.id,
|
||||||
|
"callback_uri": values.callback_uri,
|
||||||
|
"subscription_authentication":
|
||||||
|
values.subscription_authentication})
|
||||||
|
else:
|
||||||
|
new_entries.append({"id": values.id,
|
||||||
|
"callback_uri": values.callback_uri})
|
||||||
|
|
||||||
|
context.session.execute(
|
||||||
|
models.VnfLcmSubscriptions.__table__.insert(None),
|
||||||
|
new_entries)
|
||||||
|
|
||||||
|
callbackUri = values.callback_uri
|
||||||
|
if filter:
|
||||||
|
notification_type = filter.get('notificationTypes')
|
||||||
|
operation_type = filter.get('operationTypese')
|
||||||
|
|
||||||
|
vnf_lcm_subscriptions_id = _vnf_lcm_subscriptions_id_get(
|
||||||
|
context,
|
||||||
|
callbackUri,
|
||||||
|
notification_type=notification_type,
|
||||||
|
operation_type=operation_type)
|
||||||
|
|
||||||
|
if vnf_lcm_subscriptions_id:
|
||||||
|
raise Exception("303" + vnf_lcm_subscriptions_id.id.decode())
|
||||||
|
|
||||||
|
_add_filter_data(context, values.id, filter)
|
||||||
|
|
||||||
|
else:
|
||||||
|
vnf_lcm_subscriptions_id = _vnf_lcm_subscriptions_id_get(context,
|
||||||
|
callbackUri)
|
||||||
|
|
||||||
|
if vnf_lcm_subscriptions_id:
|
||||||
|
raise Exception("303" + vnf_lcm_subscriptions_id.id.decode())
|
||||||
|
_add_filter_data(context, values.id, {})
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
@db_api.context_manager.writer
|
||||||
|
def _destroy_vnf_lcm_subscription(context, subscriptionId):
|
||||||
|
now = timeutils.utcnow()
|
||||||
|
updated_values = {'deleted': 1,
|
||||||
|
'deleted_at': now}
|
||||||
|
try:
|
||||||
|
api.model_query(context, models.VnfLcmSubscriptions). \
|
||||||
|
filter_by(id=subscriptionId). \
|
||||||
|
update(updated_values, synchronize_session=False)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
@base.TackerObjectRegistry.register
|
||||||
|
class LccnSubscriptionRequest(base.TackerObject, base.TackerPersistentObject):
|
||||||
|
|
||||||
|
# Version 1.0: Initial version
|
||||||
|
VERSION = '1.0'
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
'id': fields.UUIDField(nullable=False),
|
||||||
|
'callback_uri': fields.StringField(nullable=False),
|
||||||
|
'subscription_authentication':
|
||||||
|
fields.DictOfStringsField(nullable=True),
|
||||||
|
'filter': fields.StringField(nullable=True)
|
||||||
|
}
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def create(self, filter):
|
||||||
|
updates = self.obj_clone()
|
||||||
|
db_vnf_lcm_subscriptions = _vnf_lcm_subscriptions_create(
|
||||||
|
self._context, updates, filter)
|
||||||
|
return db_vnf_lcm_subscriptions
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def vnf_lcm_subscriptions_show(cls, context, subscriptionId):
|
||||||
|
try:
|
||||||
|
vnf_lcm_subscriptions = _vnf_lcm_subscriptions_show(
|
||||||
|
context, subscriptionId)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
return vnf_lcm_subscriptions
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def vnf_lcm_subscriptions_list(cls, context):
|
||||||
|
# get vnf_lcm_subscriptions data
|
||||||
|
try:
|
||||||
|
vnf_lcm_subscriptions = _vnf_lcm_subscriptions_all(context)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return vnf_lcm_subscriptions
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def vnf_lcm_subscriptions_get(cls, context,
|
||||||
|
notification_type,
|
||||||
|
operation_type=None):
|
||||||
|
return _vnf_lcm_subscriptions_get(context,
|
||||||
|
notification_type,
|
||||||
|
operation_type)
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def destroy(cls, context, subscriptionId):
|
||||||
|
try:
|
||||||
|
get_subscriptionid = _get_by_subscriptionid(
|
||||||
|
context, subscriptionId)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
if not get_subscriptionid:
|
||||||
|
return 404
|
||||||
|
|
||||||
|
try:
|
||||||
|
_destroy_vnf_lcm_subscription(context, subscriptionId)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return 204
|
@ -50,6 +50,50 @@ artifacts = {
|
|||||||
'type': 'tosca.artifacts.nfv.SwImage',
|
'type': 'tosca.artifacts.nfv.SwImage',
|
||||||
'algorithm': 'sha512', 'hash': uuidsentinel.hash}
|
'algorithm': 'sha512', 'hash': uuidsentinel.hash}
|
||||||
|
|
||||||
|
filter = {
|
||||||
|
"usageState": ["NOT_IN_USE"],
|
||||||
|
"vnfPkgId": ["f04857cb-abdc-405f-8254-01501f3fa059"],
|
||||||
|
"vnfdId": ["b1bb0ce7-5555-0001-95ed-4840d70a1209"],
|
||||||
|
"vnfProductsFromProviders": [
|
||||||
|
{
|
||||||
|
"vnfProvider": "xxxxx",
|
||||||
|
"vnfProducts": [
|
||||||
|
{
|
||||||
|
"vnfProductName": "artifactVNF",
|
||||||
|
"versions": [
|
||||||
|
{
|
||||||
|
"vnfSoftwareVersion": "1.0",
|
||||||
|
"vnfdVersions": ["v2.2"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vnfProvider": "xxxxx",
|
||||||
|
"vnfProducts": [
|
||||||
|
{
|
||||||
|
"vnfProductName": "artifactVNF",
|
||||||
|
"versions": [
|
||||||
|
{
|
||||||
|
"vnfSoftwareVersion": "1.0",
|
||||||
|
"vnfdVersions": ["v2.2"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"notificationTypes": ["VnfLcmOperationOccurrenceNotification"],
|
||||||
|
"operationalState": ["ENABLED"]
|
||||||
|
}
|
||||||
|
|
||||||
|
subscription_data = {
|
||||||
|
'id': "c3e5ea85-8e3d-42df-a636-3b7857cbd7f9",
|
||||||
|
'callback_uri': "fake_url",
|
||||||
|
'created_at': "2020-06-11 09:39:58"
|
||||||
|
}
|
||||||
|
|
||||||
fake_vnf_package_response = copy.deepcopy(vnf_package_data)
|
fake_vnf_package_response = copy.deepcopy(vnf_package_data)
|
||||||
fake_vnf_package_response.pop('user_data')
|
fake_vnf_package_response.pop('user_data')
|
||||||
fake_vnf_package_response.update({'id': uuidsentinel.package_uuid})
|
fake_vnf_package_response.update({'id': uuidsentinel.package_uuid})
|
||||||
|
309
tacker/tests/unit/objects/test_vnf_lcm_subscriptions.py
Normal file
309
tacker/tests/unit/objects/test_vnf_lcm_subscriptions.py
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
# 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 unittest import mock
|
||||||
|
|
||||||
|
from tacker import context
|
||||||
|
from tacker import objects
|
||||||
|
from tacker.tests.unit.db.base import SqlTestCase
|
||||||
|
from tacker.tests.unit.objects import fakes
|
||||||
|
from tacker.tests import uuidsentinel
|
||||||
|
|
||||||
|
|
||||||
|
class TestVnfd(SqlTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestVnfd, self).setUp()
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
self.vnf_package = self._create_vnf_package()
|
||||||
|
self.vnf_package_vnfd = self._create_and_upload_vnf_package_vnfd()
|
||||||
|
self.subscription = self._create_subscription()
|
||||||
|
|
||||||
|
def _create_vnf_package(self):
|
||||||
|
vnfpkgm = objects.VnfPackage(context=self.context,
|
||||||
|
**fakes.vnf_package_data)
|
||||||
|
vnfpkgm.create()
|
||||||
|
return vnfpkgm
|
||||||
|
|
||||||
|
def _create_and_upload_vnf_package_vnfd(self):
|
||||||
|
vnf_package = objects.VnfPackage(context=self.context,
|
||||||
|
**fakes.vnf_package_data)
|
||||||
|
vnf_package.create()
|
||||||
|
|
||||||
|
vnf_pack_vnfd = fakes.get_vnf_package_vnfd_data(
|
||||||
|
vnf_package.id, uuidsentinel.vnfd_id)
|
||||||
|
|
||||||
|
vnf_pack_vnfd_obj = objects.VnfPackageVnfd(
|
||||||
|
context=self.context, **vnf_pack_vnfd)
|
||||||
|
vnf_pack_vnfd_obj.create()
|
||||||
|
|
||||||
|
return vnf_pack_vnfd_obj
|
||||||
|
|
||||||
|
@mock.patch.object(objects.vnf_lcm_subscriptions,
|
||||||
|
'_vnf_lcm_subscriptions_create')
|
||||||
|
def _create_subscription(self, mock_vnf_lcm_subscriptions_create):
|
||||||
|
filter = fakes.filter
|
||||||
|
mock_vnf_lcm_subscriptions_create.return_value = \
|
||||||
|
'{\
|
||||||
|
"filter": "{"operationStates": ["COMPLETED"],\
|
||||||
|
"vnfInstanceNames": ["xxxxxxxxxxxxxxxxxx"],\
|
||||||
|
"operationTypes": ["INSTANTIATE"],\
|
||||||
|
"vnfdIds": ["405d73c7-e964-4c8b-a914-41478ccd7c42"],\
|
||||||
|
"vnfProductsFromProviders": [{\
|
||||||
|
"vnfProvider": "x2x", \
|
||||||
|
"vnfProducts": [{\
|
||||||
|
"vnfProductName": "x2xx", \
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xx2XX", \
|
||||||
|
"vnfdVersions": ["ss2"]\
|
||||||
|
}]\
|
||||||
|
}]\
|
||||||
|
}, \
|
||||||
|
{\
|
||||||
|
"vnfProvider": "z2z",\
|
||||||
|
"vnfProducts": [{\
|
||||||
|
"vnfProductName": "z2zx", \
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xx3XX",\
|
||||||
|
"vnfdVersions": \
|
||||||
|
["s3sx", "s3sa"]\
|
||||||
|
}\
|
||||||
|
]},\
|
||||||
|
{\
|
||||||
|
"vnfProductName": "zz3ex",\
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xxe3eXz",\
|
||||||
|
"vnfdVersions": ["ss3xz", "s3esaz"]\
|
||||||
|
},\
|
||||||
|
{\
|
||||||
|
"vnfSoftwareVersion": "xxeeeXw", \
|
||||||
|
"vnfdVersions": ["ss3xw", "ss3w"]\
|
||||||
|
}]\
|
||||||
|
}]\
|
||||||
|
}],\
|
||||||
|
"notificationTypes": [\
|
||||||
|
"VnfLcmOperationOccurrenceNotification"],\
|
||||||
|
"vnfInstanceIds": ["fb0b9a12-4b55-47ac-9ca8-5fdd52c4c07f"]}",\
|
||||||
|
"callbackUri": "http://localhost/xxx",\
|
||||||
|
"_links": {\
|
||||||
|
"self": {\
|
||||||
|
"href":\
|
||||||
|
"http://localhost:9890//vnflcm/v1/subscriptions\
|
||||||
|
/530a3c43-043a-4b84-9d65-aa0df49f7ced"\
|
||||||
|
}\
|
||||||
|
},\
|
||||||
|
"id": "530a3c43-043a-4b84-9d65-aa0df49f7ced"\
|
||||||
|
}'
|
||||||
|
|
||||||
|
subscription_obj = \
|
||||||
|
objects.vnf_lcm_subscriptions.LccnSubscriptionRequest(
|
||||||
|
context=self.context, **fakes.subscription_data)
|
||||||
|
subscription_obj.create(filter)
|
||||||
|
|
||||||
|
return subscription_obj
|
||||||
|
|
||||||
|
@mock.patch.object(objects.vnf_lcm_subscriptions,
|
||||||
|
'_vnf_lcm_subscriptions_create')
|
||||||
|
def test_create(self, mock_vnf_lcm_subscriptions_create):
|
||||||
|
filter = fakes.filter
|
||||||
|
subscription_obj = \
|
||||||
|
objects.vnf_lcm_subscriptions.LccnSubscriptionRequest(
|
||||||
|
context=self.context)
|
||||||
|
mock_vnf_lcm_subscriptions_create.return_value = \
|
||||||
|
'{\
|
||||||
|
"filter": "{"operationStates": ["COMPLETED"],\
|
||||||
|
"vnfInstanceNames": ["xxxxxxxxxxxxxxxxxx"],\
|
||||||
|
"operationTypes": ["INSTANTIATE"],\
|
||||||
|
"vnfdIds": ["405d73c7-e964-4c8b-a914-41478ccd7c42"],\
|
||||||
|
"vnfProductsFromProviders": [{\
|
||||||
|
"vnfProvider": "x2x", \
|
||||||
|
"vnfProducts": [{\
|
||||||
|
"vnfProductName": "x2xx", \
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xx2XX", \
|
||||||
|
"vnfdVersions": ["ss2"]\
|
||||||
|
}]\
|
||||||
|
}]\
|
||||||
|
}, \
|
||||||
|
{\
|
||||||
|
"vnfProvider": "z2z",\
|
||||||
|
"vnfProducts": [{\
|
||||||
|
"vnfProductName": "z2zx", \
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xx3XX",\
|
||||||
|
"vnfdVersions": \
|
||||||
|
["s3sx", "s3sa"]\
|
||||||
|
}\
|
||||||
|
]},\
|
||||||
|
{\
|
||||||
|
"vnfProductName": "zz3ex",\
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xxe3eXz",\
|
||||||
|
"vnfdVersions": ["ss3xz", "s3esaz"]\
|
||||||
|
},\
|
||||||
|
{\
|
||||||
|
"vnfSoftwareVersion": "xxeeeXw", \
|
||||||
|
"vnfdVersions": ["ss3xw", "ss3w"]\
|
||||||
|
}]\
|
||||||
|
}]\
|
||||||
|
}],\
|
||||||
|
"notificationTypes": [\
|
||||||
|
"VnfLcmOperationOccurrenceNotification"],\
|
||||||
|
"vnfInstanceIds": ["fb0b9a12-4b55-47ac-9ca8-5fdd52c4c07f"]}",\
|
||||||
|
"callbackUri": "http://localhost/xxx",\
|
||||||
|
"_links": {\
|
||||||
|
"self": {\
|
||||||
|
"href":\
|
||||||
|
"http://localhost:9890//vnflcm/v1/subscriptions\
|
||||||
|
/530a3c43-043a-4b84-9d65-aa0df49f7ced"\
|
||||||
|
}\
|
||||||
|
},\
|
||||||
|
"id": "530a3c43-043a-4b84-9d65-aa0df49f7ced"\
|
||||||
|
}'
|
||||||
|
|
||||||
|
result = subscription_obj.create(filter)
|
||||||
|
self.assertTrue(filter, result)
|
||||||
|
|
||||||
|
@mock.patch.object(objects.vnf_lcm_subscriptions,
|
||||||
|
'_vnf_lcm_subscriptions_show')
|
||||||
|
def test_show(self, mock_vnf_lcm_subscriptions_show):
|
||||||
|
filter = fakes.filter
|
||||||
|
subscription_obj = \
|
||||||
|
objects.vnf_lcm_subscriptions.LccnSubscriptionRequest(
|
||||||
|
context=self.context)
|
||||||
|
mock_vnf_lcm_subscriptions_show.return_value = \
|
||||||
|
'{\
|
||||||
|
"filter": "{"operationStates": ["COMPLETED"],\
|
||||||
|
"vnfInstanceNames": ["xxxxxxxxxxxxxxxxxx"],\
|
||||||
|
"operationTypes": ["INSTANTIATE"],\
|
||||||
|
"vnfdIds": ["405d73c7-e964-4c8b-a914-41478ccd7c42"],\
|
||||||
|
"vnfProductsFromProviders": [{\
|
||||||
|
"vnfProvider": "x2x", \
|
||||||
|
"vnfProducts": [{\
|
||||||
|
"vnfProductName": "x2xx", \
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xx2XX", \
|
||||||
|
"vnfdVersions": ["ss2"]\
|
||||||
|
}]\
|
||||||
|
}]\
|
||||||
|
}, \
|
||||||
|
{\
|
||||||
|
"vnfProvider": "z2z",\
|
||||||
|
"vnfProducts": [{\
|
||||||
|
"vnfProductName": "z2zx", \
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xx3XX",\
|
||||||
|
"vnfdVersions": \
|
||||||
|
["s3sx", "s3sa"]\
|
||||||
|
}\
|
||||||
|
]},\
|
||||||
|
{\
|
||||||
|
"vnfProductName": "zz3ex",\
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xxe3eXz",\
|
||||||
|
"vnfdVersions": ["ss3xz", "s3esaz"]\
|
||||||
|
},\
|
||||||
|
{\
|
||||||
|
"vnfSoftwareVersion": "xxeeeXw", \
|
||||||
|
"vnfdVersions": ["ss3xw", "ss3w"]\
|
||||||
|
}]\
|
||||||
|
}]\
|
||||||
|
}],\
|
||||||
|
"notificationTypes": [\
|
||||||
|
"VnfLcmOperationOccurrenceNotification"],\
|
||||||
|
"vnfInstanceIds": ["fb0b9a12-4b55-47ac-9ca8-5fdd52c4c07f"]}",\
|
||||||
|
"callbackUri": "http://localhost/xxx",\
|
||||||
|
"_links": {\
|
||||||
|
"self": {\
|
||||||
|
"href":\
|
||||||
|
"http://localhost:9890//vnflcm/v1/subscriptions\
|
||||||
|
/530a3c43-043a-4b84-9d65-aa0df49f7ced"\
|
||||||
|
}\
|
||||||
|
},\
|
||||||
|
"id": "530a3c43-043a-4b84-9d65-aa0df49f7ced"\
|
||||||
|
}'
|
||||||
|
|
||||||
|
result = subscription_obj.vnf_lcm_subscriptions_show(
|
||||||
|
self.context, self.subscription.id)
|
||||||
|
self.assertTrue(filter, result)
|
||||||
|
|
||||||
|
@mock.patch.object(objects.vnf_lcm_subscriptions,
|
||||||
|
'_vnf_lcm_subscriptions_all')
|
||||||
|
def test_list(self, mock_vnf_lcm_subscriptions_all):
|
||||||
|
filter = fakes.filter
|
||||||
|
subscription_obj = \
|
||||||
|
objects.vnf_lcm_subscriptions.LccnSubscriptionRequest(
|
||||||
|
context=self.context)
|
||||||
|
mock_vnf_lcm_subscriptions_all.return_value = \
|
||||||
|
'{\
|
||||||
|
"filter": "{"operationStates": ["COMPLETED"],\
|
||||||
|
"vnfInstanceNames": ["xxxxxxxxxxxxxxxxxx"],\
|
||||||
|
"operationTypes": ["INSTANTIATE"],\
|
||||||
|
"vnfdIds": ["405d73c7-e964-4c8b-a914-41478ccd7c42"],\
|
||||||
|
"vnfProductsFromProviders": [{\
|
||||||
|
"vnfProvider": "x2x", \
|
||||||
|
"vnfProducts": [{\
|
||||||
|
"vnfProductName": "x2xx", \
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xx2XX", \
|
||||||
|
"vnfdVersions": ["ss2"]\
|
||||||
|
}]\
|
||||||
|
}]\
|
||||||
|
}, \
|
||||||
|
{\
|
||||||
|
"vnfProvider": "z2z",\
|
||||||
|
"vnfProducts": [{\
|
||||||
|
"vnfProductName": "z2zx", \
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xx3XX",\
|
||||||
|
"vnfdVersions": \
|
||||||
|
["s3sx", "s3sa"]\
|
||||||
|
}\
|
||||||
|
]},\
|
||||||
|
{\
|
||||||
|
"vnfProductName": "zz3ex",\
|
||||||
|
"versions": [{\
|
||||||
|
"vnfSoftwareVersion": "xxe3eXz",\
|
||||||
|
"vnfdVersions": ["ss3xz", "s3esaz"]\
|
||||||
|
},\
|
||||||
|
{\
|
||||||
|
"vnfSoftwareVersion": "xxeeeXw", \
|
||||||
|
"vnfdVersions": ["ss3xw", "ss3w"]\
|
||||||
|
}]\
|
||||||
|
}]\
|
||||||
|
}],\
|
||||||
|
"notificationTypes": [\
|
||||||
|
"VnfLcmOperationOccurrenceNotification"],\
|
||||||
|
"vnfInstanceIds": ["fb0b9a12-4b55-47ac-9ca8-5fdd52c4c07f"]}",\
|
||||||
|
"callbackUri": "http://localhost/xxx",\
|
||||||
|
"_links": {\
|
||||||
|
"self": {\
|
||||||
|
"href":\
|
||||||
|
"http://localhost:9890//vnflcm/v1/subscriptions\
|
||||||
|
/530a3c43-043a-4b84-9d65-aa0df49f7ced"\
|
||||||
|
}\
|
||||||
|
},\
|
||||||
|
"id": "530a3c43-043a-4b84-9d65-aa0df49f7ced"\
|
||||||
|
}'
|
||||||
|
|
||||||
|
result = subscription_obj.vnf_lcm_subscriptions_list(self.context)
|
||||||
|
self.assertTrue(filter, result)
|
||||||
|
|
||||||
|
@mock.patch.object(objects.vnf_lcm_subscriptions,
|
||||||
|
'_destroy_vnf_lcm_subscription')
|
||||||
|
@mock.patch.object(objects.vnf_lcm_subscriptions, '_get_by_subscriptionid')
|
||||||
|
def test_destroy(self, mock_get_by_subscriptionid,
|
||||||
|
mock_vnf_lcm_subscriptions_destroy):
|
||||||
|
mock_get_by_subscriptionid.result_value = "OK"
|
||||||
|
self.subscription.destroy(self.context, self.subscription.id)
|
||||||
|
mock_vnf_lcm_subscriptions_destroy.assert_called_with(
|
||||||
|
self.context, self.subscription.id)
|
Loading…
x
Reference in New Issue
Block a user