Return error when polices are not present
[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 json
21 import os
22 from requests import RequestException
23 import uuid
24 import yaml
25
26 from osdf.adapters.local_data.local_policies import get_local_policies
27 from osdf.adapters.policy.utils import policy_name_as_regex
28 from osdf.config.base import osdf_config
29 from osdf.logging.osdf_logging import audit_log
30 from osdf.logging.osdf_logging import debug_log
31 from osdf.logging.osdf_logging import metrics_log
32 from osdf.logging.osdf_logging import MH
33 from osdf.operation.exceptions import BusinessException
34 from osdf.utils.interfaces import RestClient
35 from osdf.utils.programming_utils import dot_notation
36 from osdf.utils.programming_utils import list_flatten
37
38
39 def get_by_name(rest_client, policy_name_list, wildcards=True):
40     policy_list = []
41     for policy_name in policy_name_list:
42         try:
43             query_name = policy_name
44             if wildcards:
45                 query_name = policy_name_as_regex(query_name)
46             policy_list.append(rest_client.request(json={"policyName": query_name}))
47         except RequestException as err:
48             audit_log.warn("Error in fetching policy: " + policy_name)
49             raise BusinessException("Cannot fetch policy {}: ".format(policy_name), err)
50     return policy_list
51
52
53 def get_by_scope(rest_client, req, config_local, type_service):
54     """Get policies by scopes as defined in the configuration file.
55
56     :param rest_client: a rest client object to make a call.
57     :param req: an optimization request.
58     :param config_local: application configuration file.
59     :param type_service: the type of optimization service.
60     :return: policies in the form of list of list where inner list contains policies for a single a scope.
61     """
62     scope_policies = []
63     references = config_local.get('references', {})
64     pscope = config_local.get('policy_info', {}).get(type_service, {}).get('policy_scope', [])
65     scope_fields = {}
66     policies = {}
67     for scopes in pscope:
68         for key in scopes.keys():
69             for field in scopes[key]:
70                 scope_fields[key] = list_flatten([get_scope_fields(field, references, req, policies)
71                                                   if 'get_param' in field else field])
72         if scope_fields.get('resources') and len(scope_fields['resources']) > 1:
73             for s in scope_fields['resources']:
74                 scope_fields['resources'] = [s]
75                 policies.update(policy_api_call(rest_client, scope_fields).get('policies', {}))
76         else:
77             policies.update(policy_api_call(rest_client, scope_fields).get('policies', {}))
78         for policyName in policies.keys():
79             keys = scope_fields.keys() & policies[policyName]['properties'].keys()
80             policy = {}
81             policy[policyName] = policies[policyName]
82             for k in keys:
83                 if set(policies.get(policyName, {}).get('properties', {}).get(k)) >= set(scope_fields[k]) \
84                         and policy not in scope_policies:
85                     scope_policies.append(policy)
86
87     return scope_policies
88
89
90 def get_scope_fields(field, references, req, policies):
91     """Retrieve the values for scope fields from a request and policies
92
93     They are derived as per the configuration and references defined in a
94     configuration file. If the value of a scope field missing in a request or
95     policies, throw an exception since correct policies cannot be retrieved.
96
97     :param field: details on a scope field from a configuration file.
98     :param references: references defined in a configuration file.
99     :param req: an optimization request.
100     :param policy_info: a list of policies.
101     :return: scope fields retrieved from a request and policies.
102     """
103     ref_source = references.get(field.get('get_param', ""), {}).get('source')
104     ref_value = references.get(field.get('get_param', ""), {}).get('value')
105     if ref_source == "request":
106         scope_field = dot_notation(req, ref_value)
107         if scope_field:
108             return scope_field
109         raise BusinessException("Field {} is missing a value in a request".format(ref_value.split('.')[-1]))
110     else:
111         scope_fields = []
112         for policyName in policies.keys():
113             policy_content = policies.get(policyName)
114             if policy_content.get('type', "invalid_policy") == ref_source:
115                 scope_fields.append(dot_notation(policy_content, ref_value))
116         scope_values = list_flatten(scope_fields)
117         if len(scope_values) > 0:
118             return scope_values
119         raise BusinessException("Field {} is missing a value in all policies of type {}".format(
120             ref_value.split('.')[-1], ref_source))
121
122
123 def policy_api_call(rest_client, scope_fields):
124     """Makes the api call to policy and return the response if policies are present
125
126     :param rest_client: rest client to make a call
127     :param scope_fields: a collection of scopes to be used for filtering
128     :return: a list of policies matching all filters
129     """
130     api_call_body = {"ONAPName": "OOF",
131                      "ONAPComponent": "OOF_Component",
132                      "ONAPInstance": "OOF_Component_Instance",
133                      "action": "optimize",
134                      "resource": scope_fields}
135     response = rest_client.request(json=api_call_body)
136     if not response.get("policies"):
137         raise Exception("Policies not found for the scope {}".format(scope_fields))
138     return response
139
140
141 def remote_api(req_json, osdf_config, service_type="placement"):
142     """Make a request to policy and return response -- it accounts for multiple requests that be needed
143
144     :param req_json: policy request object (can have multiple policy names)
145     :param osdf_config: main config that will have credential information
146     :param service_type: the type of service to call: "placement", "scheduling"
147     :return: all related policies and provStatus retrieved from Subscriber policy
148     """
149     config = osdf_config.deployment
150     headers = {"Content-type": "application/json"}
151     uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword']
152     url = config['policyPlatformUrl']
153     rc = RestClient(userid=uid, passwd=passwd, headers=headers, url=url, log_func=debug_log.debug)
154
155     if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
156         policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=True)
157     elif osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name_no_wildcards":
158         policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=False)
159     else:
160         policies = get_by_scope(rc, req_json, osdf_config.core, service_type)
161
162     formatted_policies = []
163     for x in policies:
164         if x[list(x.keys())[0]].get('properties') is None:
165             raise BusinessException("Properties not found for policy with name %s" % x[list(x.keys()[0])])
166         else:
167             formatted_policies.append(x)
168     return formatted_policies
169
170
171 def local_policies_location(req_json, osdf_config, service_type):
172     """Get folder and list of policy_files if "local policies" option is enabled
173
174     :param service_type: placement supported for now, but can be any other service
175     :return: a tuple (folder, file_list) or None
176     """
177     lp = osdf_config.core.get('osdf_temp', {}).get('local_policies', {})
178     if lp.get('global_disabled'):
179         return None  # short-circuit to disable all local policies
180     if lp.get('local_{}_policies_enabled'.format(service_type)):
181         debug_log.debug('Loading local policies for service type: {}'.format(service_type))
182         if service_type == "scheduling":
183             return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type))
184         else:
185             service_name = req_json['serviceInfo']['serviceName']
186             debug_log.debug('Loading local policies for service name: {}'.format(service_name))
187             return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), \
188                 lp.get('{}_policy_files_{}'.format(service_type, service_name.lower()))
189     return None
190
191
192 def get_policies(request_json, service_type):
193     """Validate the request and get relevant policies
194
195     :param request_json: Request object
196     :param service_type: the type of service to call: "placement", "scheduling"
197     :return: policies associated with this request and provStatus retrieved from Subscriber policy
198     """
199     req_info = request_json['requestInfo']
200     req_id = req_info['requestId']
201     metrics_log.info(MH.requesting("policy", req_id))
202     local_info = local_policies_location(request_json, osdf_config, service_type)
203
204     if local_info:  # tuple containing location and list of files
205         if local_info[0] is None or local_info[1] is None:
206             raise ValueError("Error fetching local policy info")
207         to_filter = None
208         if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
209             to_filter = request_json[service_type + "Info"]['policyId']
210         policies = get_local_policies(local_info[0], local_info[1], to_filter)
211     else:
212         policies = remote_api(request_json, osdf_config, service_type)
213
214     return policies
215
216
217 def upload_policy_models():
218     """Upload all the policy models reside in the folder"""
219     requestId = uuid.uuid4()
220     config = osdf_config.deployment
221     model_path = config['pathPolicyModelUpload']
222     uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword']
223     pcuid, pcpasswd = config['policyClientUsername'], config['policyClientPassword']
224     headers = {"ClientAuth": base64.b64encode(bytes("{}:{}".format(pcuid, pcpasswd), "ascii"))}
225     headers.update({'Environment': config['policyPlatformEnv']})
226     headers.update({'X-ONAP-RequestID': requestId})
227     url = config['policyPlatformUrlModelUpload']
228     rc = RestClient(userid=uid, passwd=passwd, headers=headers, url=url, log_func=debug_log.debug)
229
230     for file in os.listdir(model_path):
231         if not file.endswith(".yaml"):
232             continue
233         with open(file) as f:
234             file_converted = json.dumps(yaml.load(f))
235             response = rc.request(json=file_converted, ok_codes=(200))
236         if not response:
237             success = False
238             audit_log.warn("Policy model %s uploading failed!" % file)
239     if not success:
240         return "Policy model uploading success!"
241     else:
242         return "Policy model uploading not success!"