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