560940307fc64d531dc425c9213a49ae853403f4
[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     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_valid_auth_info()
95         self.check_callbackuri_connection()
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 const.BASIC not in self.authentication.get("authType"):
108             raise VnfPkgSubscriptionException('Auth type should be ' + const.BASIC)
109         if self.authentication.get("paramsOauth2ClientCredentials", {}) \
110                 and const.OAUTH2_CLIENT_CREDENTIALS not in self.authentication.get("authType"):
111             raise VnfPkgSubscriptionException('Auth type should be ' + const.OAUTH2_CLIENT_CREDENTIALS)
112
113     def check_filter_exists(self, sub):
114         # Check the usage states, operationStates
115         for filter_type in ["operation_states", "usage_states"]:
116             if not is_filter_type_equal(getattr(self, filter_type),
117                                         ast.literal_eval(getattr(sub, filter_type))):
118                 return False
119         # If all the above types are same then check id filter
120         for id_filter in ["vnfd_id", "vnf_pkg_id"]:
121             if not is_filter_type_equal(getattr(self, id_filter),
122                                         ast.literal_eval(getattr(sub, id_filter))):
123                 return False
124         return True
125
126     def check_valid(self):
127         links = ""
128         logger.debug("SubscribeNotification--post::> Checking DB if "
129                      "callbackUri already exists")
130         subscriptions = VnfPkgSubscriptionModel.objects.filter(callback_uri=self.callback_uri)
131         for subscription in subscriptions:
132             if self.check_filter_exists(subscription):
133                 links = json.loads(subscription.links)
134                 logger.error("Subscriptions has already exists with the same callbackUri and filter:%s" % links)
135                 raise VnfPkgDuplicateSubscriptionException(
136                     "/%s" % (links["self"]["href"]))
137
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     def query_multi_subscriptions(self, params):
164         query_data = {}
165         logger.debug("QuerySubscription--get--multi--subscriptions--biz::> Check "
166                      "for filter in query params %s" % params)
167         for query, value in list(params.items()):
168             if query in ROOT_FILTERS:
169                 query_data[ROOT_FILTERS[query] + '__icontains'] = value
170         # Query the database with filter if the request has fields in request params, else fetch all records
171         if query_data:
172             subscriptions = VnfPkgSubscriptionModel.objects.filter(**query_data)
173         else:
174             subscriptions = VnfPkgSubscriptionModel.objects.all()
175         if not subscriptions.exists():
176             return []
177         return [subscription.toDict() for subscription in subscriptions]
178
179     def query_single_subscription(self, subscription_id):
180         logger.debug("QuerySingleSubscriptions--get--single--subscription--biz::> "
181                      "ID: %s" % subscription_id)
182
183         subscription = VnfPkgSubscriptionModel.objects.filter(
184             subscription_id=subscription_id)
185         if not subscription.exists():
186             raise SubscriptionDoesNotExistsException("Subscription with ID: %s "
187                                                      "does not exist" % subscription_id)
188         return subscription[0].toDict()
189
190
191 class TerminateSubscription(object):
192     def terminate(self, subscription_id):
193         logger.debug("TerminateSubscriptions--delete--biz::> "
194                      "ID: %s" % subscription_id)
195
196         subscription = VnfPkgSubscriptionModel.objects.filter(
197             subscription_id=subscription_id)
198         if not subscription.exists():
199             raise SubscriptionDoesNotExistsException("Subscription with ID: %s "
200                                                      "does not exist" % subscription_id)
201         subscription[0].delete()