Return sdc model name instead of candidate name
[optf/osdf.git] / apps / nst / optimizers / nst_select_processor.py
1 # -------------------------------------------------------------------------
2 #   Copyright (c) 2020 Huawei 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 This application generates NST SELECTION API calls using the information received from SO
20 """
21 import os
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
29 import traceback
30 BASE_DIR = os.path.dirname(__file__)
31
32
33 # This is the class for NST Selection
34
35
36 class NstSelection(Thread):
37
38     def __init__(self, osdf_config, request_json):
39         super().__init__()
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
44
45     def run(self):
46         self.process_nst_selection()
47
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)
50
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
54         """
55         try:
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)
63
64         try:
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()))
69
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
72
73            integrated there...
74         """
75         req_info = self.request_json['requestInfo']
76         requirements = self.request_json['serviceProfile']
77         model_name = "nst"
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
81
82     def get_nst_selection_response(self, solutions):
83         """Get NST selection response from final solution
84
85             :param solutions: final solutions
86             :return: NST selection response to send back as dictionary
87         """
88         return {'requestId': self.request_info['requestId'],
89                 'transactionId': self.request_info['transactionId'],
90                 'requestStatus': 'completed',
91                 'statusMessage': '',
92                 'solutions': solutions}
93
94     def error_response(self, error_message):
95         """Form response message from the error message
96
97             :param error_message: error message while processing the request
98             :return: response json as dictionary
99         """
100         return {'requestId': self.request_info['requestId'],
101                 'transactionId': self.request_info['transactionId'],
102                 'requestStatus': 'error',
103                 'statusMessage': error_message}
104
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
110
111     def get_conductor(self, req_info, request_parameters, policies, model_name):
112         demands = [
113             {
114                 "resourceModuleName": model_name,
115                 "resourceModelInfo": {}
116             }
117         ]
118
119         try:
120             template_fields = {
121                 'location_enabled': False,
122                 'version': '2020-08-13'
123             }
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)
136
137     def process_response(self, recommendations, model_name):
138         """Process conductor response to form the response for the API request
139
140             :param recommendations: recommendations from conductor
141             :return: response json as a dictionary
142         """
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)
148
149     def get_solution_from_candidate(self, candidate):
150         if candidate['inventory_type'] == 'nst':
151             return {
152                 'UUID': candidate['model_version_id'],
153                 'invariantUUID': candidate['model_invariant_id'],
154                 'NSTName': candidate['model_name'],
155             }