1 # -------------------------------------------------------------------------
2 # Copyright (c) 2020 Huawei 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 # -------------------------------------------------------------------------
19 This application generates NST SELECTION API calls using the information received from SO
22 from osdf.adapters.conductor import conductor
23 from osdf.adapters.policy.interface import get_policies
24 from osdf.logging.osdf_logging import debug_log
25 from osdf.logging.osdf_logging import error_log
26 from osdf.utils.interfaces import get_rest_client
27 from requests import RequestException
28 from threading import Thread
30 BASE_DIR = os.path.dirname(__file__)
33 # This is the class for NST Selection
36 class NstSelection(Thread):
38 def __init__(self, osdf_config, request_json):
40 self.osdf_config = osdf_config
41 self.request_json = request_json
42 self.request_info = self.request_json['requestInfo']
43 self.request_info['numSolutions'] = 1
46 self.process_nst_selection()
48 def process_nst_selection(self):
49 """Process a PCI request from a Client (build config-db, policy and API call, make the call, return result)
51 :param req_object: Request parameters from the client
52 :param osdf_config: Configuration specific to OSDF application (core + deployment)
53 :return: response from NST Opt
56 rest_client = get_rest_client(self.request_json, service='so')
57 solution = self.get_nst_solution()
58 except Exception as err:
59 error_log.error("Error for {} {}".format(self.request_info.get('requestId'),
60 traceback.format_exc()))
61 error_message = str(err)
62 solution = self.error_response(error_message)
65 rest_client.request(json=solution, noresponse=True)
66 except RequestException:
67 error_log.error("Error sending asynchronous notification for {} {}".
68 format(self.request_info['requestId'], traceback.format_exc()))
70 def get_nst_solution(self):
71 """the file is in the same folder for now will move it to the conf folder of the has once its
75 req_info = self.request_json['requestInfo']
76 requirements = self.request_json['serviceProfile']
78 policies = self.get_app_policies(model_name, "nst_selection")
79 conductor_response = self.get_conductor(req_info, requirements, policies, model_name)
80 return conductor_response
82 def get_nst_selection_response(self, solutions):
83 """Get NST selection response from final solution
85 :param solutions: final solutions
86 :return: NST selection response to send back as dictionary
88 return {'requestId': self.request_info['requestId'],
89 'transactionId': self.request_info['transactionId'],
90 'requestStatus': 'completed',
92 'solutions': solutions}
94 def error_response(self, error_message):
95 """Form response message from the error message
97 :param error_message: error message while processing the request
98 :return: response json as dictionary
100 return {'requestId': self.request_info['requestId'],
101 'transactionId': self.request_info['transactionId'],
102 'requestStatus': 'error',
103 'statusMessage': error_message}
105 def get_app_policies(self, model_name, app_name):
106 policy_request_json = self.request_json.copy()
107 policy_request_json['serviceInfo'] = {'serviceName': model_name}
108 debug_log.debug("policy_request_json {}".format(str(policy_request_json)))
109 return get_policies(policy_request_json, app_name) # app_name: nst_selection
111 def get_conductor(self, req_info, request_parameters, policies, model_name):
114 "resourceModuleName": model_name,
115 "resourceModelInfo": {}
121 'location_enabled': False,
122 'version': '2020-08-13'
124 resp = conductor.request(req_info, demands, request_parameters, {}, template_fields,
125 self.osdf_config, policies)
126 except RequestException as e:
127 resp = e.response.json()
128 error = resp['plans'][0]['message']
129 if "Unable to find any" in error:
130 return self.get_nst_selection_response([])
131 error_log.error('Error from conductor {}'.format(error))
132 return self.error_response(error)
133 debug_log.debug("Response from conductor in get_conductor method {}".format(str(resp)))
134 recommendations = resp["plans"][0].get("recommendations")
135 return self.process_response(recommendations, model_name)
137 def process_response(self, recommendations, model_name):
138 """Process conductor response to form the response for the API request
140 :param recommendations: recommendations from conductor
141 :return: response json as a dictionary
143 if not recommendations:
144 return self.get_nst_selection_response([])
145 solutions = [self.get_solution_from_candidate(rec[model_name]['candidate'])
146 for rec in recommendations]
147 return self.get_nst_selection_response(solutions)
149 def get_solution_from_candidate(self, candidate):
150 if candidate['inventory_type'] == 'nst':
152 'UUID': candidate['model_version_id'],
153 'invariantUUID': candidate['model_invariant_id'],
154 'NSTName': candidate['model_name'],