Energy monitoring SDK update.
[functest.git] / functest / energy / energy.py
1 #!/usr/bin/env python
2 # -*- coding: UTF-8 -*-
3
4 # Copyright (c) 2017 Orange and others.
5 #
6 # All rights reserved. This program and the accompanying materials
7 # are made available under the terms of the Apache License, Version 2.0
8 # which accompanies this distribution, and is available at
9 # http://www.apache.org/licenses/LICENSE-2.0
10
11 """This module manages calls to Energy recording API."""
12
13 import json
14 import logging
15 import urllib
16 import requests
17
18 import functest.utils.functest_utils as ft_utils
19
20
21 def enable_recording(method):
22     """
23     Record energy during method execution.
24
25     Decorator to record energy during "method" exection.
26
27         param method: Method to suround with start and stop
28         :type method: function
29
30         .. note:: "method" should belong to a class having a "case_name"
31                   attribute
32     """
33     def wrapper(*args):
34         """
35         Record energy during method execution (implementation).
36
37         Wrapper for decorator to handle method arguments.
38         """
39         current_scenario = EnergyRecorder.get_current_scenario()
40         EnergyRecorder.start(args[0].case_name)
41         return_value = method(*args)
42         if current_scenario is None:
43             EnergyRecorder.stop()
44         else:
45             EnergyRecorder.submit_scenario(
46                 current_scenario["scenario"],
47                 current_scenario["step"]
48             )
49         return return_value
50     return wrapper
51
52
53 # Class to manage energy recording sessions
54 class EnergyRecorder(object):
55     """Manage Energy recording session."""
56
57     logger = logging.getLogger(__name__)
58     # Energy recording API connectivity settings
59     # see load_config method
60     energy_recorder_api = None
61
62     # Default initial step
63     INITIAL_STEP = "running"
64
65     @staticmethod
66     def load_config():
67         """
68         Load connectivity settings from yaml.
69
70         Load connectivity settings to Energy recording API
71         Use functest global config yaml file
72         (see functest_utils.get_functest_config)
73         """
74         # Singleton pattern for energy_recorder_api static member
75         # Load only if not previouly done
76         if EnergyRecorder.energy_recorder_api is None:
77             environment = ft_utils.get_pod_name()
78
79             # API URL
80             energy_recorder_uri = ft_utils.get_functest_config(
81                 "energy_recorder.api_url")
82             assert energy_recorder_uri
83             assert environment
84
85             energy_recorder_uri += "/recorders/environment/"
86             energy_recorder_uri += urllib.quote_plus(environment)
87             EnergyRecorder.logger.debug(
88                 "API recorder at: " + energy_recorder_uri)
89
90             # Creds
91             user = ft_utils.get_functest_config(
92                 "energy_recorder.api_user")
93             password = ft_utils.get_functest_config(
94                 "energy_recorder.api_password")
95
96             if user != "" and password != "":
97                 energy_recorder_api_auth = (user, password)
98             else:
99                 energy_recorder_api_auth = None
100
101             # Final config
102             EnergyRecorder.energy_recorder_api = {
103                 "uri": energy_recorder_uri,
104                 "auth": energy_recorder_api_auth
105             }
106
107     @staticmethod
108     def submit_scenario(scenario, step):
109         """
110         Submit a complet scenario definition to Energy recorder API.
111
112             param scenario: Scenario name
113             :type scenario: string
114             param step: Step name
115             :type step: string
116         """
117         return_status = True
118         try:
119             EnergyRecorder.logger.debug("Submitting scenario")
120             # Ensure that connectyvity settings are loaded
121             EnergyRecorder.load_config()
122
123             # Create API payload
124             payload = {
125                 "step": step,
126                 "scenario": scenario
127             }
128             # Call API to start energy recording
129             response = requests.post(
130                 EnergyRecorder.energy_recorder_api["uri"],
131                 data=json.dumps(payload),
132                 auth=EnergyRecorder.energy_recorder_api["auth"],
133                 headers={
134                     'content-type': 'application/json'
135                 }
136             )
137             if response.status_code != 200:
138                 log_msg = "Error while submitting scenario\n{}"
139                 log_msg = log_msg.format(response.text)
140                 EnergyRecorder.logger.info(log_msg)
141                 return_status = False
142         except Exception:  # pylint: disable=broad-except
143             # Default exception handler to ensure that method
144             # is safe for caller
145             EnergyRecorder.logger.exception(
146                 "Error while submitting scenarion to energy recorder API"
147             )
148             return_status = False
149         return return_status
150
151     @staticmethod
152     def start(scenario):
153         """
154         Start a recording session for scenario.
155
156             param scenario: Starting scenario
157             :type scenario: string
158         """
159         return_status = True
160         try:
161             EnergyRecorder.logger.debug("Starting recording")
162             return_status = EnergyRecorder.submit_scenario(
163                 scenario,
164                 EnergyRecorder.INITIAL_STEP
165             )
166
167         except Exception:  # pylint: disable=broad-except
168             # Default exception handler to ensure that method
169             # is safe for caller
170             EnergyRecorder.logger.exception(
171                 "Error while starting energy recorder API"
172             )
173             return_status = False
174         return return_status
175
176     @staticmethod
177     def stop():
178         """Stop current recording session."""
179         EnergyRecorder.logger.debug("Stopping recording")
180         return_status = True
181         try:
182             # Ensure that connectyvity settings are loaded
183             EnergyRecorder.load_config()
184
185             # Call API to stop energy recording
186             response = requests.delete(
187                 EnergyRecorder.energy_recorder_api["uri"],
188                 auth=EnergyRecorder.energy_recorder_api["auth"],
189                 headers={
190                     'content-type': 'application/json'
191                 }
192             )
193             if response.status_code != 200:
194                 log_msg = "Error while stating energy recording session\n{}"
195                 log_msg = log_msg.format(response.text)
196                 EnergyRecorder.logger.error(log_msg)
197                 return_status = False
198         except Exception:  # pylint: disable=broad-except
199             # Default exception handler to ensure that method
200             # is safe for caller
201             EnergyRecorder.logger.exception(
202                 "Error while stoping energy recorder API"
203             )
204             return_status = False
205         return return_status
206
207     @staticmethod
208     def set_step(step):
209         """Notify energy recording service of current step of the testcase."""
210         EnergyRecorder.logger.debug("Setting step")
211         return_status = True
212         try:
213             # Ensure that connectyvity settings are loaded
214             EnergyRecorder.load_config()
215
216             # Create API payload
217             payload = {
218                 "step": step,
219             }
220
221             # Call API to define step
222             response = requests.post(
223                 EnergyRecorder.energy_recorder_api["uri"] + "/step",
224                 data=json.dumps(payload),
225                 auth=EnergyRecorder.energy_recorder_api["auth"],
226                 headers={
227                     'content-type': 'application/json'
228                 }
229             )
230             if response.status_code != 200:
231                 log_msg = "Error while setting current step of testcase\n{}"
232                 log_msg = log_msg.format(response.text)
233                 EnergyRecorder.logger.error(log_msg)
234                 return_status = False
235         except Exception:  # pylint: disable=broad-except
236             # Default exception handler to ensure that method
237             # is safe for caller
238             EnergyRecorder.logger.exception(
239                 "Error while setting step on energy recorder API"
240             )
241             return_status = False
242         return return_status
243
244     @staticmethod
245     def get_current_scenario():
246         """Get current running scenario (if any, None else)."""
247         EnergyRecorder.logger.debug("Getting current scenario")
248         return_value = None
249         print "In get current"
250         try:
251             # Ensure that connectyvity settings are loaded
252             EnergyRecorder.load_config()
253
254             # Call API get running scenario
255             response = requests.get(
256                 EnergyRecorder.energy_recorder_api["uri"],
257                 auth=EnergyRecorder.energy_recorder_api["auth"]
258             )
259             if response.status_code == 200:
260                 return_value = json.loads(response.text)
261             elif response.status_code == 404:
262                 log_msg = "No current running scenario at {}"
263                 log_msg = log_msg.format(
264                     EnergyRecorder.energy_recorder_api["uri"])
265                 EnergyRecorder.logger.error(log_msg)
266                 print log_msg
267                 return_value = None
268             else:
269                 log_msg = "Error while getting current scenario\n{}"
270                 log_msg = log_msg.format(response.text)
271                 EnergyRecorder.logger.error(log_msg)
272                 print log_msg
273                 return_value = None
274         except Exception:  # pylint: disable=broad-except
275             # Default exception handler to ensure that method
276             # is safe for caller
277             EnergyRecorder.logger.exception(
278                 "Error while getting current scenario from energy recorder API"
279             )
280             return_value = None
281         return return_value