1 # -------------------------------------------------------------------------
2 # Copyright (c) 2015-2017 AT&T Intellectual Property
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
16 # -------------------------------------------------------------------------
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.utils.programming_utils import list_flatten, dot_notation
29 from osdf.config.base import osdf_config
30 from osdf.logging.osdf_logging import audit_log, MH, metrics_log, debug_log
31 from osdf.utils.interfaces import RestClient
34 def get_by_name(rest_client, policy_name_list, wildcards=True):
36 for policy_name in policy_name_list:
38 query_name = policy_name
40 query_name = policy_name_as_regex(query_name)
41 policy_list.append(rest_client.request(json={"policyName": query_name}))
42 except RequestException as err:
43 audit_log.warn("Error in fetching policy: " + policy_name)
44 raise BusinessException("Cannot fetch policy {}: ".format(policy_name), err)
48 def get_by_scope(rest_client, req, config_local, type_service):
49 """ Get policies by scopes as defined in the configuration file.
50 :param rest_client: a rest client object to make a call.
51 :param req: an optimization request.
52 :param config_local: application configuration file.
53 :param type_service: the type of optimization service.
54 :return: a list of policies.
57 references = config_local.get('references', {})
58 pscope = config_local.get('policy_info', {}).get(type_service, {}).get('policy_scope', {})
59 service_name = dot_notation(req, references.get('service_name', {}).get('value', None))
60 primary_scope = pscope['{}_scope'.format(service_name.lower() if service_name else "default")]
61 for sec_scope in pscope.get('secondary_scopes', []):
62 scope_fields, scope_fields_flatten = [], []
63 for field in sec_scope:
64 if 'get_param' in field:
65 scope_fields.append(get_scope_fields(field, references, req, list_flatten(policy_list)))
67 scope_fields.append(field)
68 scope_fields_flatten = list_flatten(scope_fields)
69 policy_list.append(policy_api_call(rest_client, primary_scope, scope_fields_flatten))
73 def get_scope_fields(field, references, req, policy_info):
74 """ Retrieve the values for scope fields from a request and policies as per the configuration
75 and references defined in a configuration file. If the value of a scope field missing in a request or
76 policies, throw an exception since correct policies cannot be retrieved.
77 :param field: details on a scope field from a configuration file.
78 :param references: references defined in a configuration file.
79 :param req: an optimization request.
80 :param policy_info: a list of policies.
81 :return: scope fields retrieved from a request and policies.
83 if references.get(field.get('get_param', ""), {}).get('source', None) == "request":
84 scope_field = dot_notation(req, references.get(field.get('get_param', ""), {}).get('value', ""))
87 raise BusinessException("Field {} is missing a value in a request".format(
88 references.get(field.get('get_param', ""), {}).get('value', "").split('.')[-1]))
91 for policy in policy_info:
92 policy_content = json.loads(policy.get('config', "{}"))
93 if policy_content.get('content', {}).get('policyType', "invalid_policy") == \
94 references.get(field.get('get_param', ""), {}).get('source', None):
95 scope_fields.append(dot_notation(policy_content,
96 references.get(field.get('get_param', ""), {}).get('value', "")))
97 scope_values = list_flatten(scope_fields)
98 if len(scope_values) > 0:
100 raise BusinessException("Field {} is missing a value in all policies of type {}".format(
101 references.get(field.get('get_param', ""), {}).get('value', "").split('.')[-1],
102 references.get(field.get('get_param', ""), {}).get('source', "")))
105 def policy_api_call(rest_client, primary_scope, scope_fields):
106 """ Makes a getConfig API call to the policy system to retrieve policies matching a scope.
107 :param rest_client: rest client object to make a call
108 :param primary_scope: the primary scope of policies, which is a folder in the policy system
109 where policies are stored.
110 :param scope_fields: the secondary scope of policies, which is a collection of domain values.
111 :return: a list of policies matching both primary and secondary scopes.
113 api_call_body = {"policyName": "{}.*".format(primary_scope),
114 "configAttributes": {"policyScope": "{}".format(scope_fields)}}
115 return rest_client.request(json=api_call_body)
118 def remote_api(req_json, osdf_config, service_type="placement"):
119 """Make a request to policy and return response -- it accounts for multiple requests that be needed
120 :param req_json: policy request object (can have multiple policy names)
121 :param osdf_config: main config that will have credential information
122 :param service_type: the type of service to call: "placement", "scheduling"
123 :return: all related policies and provStatus retrieved from Subscriber policy
125 config = osdf_config.deployment
126 uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword']
127 pcuid, pcpasswd = config['policyClientUsername'], config['policyClientPassword']
128 headers = {"ClientAuth": base64.b64encode(bytes("{}:{}".format(pcuid, pcpasswd), "ascii"))}
129 headers.update({'Environment': config['policyPlatformEnv']})
130 url = config['policyPlatformUrl']
131 rc = RestClient(userid=uid, passwd=passwd, headers=headers, url=url, log_func=debug_log.debug)
133 if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
134 policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=True)
135 elif osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name_no_wildcards":
136 policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=False)
138 policies = get_by_scope(rc, req_json, osdf_config.core, service_type)
140 formatted_policies = []
141 for x in itertools.chain(*policies):
142 if x['config'] is None:
143 raise BusinessException("Config not found for policy with name %s" % x['policyName'])
145 formatted_policies.append(json.loads(x['config']))
146 return formatted_policies
149 def local_policies_location(req_json, osdf_config, service_type):
151 Get folder and list of policy_files if "local policies" option is enabled
152 :param service_type: placement supported for now, but can be any other service
153 :return: a tuple (folder, file_list) or None
155 lp = osdf_config.core.get('osdf_temp', {}).get('local_policies', {})
156 if lp.get('global_disabled'):
157 return None # short-circuit to disable all local policies
158 if lp.get('local_{}_policies_enabled'.format(service_type)):
159 if service_type == "scheduling":
160 return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type))
162 required_node = osdf_config.core['policy_info'][service_type]['policy_scope']['service_name']
163 model_name = retrieve_node(req_json, required_node)
164 service_name = model_name # TODO: data_mapping.get_service_type(model_name)
165 return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), \
166 lp.get('{}_policy_files_{}'.format(service_type, service_name.lower()))
170 def get_policies(request_json, service_type):
171 """Validate the request and get relevant policies
172 :param request_json: Request object
173 :param service_type: the type of service to call: "placement", "scheduling"
174 :return: policies associated with this request and provStatus retrieved from Subscriber policy
177 req_info = request_json['requestInfo']
178 req_id = req_info['requestId']
179 metrics_log.info(MH.requesting("policy", req_id))
180 local_info = local_policies_location(request_json, osdf_config, service_type)
182 if local_info: # tuple containing location and list of files
184 if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
185 to_filter = request_json[service_type + "Info"]['policyId']
186 policies = get_local_policies(local_info[0], local_info[1], to_filter)
188 policies = remote_api(request_json, osdf_config, service_type)
190 return policies, prov_status