Add log and comment
[modeling/etsicatalog.git] / catalog / packages / biz / vnf_pkg_subscription.py
1 # Copyright (C) 2019 Verizon. All Rights Reserved
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 os
19 import uuid
20 from collections import Counter
21
22 import requests
23 from requests.auth import HTTPBasicAuth
24 from rest_framework import status
25
26 from catalog.packages import const
27 from catalog.pub.database.models import VnfPkgSubscriptionModel
28 from catalog.pub.exceptions import VnfPkgSubscriptionException, \
29     VnfPkgDuplicateSubscriptionException, SubscriptionDoesNotExistsException
30 from catalog.pub.utils.values import ignore_case_get
31
32 logger = logging.getLogger(__name__)
33
34 ROOT_FILTERS = {
35     "notificationTypes": "notification_types",
36     "vnfdId": "vnfd_id",
37     "vnfPkgId": "vnf_pkg_id",
38     "operationalState": "operation_states",
39     "usageState": "usage_states"
40 }
41
42
43 def is_filter_type_equal(new_filter, existing_filter):
44     return Counter(new_filter) == Counter(existing_filter)
45
46
47 class CreateSubscription(object):
48     """
49     Create subscription info
50     """
51     def __init__(self, data):
52         self.data = data
53         self.filter = ignore_case_get(self.data, "filter", {})
54         self.callback_uri = ignore_case_get(self.data, "callbackUri")
55         self.authentication = ignore_case_get(self.data, "authentication", {})
56         self.notification_types = ignore_case_get(self.filter, "notificationTypes", [])
57         self.operation_states = ignore_case_get(self.filter, "operationalState", [])
58         self.usage_states = ignore_case_get(self.filter, "usageState", [])
59         self.vnfd_id = ignore_case_get(self.filter, "vnfdId", [])
60         self.vnf_pkg_id = ignore_case_get(self.filter, "vnfPkgId", [])
61         self.vnf_products_from_provider = \
62             ignore_case_get(self.filter, "vnfProductsFromProviders", [])
63
64     def check_callbackuri_connection(self):
65         """
66         Check if the callback uri can access
67         :return:
68         """
69         logger.debug("SubscribeNotification-post::> Sending GET request "
70                      "to %s" % self.callback_uri)
71         try:
72             if self.authentication:
73                 if const.BASIC in self.authentication.get("authType", ''):
74                     params = self.authentication.get("paramsBasic", {})
75                     username = params.get("userName")
76                     password = params.get("password")
77                     response = requests.get(self.callback_uri, auth=HTTPBasicAuth(username, password), timeout=2,
78                                             verify=False)
79                 elif const.OAUTH2_CLIENT_CREDENTIALS in self.authentication.get("authType", ''):
80                     # todo
81                     pass
82                 else:
83                     # todo
84                     pass
85             else:
86                 response = requests.get(self.callback_uri, timeout=2, verify=False)
87             if response.status_code != status.HTTP_204_NO_CONTENT:
88                 raise VnfPkgSubscriptionException(
89                     "callbackUri %s returns %s status code." % (
90                         self.callback_uri,
91                         response.status_code
92                     )
93                 )
94         except Exception:
95             raise VnfPkgSubscriptionException(
96                 "callbackUri %s didn't return 204 status code." % self.callback_uri
97             )
98
99     def do_biz(self):
100         """
101         Do business
102         :return:
103         """
104         self.subscription_id = str(uuid.uuid4())
105         self.check_valid_auth_info()
106         self.check_callbackuri_connection()
107         self.check_valid()
108         self.save_db()
109         subscription = VnfPkgSubscriptionModel.objects.get(
110             subscription_id=self.subscription_id
111         )
112         if subscription:
113             return subscription.toDict()
114
115     def check_valid_auth_info(self):
116         """
117         Check if the Auth info is valid
118         :return:
119         """
120         logger.debug("SubscribeNotification--post::> Validating Auth "
121                      "details if provided")
122         if self.authentication.get("paramsBasic", {}) and const.BASIC not in self.authentication.get("authType"):
123             raise VnfPkgSubscriptionException('Auth type should be ' + const.BASIC)
124         if self.authentication.get("paramsOauth2ClientCredentials", {}) \
125                 and const.OAUTH2_CLIENT_CREDENTIALS not in self.authentication.get("authType"):
126             raise VnfPkgSubscriptionException('Auth type should be ' + const.OAUTH2_CLIENT_CREDENTIALS)
127
128     def check_filter_exists(self, sub):
129         # Check the usage states, operationStates
130         for filter_type in ["operation_states", "usage_states"]:
131             if not is_filter_type_equal(getattr(self, filter_type),
132                                         ast.literal_eval(getattr(sub, filter_type))):
133                 return False
134         # If all the above types are same then check id filter
135         for id_filter in ["vnfd_id", "vnf_pkg_id"]:
136             if not is_filter_type_equal(getattr(self, id_filter),
137                                         ast.literal_eval(getattr(sub, id_filter))):
138                 return False
139         return True
140
141     def check_valid(self):
142         links = ""
143         logger.debug("SubscribeNotification--post::> Checking DB if "
144                      "callbackUri already exists")
145         subscriptions = VnfPkgSubscriptionModel.objects.filter(callback_uri=self.callback_uri)
146         for subscription in subscriptions:
147             if self.check_filter_exists(subscription):
148                 links = json.loads(subscription.links)
149                 logger.error("Subscriptions has already exists with the same callbackUri and filter:%s" % links)
150                 raise VnfPkgDuplicateSubscriptionException(
151                     "/%s" % (links["self"]["href"]))
152
153         return True
154
155     def save_db(self):
156         """
157         Save the subscription info to DB
158         :return:
159         """
160         logger.debug("SubscribeNotification--post::> Saving the subscription "
161                      "%s to the database" % self.subscription_id)
162         links = {
163             "self": {
164                 "href": os.path.join(const.VNFPKG_SUBSCRIPTION_ROOT_URI, self.subscription_id)
165             }
166         }
167         VnfPkgSubscriptionModel.objects.create(
168             subscription_id=self.subscription_id,
169             callback_uri=self.callback_uri,
170             notification_types=json.dumps(self.notification_types),
171             auth_info=json.dumps(self.authentication),
172             usage_states=json.dumps(self.usage_states),
173             operation_states=json.dumps(self.operation_states),
174             vnf_products_from_provider=json.dumps(self.vnf_products_from_provider),
175             vnfd_id=json.dumps(self.vnfd_id),
176             vnf_pkg_id=json.dumps(self.vnf_pkg_id),
177             links=json.dumps(links))
178         logger.debug('Create Subscription[%s] success', self.subscription_id)
179
180
181 class QuerySubscription(object):
182     """
183     The class for query subscription
184     """
185     def query_multi_subscriptions(self, params):
186         """
187         Query subscriptions
188         :param params:
189         :return:
190         """
191         query_data = {}
192         logger.debug("QuerySubscription--get--multi--subscriptions--biz::> Check "
193                      "for filter in query params %s" % params)
194         for query, value in list(params.items()):
195             if query in ROOT_FILTERS:
196                 query_data[ROOT_FILTERS[query] + '__icontains'] = value
197         # Query the database with filter if the request has fields in request params, else fetch all records
198         if query_data:
199             subscriptions = VnfPkgSubscriptionModel.objects.filter(**query_data)
200         else:
201             subscriptions = VnfPkgSubscriptionModel.objects.all()
202         if not subscriptions.exists():
203             return []
204         return [subscription.toDict() for subscription in subscriptions]
205
206     def query_single_subscription(self, subscription_id):
207         """
208         Query subscription by id
209         :param subscription_id:
210         :return:
211         """
212         logger.debug("QuerySingleSubscriptions--get--single--subscription--biz::> "
213                      "ID: %s" % subscription_id)
214
215         subscription = VnfPkgSubscriptionModel.objects.filter(
216             subscription_id=subscription_id)
217         if not subscription.exists():
218             raise SubscriptionDoesNotExistsException("Subscription with ID: %s "
219                                                      "does not exist" % subscription_id)
220         return subscription[0].toDict()
221
222
223 class TerminateSubscription(object):
224     """
225     The class to terminate the subscription
226     """
227     def terminate(self, subscription_id):
228         logger.debug("TerminateSubscriptions--delete--biz::> "
229                      "ID: %s" % subscription_id)
230
231         subscription = VnfPkgSubscriptionModel.objects.filter(
232             subscription_id=subscription_id)
233         if not subscription.exists():
234             raise SubscriptionDoesNotExistsException("Subscription with ID: %s "
235                                                      "does not exist" % subscription_id)
236         subscription[0].delete()