summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorgan Richomme <morgan.richomme@orange.com>2017-08-10 09:26:46 +0000
committerGerrit Code Review <gerrit@opnfv.org>2017-08-10 09:26:46 +0000
commit42877bb48714293d3ec761f42007fcc8fa925724 (patch)
treeabb151b024caa6eca473bd8fbbf0ed8270a5e78b
parent7bd30cdc1e6d506bf1245daeb9d01530eab15674 (diff)
parentbabd8e011681084c055dd2e131faf1f5f4d9a646 (diff)
Merge "Improved error handling when Energy recorder API is unavailable."
-rw-r--r--functest/energy/energy.py218
-rw-r--r--functest/tests/unit/energy/test_functest_energy.py74
2 files changed, 183 insertions, 109 deletions
diff --git a/functest/energy/energy.py b/functest/energy/energy.py
index 372c1d32f..580bc6b09 100644
--- a/functest/energy/energy.py
+++ b/functest/energy/energy.py
@@ -55,9 +55,10 @@ def enable_recording(method):
try:
return_value = method(*args)
finish_session(current_scenario)
- except Exception: # pylint: disable=broad-except
+ except Exception as exc: # pylint: disable=broad-except
+ EnergyRecorder.logger.exception(exc)
finish_session(current_scenario)
- raise
+ raise exc
return return_value
return wrapper
@@ -74,6 +75,9 @@ class EnergyRecorder(object):
# Default initial step
INITIAL_STEP = "running"
+ # Default connection timeout
+ CONNECTION_TIMOUT = 1
+
@staticmethod
def load_config():
"""
@@ -94,10 +98,10 @@ class EnergyRecorder(object):
assert energy_recorder_uri
assert environment
- energy_recorder_uri += "/recorders/environment/"
- energy_recorder_uri += urllib.quote_plus(environment)
+ uri_comp = "/recorders/environment/"
+ uri_comp += urllib.quote_plus(environment)
EnergyRecorder.logger.debug(
- "API recorder at: " + energy_recorder_uri)
+ "API recorder at: " + energy_recorder_uri + uri_comp)
# Creds
user = ft_utils.get_functest_config(
@@ -110,11 +114,25 @@ class EnergyRecorder(object):
else:
energy_recorder_api_auth = None
+ try:
+ resp = requests.get(energy_recorder_uri + "/monitoring/ping",
+ auth=energy_recorder_api_auth,
+ headers={
+ 'content-type': 'application/json'
+ },
+ timeout=EnergyRecorder.CONNECTION_TIMOUT)
+ api_available = json.loads(resp.text)["status"] == "OK"
+ except Exception: # pylint: disable=broad-except
+ EnergyRecorder.logger.error(
+ "Energy recorder API is not available")
+ api_available = False
# Final config
EnergyRecorder.energy_recorder_api = {
- "uri": energy_recorder_uri,
- "auth": energy_recorder_api_auth
+ "uri": energy_recorder_uri + uri_comp,
+ "auth": energy_recorder_api_auth,
+ "available": api_available
}
+ return EnergyRecorder.energy_recorder_api["available"]
@staticmethod
def submit_scenario(scenario, step):
@@ -126,31 +144,36 @@ class EnergyRecorder(object):
param step: Step name
:type step: string
"""
- return_status = True
try:
- EnergyRecorder.logger.debug("Submitting scenario")
+ return_status = True
# Ensure that connectyvity settings are loaded
- EnergyRecorder.load_config()
+ if EnergyRecorder.load_config():
+ EnergyRecorder.logger.debug("Submitting scenario")
- # Create API payload
- payload = {
- "step": step,
- "scenario": scenario
- }
- # Call API to start energy recording
- response = requests.post(
- EnergyRecorder.energy_recorder_api["uri"],
- data=json.dumps(payload),
- auth=EnergyRecorder.energy_recorder_api["auth"],
- headers={
- 'content-type': 'application/json'
+ # Create API payload
+ payload = {
+ "step": step,
+ "scenario": scenario
}
- )
- if response.status_code != 200:
- log_msg = "Error while submitting scenario\n{}"
- log_msg = log_msg.format(response.text)
- EnergyRecorder.logger.info(log_msg)
- return_status = False
+ # Call API to start energy recording
+ response = requests.post(
+ EnergyRecorder.energy_recorder_api["uri"],
+ data=json.dumps(payload),
+ auth=EnergyRecorder.energy_recorder_api["auth"],
+ headers={
+ 'content-type': 'application/json'
+ },
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
+ )
+ if response.status_code != 200:
+ EnergyRecorder.logger.error(
+ "Error while submitting scenario\n%s",
+ response.text)
+ return_status = False
+ except requests.exceptions.ConnectionError:
+ EnergyRecorder.logger.warning(
+ "submit_scenario: Unable to connect energy recorder API")
+ return_status = False
except Exception: # pylint: disable=broad-except
# Default exception handler to ensure that method
# is safe for caller
@@ -170,11 +193,12 @@ class EnergyRecorder(object):
"""
return_status = True
try:
- EnergyRecorder.logger.debug("Starting recording")
- return_status = EnergyRecorder.submit_scenario(
- scenario,
- EnergyRecorder.INITIAL_STEP
- )
+ if EnergyRecorder.load_config():
+ EnergyRecorder.logger.debug("Starting recording")
+ return_status = EnergyRecorder.submit_scenario(
+ scenario,
+ EnergyRecorder.INITIAL_STEP
+ )
except Exception: # pylint: disable=broad-except
# Default exception handler to ensure that method
@@ -188,25 +212,30 @@ class EnergyRecorder(object):
@staticmethod
def stop():
"""Stop current recording session."""
- EnergyRecorder.logger.debug("Stopping recording")
return_status = True
try:
# Ensure that connectyvity settings are loaded
- EnergyRecorder.load_config()
-
- # Call API to stop energy recording
- response = requests.delete(
- EnergyRecorder.energy_recorder_api["uri"],
- auth=EnergyRecorder.energy_recorder_api["auth"],
- headers={
- 'content-type': 'application/json'
- }
- )
- if response.status_code != 200:
- log_msg = "Error while stating energy recording session\n{}"
- log_msg = log_msg.format(response.text)
- EnergyRecorder.logger.error(log_msg)
- return_status = False
+ if EnergyRecorder.load_config():
+ EnergyRecorder.logger.debug("Stopping recording")
+
+ # Call API to stop energy recording
+ response = requests.delete(
+ EnergyRecorder.energy_recorder_api["uri"],
+ auth=EnergyRecorder.energy_recorder_api["auth"],
+ headers={
+ 'content-type': 'application/json'
+ },
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
+ )
+ if response.status_code != 200:
+ EnergyRecorder.logger.error(
+ "Error while stating energy recording session\n%s",
+ response.text)
+ return_status = False
+ except requests.exceptions.ConnectionError:
+ EnergyRecorder.logger.warning(
+ "stop: Unable to connect energy recorder API")
+ return_status = False
except Exception: # pylint: disable=broad-except
# Default exception handler to ensure that method
# is safe for caller
@@ -219,31 +248,36 @@ class EnergyRecorder(object):
@staticmethod
def set_step(step):
"""Notify energy recording service of current step of the testcase."""
- EnergyRecorder.logger.debug("Setting step")
return_status = True
try:
# Ensure that connectyvity settings are loaded
- EnergyRecorder.load_config()
+ if EnergyRecorder.load_config():
+ EnergyRecorder.logger.debug("Setting step")
- # Create API payload
- payload = {
- "step": step,
- }
-
- # Call API to define step
- response = requests.post(
- EnergyRecorder.energy_recorder_api["uri"] + "/step",
- data=json.dumps(payload),
- auth=EnergyRecorder.energy_recorder_api["auth"],
- headers={
- 'content-type': 'application/json'
+ # Create API payload
+ payload = {
+ "step": step,
}
- )
- if response.status_code != 200:
- log_msg = "Error while setting current step of testcase\n{}"
- log_msg = log_msg.format(response.text)
- EnergyRecorder.logger.error(log_msg)
- return_status = False
+
+ # Call API to define step
+ response = requests.post(
+ EnergyRecorder.energy_recorder_api["uri"] + "/step",
+ data=json.dumps(payload),
+ auth=EnergyRecorder.energy_recorder_api["auth"],
+ headers={
+ 'content-type': 'application/json'
+ },
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
+ )
+ if response.status_code != 200:
+ EnergyRecorder.logger.error(
+ "Error while setting current step of testcase\n%s",
+ response.text)
+ return_status = False
+ except requests.exceptions.ConnectionError:
+ EnergyRecorder.logger.warning(
+ "set_step: Unable to connect energy recorder API")
+ return_status = False
except Exception: # pylint: disable=broad-except
# Default exception handler to ensure that method
# is safe for caller
@@ -256,30 +290,34 @@ class EnergyRecorder(object):
@staticmethod
def get_current_scenario():
"""Get current running scenario (if any, None else)."""
- EnergyRecorder.logger.debug("Getting current scenario")
return_value = None
try:
# Ensure that connectyvity settings are loaded
- EnergyRecorder.load_config()
-
- # Call API get running scenario
- response = requests.get(
- EnergyRecorder.energy_recorder_api["uri"],
- auth=EnergyRecorder.energy_recorder_api["auth"]
- )
- if response.status_code == 200:
- return_value = json.loads(response.text)
- elif response.status_code == 404:
- log_msg = "No current running scenario at {}"
- log_msg = log_msg.format(
- EnergyRecorder.energy_recorder_api["uri"])
- EnergyRecorder.logger.error(log_msg)
- return_value = None
- else:
- log_msg = "Error while getting current scenario\n{}"
- log_msg = log_msg.format(response.text)
- EnergyRecorder.logger.error(log_msg)
- return_value = None
+ if EnergyRecorder.load_config():
+ EnergyRecorder.logger.debug("Getting current scenario")
+
+ # Call API get running scenario
+ response = requests.get(
+ EnergyRecorder.energy_recorder_api["uri"],
+ auth=EnergyRecorder.energy_recorder_api["auth"],
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
+ )
+ if response.status_code == 200:
+ return_value = json.loads(response.text)
+ elif response.status_code == 404:
+ EnergyRecorder.logger.info(
+ "No current running scenario at %s",
+ EnergyRecorder.energy_recorder_api["uri"])
+ return_value = None
+ else:
+ EnergyRecorder.logger.error(
+ "Error while getting current scenario\n%s",
+ response.text)
+ return_value = None
+ except requests.exceptions.ConnectionError:
+ EnergyRecorder.logger.warning(
+ "get_currernt_sceario: Unable to connect energy recorder API")
+ return_value = None
except Exception: # pylint: disable=broad-except
# Default exception handler to ensure that method
# is safe for caller
diff --git a/functest/tests/unit/energy/test_functest_energy.py b/functest/tests/unit/energy/test_functest_energy.py
index f8bb13c99..a576e2c3f 100644
--- a/functest/tests/unit/energy/test_functest_energy.py
+++ b/functest/tests/unit/energy/test_functest_energy.py
@@ -35,6 +35,15 @@ class MockHttpResponse(object): # pylint: disable=too-few-public-methods
self.status_code = status_code
+API_OK = MockHttpResponse(
+ '{"status": "OK"}',
+ 200
+)
+API_KO = MockHttpResponse(
+ '{"message": "API-KO"}',
+ 500
+)
+
RECORDER_OK = MockHttpResponse(
'{"environment": "UNIT_TEST",'
' "step": "string",'
@@ -81,7 +90,7 @@ class EnergyRecorderTest(unittest.TestCase):
@mock.patch('functest.energy.energy.requests.post',
return_value=RECORDER_OK)
- def test_start(self, post_mock=None):
+ def test_start(self, post_mock=None, get_mock=None):
"""EnergyRecorder.start method (regular case)."""
self.test_load_config()
self.assertTrue(EnergyRecorder.start(self.case_name))
@@ -89,7 +98,8 @@ class EnergyRecorderTest(unittest.TestCase):
EnergyRecorder.energy_recorder_api["uri"],
auth=EnergyRecorder.energy_recorder_api["auth"],
data=mock.ANY,
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@mock.patch('functest.energy.energy.requests.post',
@@ -102,7 +112,8 @@ class EnergyRecorderTest(unittest.TestCase):
EnergyRecorder.energy_recorder_api["uri"],
auth=EnergyRecorder.energy_recorder_api["auth"],
data=mock.ANY,
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@mock.patch('functest.energy.energy.requests.post',
@@ -115,7 +126,8 @@ class EnergyRecorderTest(unittest.TestCase):
EnergyRecorder.energy_recorder_api["uri"],
auth=EnergyRecorder.energy_recorder_api["auth"],
data=mock.ANY,
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@mock.patch('functest.energy.energy.requests.post',
@@ -128,7 +140,8 @@ class EnergyRecorderTest(unittest.TestCase):
EnergyRecorder.energy_recorder_api["uri"] + "/step",
auth=EnergyRecorder.energy_recorder_api["auth"],
data=mock.ANY,
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@mock.patch('functest.energy.energy.requests.post',
@@ -141,7 +154,8 @@ class EnergyRecorderTest(unittest.TestCase):
EnergyRecorder.energy_recorder_api["uri"] + "/step",
auth=EnergyRecorder.energy_recorder_api["auth"],
data=mock.ANY,
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@mock.patch('functest.energy.energy.requests.post',
@@ -154,7 +168,8 @@ class EnergyRecorderTest(unittest.TestCase):
EnergyRecorder.energy_recorder_api["uri"] + "/step",
auth=EnergyRecorder.energy_recorder_api["auth"],
data=mock.ANY,
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@mock.patch('functest.energy.energy.requests.delete',
@@ -166,7 +181,8 @@ class EnergyRecorderTest(unittest.TestCase):
delete_mock.assert_called_once_with(
EnergyRecorder.energy_recorder_api["uri"],
auth=EnergyRecorder.energy_recorder_api["auth"],
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@mock.patch('functest.energy.energy.requests.delete',
@@ -178,7 +194,8 @@ class EnergyRecorderTest(unittest.TestCase):
delete_mock.assert_called_once_with(
EnergyRecorder.energy_recorder_api["uri"],
auth=EnergyRecorder.energy_recorder_api["auth"],
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@mock.patch('functest.energy.energy.requests.delete',
@@ -190,7 +207,8 @@ class EnergyRecorderTest(unittest.TestCase):
delete_mock.assert_called_once_with(
EnergyRecorder.energy_recorder_api["uri"],
auth=EnergyRecorder.energy_recorder_api["auth"],
- headers=self.request_headers
+ headers=self.request_headers,
+ timeout=EnergyRecorder.CONNECTION_TIMOUT
)
@energy.enable_recording
@@ -206,13 +224,7 @@ class EnergyRecorderTest(unittest.TestCase):
@mock.patch("functest.energy.energy.EnergyRecorder.get_current_scenario",
return_value=None)
@mock.patch("functest.energy.energy.EnergyRecorder")
- @mock.patch("functest.utils.functest_utils.get_pod_name",
- return_value="MOCK_POD")
- @mock.patch("functest.utils.functest_utils.get_functest_config",
- side_effect=config_loader_mock)
def test_decorators(self,
- loader_mock=None,
- pod_mock=None,
recorder_mock=None,
cur_scenario_mock=None):
"""Test energy module decorators."""
@@ -264,10 +276,14 @@ class EnergyRecorderTest(unittest.TestCase):
side_effect=config_loader_mock)
@mock.patch("functest.utils.functest_utils.get_pod_name",
return_value="MOCK_POD")
- def test_load_config(self, loader_mock=None, pod_mock=None):
+ @mock.patch("functest.energy.energy.requests.get",
+ return_value=API_OK)
+ def test_load_config(self, loader_mock=None, pod_mock=None,
+ get_mock=None):
"""Test load config."""
EnergyRecorder.energy_recorder_api = None
EnergyRecorder.load_config()
+
self.assertEquals(
EnergyRecorder.energy_recorder_api["auth"],
("user", "password")
@@ -281,7 +297,10 @@ class EnergyRecorderTest(unittest.TestCase):
side_effect=config_loader_mock_no_creds)
@mock.patch("functest.utils.functest_utils.get_pod_name",
return_value="MOCK_POD")
- def test_load_config_no_creds(self, loader_mock=None, pod_mock=None):
+ @mock.patch("functest.energy.energy.requests.get",
+ return_value=API_OK)
+ def test_load_config_no_creds(self, loader_mock=None, pod_mock=None,
+ get_mock=None):
"""Test load config without creds."""
EnergyRecorder.energy_recorder_api = None
EnergyRecorder.load_config()
@@ -295,7 +314,10 @@ class EnergyRecorderTest(unittest.TestCase):
return_value=None)
@mock.patch("functest.utils.functest_utils.get_pod_name",
return_value="MOCK_POD")
- def test_load_config_ex(self, loader_mock=None, pod_mock=None):
+ @mock.patch("functest.energy.energy.requests.get",
+ return_value=API_OK)
+ def test_load_config_ex(self, loader_mock=None, pod_mock=None,
+ get_mock=None):
"""Test load config with exception."""
with self.assertRaises(AssertionError):
EnergyRecorder.energy_recorder_api = None
@@ -303,6 +325,20 @@ class EnergyRecorderTest(unittest.TestCase):
self.assertEquals(EnergyRecorder.energy_recorder_api, None)
@mock.patch("functest.utils.functest_utils.get_functest_config",
+ side_effect=config_loader_mock)
+ @mock.patch("functest.utils.functest_utils.get_pod_name",
+ return_value="MOCK_POD")
+ @mock.patch("functest.energy.energy.requests.get",
+ return_value=API_KO)
+ def test_load_config_api_ko(self, loader_mock=None, pod_mock=None,
+ get_mock=None):
+ """Test load config with API unavailable."""
+ EnergyRecorder.energy_recorder_api = None
+ EnergyRecorder.load_config()
+ self.assertEquals(EnergyRecorder.energy_recorder_api["available"],
+ False)
+
+ @mock.patch("functest.utils.functest_utils.get_functest_config",
return_value=None)
@mock.patch("functest.utils.functest_utils.get_pod_name",
return_value="MOCK_POD")