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