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
|
||||
|
||||
subscription_id:
|
||||
description: |
|
||||
Identifier of the subscription.
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
vnf_instance_id:
|
||||
description: |
|
||||
Identifier of the VNF instance.
|
||||
@ -7,6 +14,104 @@ vnf_instance_id:
|
||||
type: string
|
||||
|
||||
# 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:
|
||||
description: |
|
||||
Indicates the reason why a healing procedure is required.
|
||||
@ -222,6 +327,40 @@ ext_virtual_links_resource_id:
|
||||
in: body
|
||||
required: true
|
||||
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:
|
||||
description: |
|
||||
Fixed addresses to assign (from the subnet defined by "subnetId"
|
||||
@ -408,6 +547,12 @@ subnet_id:
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
subscription_id_response:
|
||||
description: |
|
||||
Identifier of this subscription resource.
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
termination_type:
|
||||
description: |
|
||||
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
|
||||
usually contains a 'location' value where requesters can check to track
|
||||
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 #
|
||||
|
@ -569,3 +569,192 @@ Response Example
|
||||
|
||||
.. literalinclude:: samples/vnflcm/list-vnf-instance-response.json
|
||||
: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,
|
||||
}
|
||||
|
||||
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
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
|
||||
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 fields
|
||||
from tacker.objects import vnf_instance as _vnf_instance
|
||||
|
||||
CONF = tacker.conf.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ViewBuilder(base.BaseViewBuilder):
|
||||
|
||||
@ -108,3 +117,113 @@ class ViewBuilder(base.BaseViewBuilder):
|
||||
def index(self, vnf_instances):
|
||||
return [self._get_vnf_instance_info(vnf_instance)
|
||||
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
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
import ast
|
||||
import json
|
||||
import re
|
||||
import traceback
|
||||
|
||||
import six
|
||||
from six.moves import http_client
|
||||
import webob
|
||||
|
||||
from urllib import parse
|
||||
|
||||
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.common import exceptions
|
||||
from tacker.common import utils
|
||||
from tacker.conductor.conductorrpc import vnf_lcm_rpc
|
||||
import tacker.conf
|
||||
from tacker.extensions import nfvo
|
||||
from tacker import manager
|
||||
from tacker import objects
|
||||
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.vnfm import vim_client
|
||||
from tacker import wsgi
|
||||
|
||||
CONF = tacker.conf.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def check_vnf_state(action, instantiation_state=None, task_state=(None,)):
|
||||
"""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):
|
||||
|
||||
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
|
||||
|
||||
def __init__(self):
|
||||
super(VnfLcmController, self).__init__()
|
||||
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
|
||||
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
|
||||
|
||||
def _get_vnf_instance_href(self, vnf_instance):
|
||||
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)
|
||||
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():
|
||||
return wsgi.Resource(VnfLcmController())
|
||||
|
@ -90,3 +90,11 @@ class VnflcmAPIRouter(wsgi.Router):
|
||||
self._setup_route(mapper,
|
||||
"/vnf_instances/{id}/heal",
|
||||
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):
|
||||
msg_fmt = _("User data for VNF package %(id)s cannot be updated "
|
||||
"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 coordination
|
||||
from tacker.conf import vnf_lcm
|
||||
from tacker.conf import vnf_package
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
|
||||
|
||||
vnf_package.register_opts(CONF)
|
||||
vnf_lcm.register_opts(CONF)
|
||||
conductor.register_opts(CONF)
|
||||
coordination.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_identifier = 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.terminate_vnf_req')
|
||||
__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
|
||||
|
||||
|