[WIP]Migration to new policy api
[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] = set(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": "{}".format(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     uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword']
137     url = config['policyPlatformUrl']
138     rc = RestClient(userid=uid, passwd=passwd, url=url, log_func=debug_log.debug)
139
140     if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
141         policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=True)
142     elif osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name_no_wildcards":
143         policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=False)
144     else:
145         policies = get_by_scope(rc, req_json, osdf_config.core, service_type)
146
147     formatted_policies = []
148     for x in itertools.chain(*policies):
149         if x[list(x.keys())[0]].get('properties') is None:
150             raise BusinessException("Properties not found for policy with name %s" % x[list(x.keys()[0])])
151         else:
152             formatted_policies.append(x)
153     return formatted_policies
154
155
156 def local_policies_location(req_json, osdf_config, service_type):
157     """
158     Get folder and list of policy_files if "local policies" option is enabled
159     :param service_type: placement supported for now, but can be any other service
160     :return: a tuple (folder, file_list) or None
161     """
162     lp = osdf_config.core.get('osdf_temp', {}).get('local_policies', {})
163     if lp.get('global_disabled'):
164         return None  # short-circuit to disable all local policies
165     if lp.get('local_{}_policies_enabled'.format(service_type)):
166         debug_log.debug('Loading local policies for service type: {}'.format(service_type))
167         if service_type == "scheduling":
168             return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type))
169         else:
170             service_name = req_json['serviceInfo']['serviceName']  # TODO: data_mapping.get_service_type(model_name)
171             debug_log.debug('Loading local policies for service name: {}'.format(service_name))
172             return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), \
173                    lp.get('{}_policy_files_{}'.format(service_type, service_name.lower()))
174     return None
175
176
177 def get_policies(request_json, service_type):
178     """Validate the request and get relevant policies
179     :param request_json: Request object
180     :param service_type: the type of service to call: "placement", "scheduling"
181     :return: policies associated with this request and provStatus retrieved from Subscriber policy
182     """
183     req_info = request_json['requestInfo']
184     req_id = req_info['requestId']
185     metrics_log.info(MH.requesting("policy", req_id))
186     local_info = local_policies_location(request_json, osdf_config, service_type)
187
188     if local_info:  # tuple containing location and list of files
189         if local_info[0] is None or local_info[1] is None:
190             raise ValueError("Error fetching local policy info")
191         to_filter = None
192         if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
193             to_filter = request_json[service_type + "Info"]['policyId']
194         policies = get_local_policies(local_info[0], local_info[1], to_filter)
195     else:
196         policies = remote_api(request_json, osdf_config, service_type)
197
198     return policies
199
200 def upload_policy_models():
201     """Upload all the policy models reside in the folder"""
202     requestId = uuid.uuid4()
203     config = osdf_config.deployment
204     model_path = config['pathPolicyModelUpload']
205     uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword']
206     pcuid, pcpasswd = config['policyClientUsername'], config['policyClientPassword']
207     headers = {"ClientAuth": base64.b64encode(bytes("{}:{}".format(pcuid, pcpasswd), "ascii"))}
208     headers.update({'Environment': config['policyPlatformEnv']})
209     headers.update({'X-ONAP-RequestID': requestId})
210     url = config['policyPlatformUrlModelUpload']
211     rc = RestClient(userid=uid, passwd=passwd, headers=headers, url=url, log_func=debug_log.debug)
212
213     for file in os.listdir(model_path):
214         if not file.endswith(".yaml"):
215             continue
216         with open(file) as f:
217             file_converted = json.dumps(yaml.load(f))
218             response = rc.request(json=file_converted, ok_codes=(200))
219         if not response:
220             success = False
221             audit_log.warn("Policy model %s uploading failed!" % file)
222     if not success:
223         return "Policy model uploading success!"
224     else:
225         return "Policy model uploading not success!"