self.redfish_session_location: str = ''
def _cp_dispatch(self, vpath: List[str]) -> "NodeProxy":
- if len(vpath) == 2:
- hostname = vpath.pop(0)
+ if len(vpath) > 1: # /{hostname}/<endpoint>
+ hostname = vpath.pop(0) # /<endpoint>
cherrypy.request.params['hostname'] = hostname
+ # /{hostname}/led/{type}/{drive} eg: /{hostname}/led/chassis or /{hostname}/led/drive/{id}
+ if vpath[0] == 'led' and len(vpath) > 1: # /led/{type}/{id}
+ _type = vpath[1]
+ cherrypy.request.params['type'] = _type
+ vpath.pop(1) # /led/{id} or # /led
+ if _type == 'drive' and len(vpath) > 1: # /led/{id}
+ _id = vpath[1]
+ vpath.pop(1) # /led
+ cherrypy.request.params['id'] = _id
+ # /<endpoint>
return self
@cherrypy.expose
url = f'https://{addr}:{port}{endpoint}'
_headers = headers
response_json = {}
+ response_headers = {}
if not _headers.get('Content-Type'):
# default to application/json if nothing provided
_headers['Content-Type'] = 'application/json'
"""
method: str = cherrypy.request.method
hostname: Optional[str] = kw.get('hostname')
+ led_type: Optional[str] = kw.get('type')
+ id_drive: Optional[str] = kw.get('id')
if not hostname:
msg: str = "listing enclosure LED status for all nodes is not implemented."
self.mgr.log.debug(msg)
raise cherrypy.HTTPError(501, msg)
+ if not led_type:
+ msg = "the led type must be provided (either 'chassis' or 'drive')."
+ self.mgr.log.debug(msg)
+ raise cherrypy.HTTPError(400, msg)
+
+ if led_type == 'drive' and not id_drive:
+ msg = "the id of the drive must be provided when type is 'drive'."
+ self.mgr.log.debug(msg)
+ raise cherrypy.HTTPError(400, msg)
+
+ if hostname not in self.mgr.node_proxy.data.keys():
+ # TODO(guits): update unit test for this
+ msg = f"'{hostname}' not found."
+ self.mgr.log.debug(msg)
+ raise cherrypy.HTTPError(400, msg)
+
+ # if led_type not in ['chassis', 'drive']:
+ # # TODO(guits): update unit test for this
+ # raise cherrypy.HTTPError(404, 'LED type must be either "chassis" or "drive"')
+
addr = self.mgr.node_proxy.oob[hostname]['addr']
port = self.mgr.node_proxy.oob[hostname]['port']
username = self.mgr.node_proxy.oob[hostname]['username']
# allowing a specific keyring only ? (client.admin or client.agent.. ?)
data: str = json.dumps(cherrypy.request.json)
+ if led_type == 'drive':
+ if id_drive not in self.mgr.node_proxy.data[hostname]['status']['storage'].keys():
+ # TODO(guits): update unit test for this
+ msg = f"'{id_drive}' not found."
+ self.mgr.log.debug(msg)
+ raise cherrypy.HTTPError(400, msg)
+ endpoint = self.mgr.node_proxy.data[hostname]['status']['storage'][id_drive].get('redfish_endpoint')
+ else:
+ endpoint = self.mgr.node_proxy.data[hostname]['chassis']['redfish_endpoint']
+
with self.redfish_session(addr, username, password, port=port):
try:
status, result, _ = self.query(data=bytes(data, 'ascii'),
method=method,
headers={"X-Auth-Token": self.redfish_token},
port=port,
- endpoint='/redfish/v1/Chassis/System.Embedded.1',
+ endpoint=endpoint,
ssl_ctx=self.ssl_ctx)
- except (URLError, HTTPError) as e:
+ except (URLError, HTTPError, RuntimeError) as e:
raise cherrypy.HTTPError(502, f"{e}")
if method == 'GET':
result = {"LocationIndicatorActive": result['LocationIndicatorActive']}
from _pytest.monkeypatch import MonkeyPatch
from cherrypy.test import helper
from cephadm.agent import NodeProxy
-from unittest.mock import MagicMock, call
-from cephadm.http_server import CephadmHttpServer
+from unittest.mock import MagicMock, call, patch
from cephadm.inventory import AgentCache, NodeProxyCache, Inventory
from cephadm.ssl_cert_utils import SSLCerts
-from urllib.error import URLError
from . import node_proxy_data
PORT = 58585
self.remove_health_warning = MagicMock()
self.inventory = Inventory(self)
self.agent_cache = AgentCache(self)
+ self.agent_cache.agent_ports = {"host01": 1234}
self.node_proxy = NodeProxyCache(self)
self.node_proxy.save = MagicMock()
self.http_server = MagicMock()
('Content-Length', str(len(data)))])
self.assertStatus('501 Not Implemented')
- def test_set_led(self):
- data = '{"state": "on"}'
- TestNodeProxy.app.query_endpoint = MagicMock(return_value=(200, "OK"))
- # self.monkeypatch.setattr(NodeProxy, "query_endpoint", lambda *a, **kw: (200, "OK"))
+ @patch('cephadm.agent.AgentMessageThread.join', return_value=MagicMock)
+ @patch('cephadm.agent.AgentMessageThread.start', return_value=MagicMock)
+ def test_set_led_no_type(self, m_agent_msg_thread_start, m_agent_msg_thread_join):
+ data = '{"IndicatorLED": "Blinking"}'
self.getPage("/host01/led", method="PATCH", body=data, headers=[('Content-Type', 'application/json'),
('Content-Length', str(len(data)))])
+ self.assertStatus('400 Bad Request')
- calls = [call(addr='10.10.10.11',
- data='{"state": "on"}',
- endpoint='/led',
- headers={'Authorization': 'Basic aWRyYWMtdXNlcjAxOmlkcmFjLXBhc3MwMQ=='},
- method='PATCH',
- port=8080,
- ssl_ctx=TestNodeProxy.app.ssl_ctx)]
- self.assertStatus('200 OK')
- assert TestNodeProxy.app.query_endpoint.mock_calls == calls
-
- def test_get_led(self):
- TestNodeProxy.app.query_endpoint = MagicMock(return_value=(200, "OK"))
+ @patch('cephadm.agent.AgentMessageThread.join', return_value=MagicMock)
+ @patch('cephadm.agent.AgentMessageThread.start', return_value=MagicMock)
+ def test_set_chassis_led(self, m_agent_msg_thread_start, m_agent_msg_thread_join):
+ data = '{"IndicatorLED": "Blinking"}'
+ with patch('cephadm.agent.AgentMessageThread.get_agent_response') as a:
+ a.return_value = '{"http_code": 200}'
+ self.getPage("/host01/led/chassis", method="PATCH", body=data, headers=[('Content-Type', 'application/json'),
+ ('Content-Length', str(len(data)))])
+ self.assertStatus('200 OK')
+
+ def test_get_led_missing_type(self):
self.getPage("/host01/led", method="GET")
- calls = [call(addr='10.10.10.11',
- data=None,
- endpoint='/led',
- headers={},
- method='GET',
- port=8080,
- ssl_ctx=TestNodeProxy.app.ssl_ctx)]
- self.assertStatus('200 OK')
- assert TestNodeProxy.app.query_endpoint.mock_calls == calls
-
- def test_led_endpoint_unreachable(self):
- TestNodeProxy.app.query_endpoint = MagicMock(side_effect=URLError("fake-error"))
- self.getPage("/host02/led", method="GET")
- calls = [call(addr='10.10.10.12',
- data=None,
- endpoint='/led',
- headers={},
- method='GET',
- port=8080,
- ssl_ctx=TestNodeProxy.app.ssl_ctx)]
+ self.assertStatus('400 Bad Request')
+
+ def test_get_led_no_hostname(self):
+ self.getPage("/led", method="GET")
+ self.assertStatus('501 Not Implemented')
+
+ def test_get_led_type_chassis_no_hostname(self):
+ self.getPage("/led/chassis", method="GET")
+ self.assertStatus('404 Not Found')
+
+ def test_get_led_type_drive_no_hostname(self):
+ self.getPage("/led/chassis", method="GET")
+ self.assertStatus('404 Not Found')
+
+ def test_get_led_type_drive_missing_id(self):
+ self.getPage("/host01/led/drive", method="GET")
+ self.assertStatus('400 Bad Request')
+
+ def test_get_led_type_chassis_answer_invalid_json(self):
+ self.getPage("/host01/led/chassis", method="GET")
+ self.assertStatus('503 Service Unavailable')
+
+ @patch('cephadm.agent.AgentMessageThread.join', return_value=MagicMock)
+ @patch('cephadm.agent.AgentMessageThread.start', return_value=MagicMock)
+ def test_get_led_type_chassis_answer_no_http_code(self, m_agent_msg_thread_start, m_agent_msg_thread_join):
+ with patch('cephadm.agent.AgentMessageThread.get_agent_response') as a:
+ a.return_value = '{"foo": "bar"}'
+ self.getPage("/host01/led/chassis", method="GET")
+ self.assertStatus('503 Service Unavailable')
+
+ def test_get_led_status_not_200(self):
+ self.getPage("/host01/led/chassis", method="GET")
+ self.assertStatus('503 Service Unavailable')
+
+ def test_get_led_key_error(self):
+ self.getPage("/host02/led/chassis", method="GET")
self.assertStatus('502 Bad Gateway')
- assert TestNodeProxy.app.query_endpoint.mock_calls == calls
+
+ @patch('cephadm.agent.AgentMessageThread.join', return_value=MagicMock)
+ @patch('cephadm.agent.AgentMessageThread.start', return_value=MagicMock)
+ def test_get_chassis_led_ok(self, m_agent_msg_thread_start, m_agent_msg_thread_join):
+ with patch('cephadm.agent.AgentMessageThread.get_agent_response') as a:
+ a.return_value = '{"http_code": 200}'
+ self.getPage("/host01/led/chassis", method="GET")
+ self.assertStatus('200 OK')
+
+ @patch('cephadm.agent.AgentMessageThread.join', return_value=MagicMock)
+ @patch('cephadm.agent.AgentMessageThread.start', return_value=MagicMock)
+ def test_get_drive_led_without_id(self, m_agent_msg_thread_start, m_agent_msg_thread_join):
+ self.getPage("/host01/led/drive", method="GET")
+ self.assertStatus('400 Bad Request')
+
+ @patch('cephadm.agent.AgentMessageThread.join', return_value=MagicMock)
+ @patch('cephadm.agent.AgentMessageThread.start', return_value=MagicMock)
+ def test_get_drive_led_with_id(self, m_agent_msg_thread_start, m_agent_msg_thread_join):
+ with patch('cephadm.agent.AgentMessageThread.get_agent_response') as a:
+ a.return_value = '{"http_code": 200}'
+ self.getPage("/host01/led/drive/123", method="GET")
+ self.assertStatus('200 OK')
+
+ # def test_led_endpoint_unreachable(self):
+ # TestNodeProxy.app.query_endpoint = MagicMock(side_effect=URLError("fake-error"))
+ # self.getPage("/host02/led", method="GET")
+ # calls = [call(addr='10.10.10.12',
+ # data=None,
+ # endpoint='/led',
+ # headers={},
+ # method='GET',
+ # port=8080,
+ # ssl_ctx=TestNodeProxy.app.ssl_ctx)]
+ # self.assertStatus('502 Bad Gateway')
+ # assert TestNodeProxy.app.query_endpoint.mock_calls == calls
def test_fullreport_with_valid_hostname(self):
self.getPage("/host02/fullreport", method="GET")