e1a44eee6abf3ba732c4db1e8c161800708a9457
[vfc/nfvo/lcm.git] / lcm / ns / biz / create_subscription.py
1 # Copyright (c) 2019, ZTE Corporation.
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6
7 # http://www.apache.org/licenses/LICENSE-2.0
8
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import ast
16 import json
17 import logging
18 import requests
19 import uuid
20 from collections import Counter
21
22 from rest_framework import status
23
24 from lcm.ns.enum import NOTIFICATION_TYPE, AUTH_TYPE
25 from lcm.pub.database.models import SubscriptionModel
26 from lcm.pub.exceptions import NSLCMException, SeeOtherException
27 from lcm.pub.utils.values import ignore_case_get
28 from lcm.ns import const
29
30 logger = logging.getLogger(__name__)
31
32
33 def is_filter_type_equal(new_filter, existing_filter):
34     return Counter(new_filter) == Counter(existing_filter)
35
36
37 FILTER_TYPE = [
38     "operation_types",
39     "ns_component_types",
40     "lcm_opname_impacting_nscomponent",
41     "lcm_opoccstatus_impacting_nscomponent",
42     "notification_types",
43     "operation_states"
44 ]
45
46 NS_FILTER_TYPE = [
47     "nsdIds",
48     "nsInstanceIds",
49     "vnfdIds",
50     "pnfdIds",
51     "nsInstanceNames"
52 ]
53
54
55 class CreateSubscription:
56
57     def __init__(self, data):
58         self.data = data
59         self.filter = ignore_case_get(self.data, "filter", {})
60         self.callback_uri = ignore_case_get(self.data, "callbackUri")
61         self.authentication = ignore_case_get(self.data, "authentication", {})
62         self.notification_types = ignore_case_get(self.filter, "notificationTypes", [])
63         self.operation_types = ignore_case_get(self.filter, "operationTypes", [])
64         self.operation_states = ignore_case_get(self.filter, "notificationStates", [])
65         self.ns_component_types = ignore_case_get(self.filter, "nsComponentTypes", [])
66         self.lcm_opname_impacting_nscomponent = ignore_case_get(
67             self.filter,
68             "lcmOpNameImpactingNsComponent",
69             []
70         )
71         self.lcm_opoccstatus_impacting_nscomponent = ignore_case_get(
72             self.filter,
73             "lcmOpOccStatusImpactingNsComponent",
74             []
75         )
76         self.ns_filter = ignore_case_get(
77             self.filter,
78             "nsInstanceSubscriptionFilter",
79             {}
80         )
81
82     def check_callback_uri(self):
83         logger.debug("SubscribeNotification-post::> Sending GET request to %s" % self.callback_uri)
84         try:
85             response = requests.get(self.callback_uri, timeout=2)
86             if response.status_code != status.HTTP_204_NO_CONTENT:
87                 raise NSLCMException("callbackUri %s returns %s status code." % (
88                     self.callback_uri,
89                     response.status_code
90                 ))
91         except Exception:
92             raise NSLCMException("callbackUri %s didn't return 204 status code." % self.callback_uri)
93
94     def do_biz(self):
95         self.subscription_id = str(uuid.uuid4())
96         # self.check_callback_uri()
97         self.check_valid_auth_info()
98         self.check_filter_types()
99         self.check_valid()
100         self.save_db()
101         subscription = SubscriptionModel.objects.get(subscription_id=self.subscription_id)
102         return subscription
103
104     def check_filter_types(self):
105         logger.debug("SubscribeNotification--post::> Validating operationTypes and operationStates if exists")
106         occ_notification = NOTIFICATION_TYPE.NSLCM_OPERATION_OCCURRENCE_NOTIFICATION
107         if self.operation_types and occ_notification not in self.notification_types:
108             except_message = "If you are setting operationTypes, notificationTypes must be %s"
109             raise NSLCMException(except_message % occ_notification)
110         if self.operation_states and occ_notification not in self.notification_types:
111             except_message = "If you are setting operationStates, notificationTypes must be %s"
112             raise NSLCMException(except_message % occ_notification)
113
114     def check_valid_auth_info(self):
115         logger.debug("SubscribeNotification--post::> Validating Auth details if provided")
116         auth_type = self.authentication.get("authType")
117         params_basic = self.authentication.get("paramsBasic")
118         params_oauth2 = self.authentication.get("paramsOauth2ClientCredentials")
119         if params_basic and AUTH_TYPE.BASIC not in auth_type:
120             raise NSLCMException('Auth type should be ' + AUTH_TYPE.BASIC)
121         if params_oauth2 and AUTH_TYPE.OAUTH2_CLIENT_CREDENTIALS not in auth_type:
122             raise NSLCMException('Auth type should be ' + AUTH_TYPE.OAUTH2_CLIENT_CREDENTIALS)
123
124     def check_filter_exists(self, sub):
125         # Check the notificationTypes, operationTypes, operationStates
126         for filter_type in FILTER_TYPE:
127             if not is_filter_type_equal(
128                 getattr(self, filter_type),
129                 ast.literal_eval(getattr(sub, filter_type))
130             ):
131                 return False
132         # If all the above types are same then check ns instance filters
133         ns_filter = json.loads(sub.ns_instance_filter)
134         for ns_filter_type in NS_FILTER_TYPE:
135             if not is_filter_type_equal(
136                 self.ns_filter.get(ns_filter_type, []),
137                 ns_filter.get(ns_filter_type, [])
138             ):
139                 return False
140         return True
141
142     def check_valid(self):
143         logger.debug("SubscribeNotification--post::> Checking DB if callbackUri already exists")
144         subscriptions = SubscriptionModel.objects.filter(callback_uri=self.callback_uri)
145         if not subscriptions.exists():
146             return True
147         for subscription in subscriptions:
148             if self.check_filter_exists(subscription):
149                 raise SeeOtherException("Already Subscription exists with the same callbackUri and filter")
150         return False
151
152     def save_db(self):
153         logger.debug("SubscribeNotification--post::> Saving the subscription(%s) to the database" % self.subscription_id)
154         links = {
155             "self": {
156                 "href": const.SUBSCRIPTION_ROOT_URI % self.subscription_id
157             }
158         }
159         SubscriptionModel.objects.create(
160             subscription_id=self.subscription_id,
161             callback_uri=self.callback_uri,
162             auth_info=self.authentication,
163             notification_types=json.dumps(self.notification_types),
164             operation_types=json.dumps(self.operation_types),
165             operation_states=json.dumps(self.operation_states),
166             ns_instance_filter=json.dumps(self.ns_filter),
167             ns_component_types=json.dumps(self.ns_component_types),
168             lcm_opname_impacting_nscomponent=json.dumps(self.lcm_opname_impacting_nscomponent),
169             lcm_opoccstatus_impacting_nscomponent=json.dumps(self.lcm_opoccstatus_impacting_nscomponent),
170             links=json.dumps(links))
171         logger.debug('Create Subscription[%s] success', self.subscription_id)