Merge "Fix "brain-overload" functions flagged by SONAR"
[optf/osdf.git] / osdf / adapters / policy / interface.py
1  # -------------------------------------------------------------------------
2 #   Copyright (c) 2015-2017 AT&T Intellectual Property
3 #
4 #   Licensed under the Apache License, Version 2.0 (the "License");
5 #   you may not use this file except in compliance with the License.
6 #   You may obtain a copy of the License at
7 #
8 #       http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #   Unless required by applicable law or agreed to in writing, software
11 #   distributed under the License is distributed on an "AS IS" BASIS,
12 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 #   See the License for the specific language governing permissions and
14 #   limitations under the License.
15 #
16 # -------------------------------------------------------------------------
17 #
18
19 import base64
20 import itertools
21 import json
22
23
24 from requests import RequestException
25 from osdf.operation.exceptions import BusinessException
26 from osdf.adapters.local_data.local_policies import get_local_policies
27 from osdf.adapters.policy.utils import policy_name_as_regex, retrieve_node
28 from osdf.config.base import osdf_config
29 from osdf.logging.osdf_logging import audit_log, MH, metrics_log, debug_log
30 from osdf.utils.interfaces import RestClient
31
32
33 def get_by_name(rest_client, policy_name_list, wildcards=True):
34     policy_list = []
35     for policy_name in policy_name_list:
36         try:
37             query_name = policy_name
38             if wildcards:
39                 query_name = policy_name_as_regex(query_name)
40             policy_list.append(rest_client.request(json={"policyName": query_name}))
41         except RequestException as err:
42             audit_log.warn("Error in fetching policy: " + policy_name)
43             raise BusinessException("Cannot fetch policy {}: ".format(policy_name), err)
44     return policy_list
45
46
47 def get_subscriber_name(req, pmain):
48     subs_name = retrieve_node(req, pmain['subscriber_name'])
49     if subs_name is None:
50         return "DEFAULT"
51     else:
52         subs_name_uc = subs_name.upper()
53         if subs_name_uc in ("DEFAULT", "NULL", ""):
54             subs_name = "DEFAULT"
55     return subs_name
56
57
58 def get_subscriber_role(rest_client, req, pmain, service_name, scope):
59     """Make a request to policy and return subscriberRole
60     :param rest_client: rest client to make call
61     :param req: request object from MSO
62     :param pmain: main config that will have policy path information
63     :param service_name: the type of service to call: e.g. "vCPE
64     :param scope: the scope of policy to call: e.g. "OOF_HAS_vCPE".
65     :return: subscriberRole and provStatus retrieved from Subscriber policy
66     """
67     subscriber_role = "DEFAULT"
68     prov_status = []
69     subs_name = get_subscriber_name(req, pmain)  # what if there is no subs_name
70     if subs_name == "DEFAULT":
71         return subscriber_role, prov_status
72
73     policy_subs = pmain['policy_subscriber']
74     policy_scope = {"policyName": "{}.*".format(scope),
75                     "configAttributes": {
76                         "serviceType": "{}".format(service_name),
77                         "service": "{}".format(policy_subs)}
78                     }
79     try:
80         policy_list = rest_client.request(json=policy_scope)
81     except RequestException as err:
82         audit_log.warn("Error in fetching policy for {}, {}: ".format(policy_subs, err))
83         return subscriber_role, prov_status
84
85     policies = list(itertools.chain(*policy_list))
86     for x in policies:  
87         if not x['config']:  # some policy has no 'config' field, so it will be empty
88             raise BusinessException("Config not found for policy with name %s" % x['policyName'])
89
90     formatted_policies = [json.loads(x['config']) for x in policies]
91     role, prov = _get_subscriber_role_from_policies(formatted_policies, subs_name, subscriber_role, prov_status)
92     return role, prov
93
94
95 def _get_subscriber_role_from_policies(policies, subs_name, default_role, default_prov):
96     """
97     Get the first subscriber role found in policies
98     :param policies: JSON-loaded policies
99     :param subs_name: subscriber name
100     :param default_val: default role (e.g. "DEFAULT")
101     :param default_prov: default prov_status (e.g. [])
102     :return: role and prov_status
103     """
104     for policy in policies:
105         property_list = policy['content']['property']
106         for prop in property_list:
107             if subs_name in prop['subscriberName']:
108                 subs_role_list = prop['subscriberRole']
109                 prov_status = prop['provStatus']
110                 if isinstance(subs_role_list, list):
111                     return subs_role_list[0], prov_status   # TODO: check what to do otherwise
112     return default_role, default_prov
113
114
115 def get_by_scope(rest_client, req, config_local, type_service):
116     policy_list = []
117     pmain = config_local['policy_info'][type_service]
118     pscope = pmain['policy_scope']
119
120     model_name = retrieve_node(req, pscope['service_name'])
121     service_name = model_name
122
123     scope = pscope['scope_{}'.format(service_name.lower())]
124     subscriber_role, prov_status = get_subscriber_role(rest_client, req, pmain, service_name, scope)
125     policy_type_list = pmain['policy_type_{}'.format(service_name.lower())]
126     for policy_type in policy_type_list:
127         policy_scope = {"policyName": "{}.*".format(scope),
128                         "configAttributes": {
129                             "serviceType": "{}".format(service_name),
130                             "service": "{}".format(policy_type),
131                             "subscriberRole": "{}".format(subscriber_role)}
132                         }
133         policy_list.append(rest_client.request(json=policy_scope))
134     return policy_list, prov_status
135
136
137 def remote_api(req_json, osdf_config, service_type="placement"):
138     """Make a request to policy and return response -- it accounts for multiple requests that be needed
139     :param req_json: policy request object (can have multiple policy names)
140     :param osdf_config: main config that will have credential information
141     :param service_type: the type of service to call: "placement", "scheduling"
142     :return: all related policies and provStatus retrieved from Subscriber policy
143     """
144     prov_status = None
145     config = osdf_config.deployment
146     uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword']
147     pcuid, pcpasswd = config['policyClientUsername'], config['policyClientPassword']
148     headers = {"ClientAuth": base64.b64encode(bytes("{}:{}".format(pcuid, pcpasswd), "ascii"))}
149     headers.update({'Environment': config['policyPlatformEnv']})
150     url = config['policyPlatformUrl']
151     rc = RestClient(userid=uid, passwd=passwd, headers=headers, url=url, log_func=debug_log.debug)
152
153     if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
154         policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=True)
155     elif osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name_no_wildcards":
156         policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=False)
157     else:  # Get policy by scope
158         policies, prov_status = get_by_scope(rc, req_json, osdf_config.core, service_type)
159
160     # policies in res are list of lists, so flatten them; also only keep config part
161     formatted_policies = []
162     for x in itertools.chain(*policies):
163         if x['config'] is None:
164             raise BusinessException("Config not found for policy with name %s" % x['policyName'])
165         else:
166             formatted_policies.append(json.loads(x['config']))
167     return formatted_policies, prov_status
168
169
170 def local_policies_location(req_json, osdf_config, service_type):
171     """
172     Get folder and list of policy_files if "local policies" option is enabled
173     :param service_type: placement supported for now, but can be any other service
174     :return: a tuple (folder, file_list) or None
175     """
176     lp = osdf_config.core.get('osdf_temp', {}).get('local_policies', {})
177     if lp.get('global_disabled'):
178         return None  # short-circuit to disable all local policies
179     if lp.get('local_{}_policies_enabled'.format(service_type)):
180         if service_type == "scheduling":
181             return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type))
182         else:
183             required_node = osdf_config.core['policy_info'][service_type]['policy_scope']['service_name']
184             model_name = retrieve_node(req_json, required_node)
185             service_name = model_name  # TODO: data_mapping.get_service_type(model_name)
186             return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), \
187                    lp.get('{}_policy_files_{}'.format(service_type, service_name.lower()))
188     return None
189
190
191 def get_policies(request_json, service_type):
192     """Validate the request and get relevant policies
193     :param request_json: Request object
194     :param service_type: the type of service to call: "placement", "scheduling"
195     :return: policies associated with this request and provStatus retrieved from Subscriber policy
196     """
197     prov_status = []
198     req_info = request_json['requestInfo']
199     req_id = req_info['requestId']
200     metrics_log.info(MH.requesting("policy", req_id))
201     local_info = local_policies_location(request_json, osdf_config, service_type)
202
203     if local_info:  # tuple containing location and list of files
204         to_filter = None
205         if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
206             to_filter = request_json[service_type + "Info"]['policyId']
207         policies = get_local_policies(local_info[0], local_info[1], to_filter)
208     else:
209         policies, prov_status = remote_api(request_json, osdf_config, service_type)
210
211     return policies, prov_status