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