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