33from ceph_node_proxy .basesystem import BaseSystem
44from ceph_node_proxy .redfish_client import RedFishClient
55from time import sleep
6- from ceph_node_proxy .util import get_logger
6+ from ceph_node_proxy .util import get_logger , to_snake_case
77from typing import Dict , Any , List , Callable , Union
88from urllib .error import HTTPError , URLError
99
1010
11+ class EndpointMgr :
12+ NAME : str = 'EndpointMgr'
13+
14+ def __init__ (self ,
15+ client : RedFishClient ,
16+ prefix : str = RedFishClient .PREFIX ) -> None :
17+ self .log = get_logger (f'{ __name__ } :{ EndpointMgr .NAME } ' )
18+ self .prefix : str = prefix
19+ self .client : RedFishClient = client
20+
21+ def __getitem__ (self , index : str ) -> Any :
22+ if index in self .__dict__ :
23+ return self .__dict__ [index ]
24+ else :
25+ raise RuntimeError (f'{ index } is not a valid endpoint.' )
26+
27+ def init (self ) -> None :
28+ _error_msg : str = "Can't discover entrypoint(s)"
29+ try :
30+ _ , _data , _ = self .client .query (endpoint = self .prefix )
31+ json_data : Dict [str , Any ] = json .loads (_data )
32+ for k , v in json_data .items ():
33+ if '@odata.id' in v :
34+ self .log .debug (f'entrypoint found: { to_snake_case (k )} = { v ["@odata.id" ]} ' )
35+ _name : str = to_snake_case (k )
36+ _url : str = v ['@odata.id' ]
37+ e = Endpoint (self , _url , self .client )
38+ setattr (self , _name , e )
39+ setattr (self , 'session' , json_data ['Links' ]['Sessions' ]['@odata.id' ]) # TODO(guits): needs to be fixed
40+ except (URLError , KeyError ) as e :
41+ msg = f'{ _error_msg } : { e } '
42+ self .log .error (msg )
43+ raise RuntimeError
44+
45+
46+ class Endpoint :
47+ NAME : str = 'Endpoint'
48+
49+ def __init__ (self , url : str , client : RedFishClient ) -> None :
50+ self .log = get_logger (f'{ __name__ } :{ Endpoint .NAME } ' )
51+ self .url : str = url
52+ self .client : RedFishClient = client
53+ self .data : Dict [str , Any ] = self .get_data ()
54+ self .id : str = ''
55+ self .members_names : List [str ] = []
56+
57+ if self .has_members :
58+ self .members_names = self .get_members_names ()
59+
60+ if self .data :
61+ try :
62+ self .id = self .data ['Id' ]
63+ except KeyError :
64+ self .id = self .data ['@odata.id' ].split ('/' )[- 1 :]
65+ else :
66+ self .log .warning (f'No data could be loaded for { self .url } ' )
67+
68+ def __getitem__ (self , index : str ) -> Any :
69+ if not getattr (self , index , False ):
70+ _url : str = f'{ self .url } /{ index } '
71+ setattr (self , index , Endpoint (_url , self .client ))
72+ return self .__dict__ [index ]
73+
74+ def query (self , url : str ) -> Dict [str , Any ]:
75+ data : Dict [str , Any ] = {}
76+ try :
77+ self .log .debug (f'Querying { url } ' )
78+ _ , _data , _ = self .client .query (endpoint = url )
79+ data = json .loads (_data )
80+ except KeyError as e :
81+ self .log .error (f'Error while querying { self .url } : { e } ' )
82+ return data
83+
84+ def get_data (self ) -> Dict [str , Any ]:
85+ return self .query (self .url )
86+
87+ def get_members_names (self ) -> List [str ]:
88+ result : List [str ] = []
89+ if self .has_members :
90+ for member in self .data ['Members' ]:
91+ name : str = member ['@odata.id' ].split ('/' )[- 1 :][0 ]
92+ result .append (name )
93+ return result
94+
95+ def get_name (self , endpoint : str ) -> str :
96+ return endpoint .split ('/' )[- 1 :][0 ]
97+
98+ def get_members_endpoints (self ) -> Dict [str , str ]:
99+ members : Dict [str , str ] = {}
100+ name : str = ''
101+ if self .has_members :
102+ for member in self .data ['Members' ]:
103+ name = self .get_name (member ['@odata.id' ])
104+ members [name ] = member ['@odata.id' ]
105+ else :
106+ name = self .get_name (self .data ['@odata.id' ])
107+ members [name ] = self .data ['@odata.id' ]
108+
109+ return members
110+
111+ def get_members_data (self ) -> Dict [str , Any ]:
112+ result : Dict [str , Any ] = {}
113+ if self .has_members :
114+ for member , endpoint in self .get_members_endpoints ().items ():
115+ result [member ] = self .query (endpoint )
116+ return result
117+
118+ @property
119+ def has_members (self ) -> bool :
120+ return 'Members' in self .data .keys ()
121+
122+
11123class BaseRedfishSystem (BaseSystem ):
12124 def __init__ (self , ** kw : Any ) -> None :
13125 super ().__init__ (** kw )
14- self .common_endpoints : List [str ] = kw .get ('common_endpoints' , ['/Systems/System.Embedded.1' ,
15- '/UpdateService' ])
16- self .chassis_endpoint : str = kw .get ('chassis_endpoint' , '/Chassis/System.Embedded.1' )
17126 self .log = get_logger (__name__ )
18127 self .host : str = kw ['host' ]
19128 self .port : str = kw ['port' ]
20129 self .username : str = kw ['username' ]
21130 self .password : str = kw ['password' ]
22131 # move the following line (class attribute?)
23132 self .client : RedFishClient = RedFishClient (host = self .host , port = self .port , username = self .username , password = self .password )
133+ self .endpoints : EndpointMgr = EndpointMgr (self .client )
24134 self .log .info (f'redfish system initialization, host: { self .host } , user: { self .username } ' )
25135 self .data_ready : bool = False
26136 self .previous_data : Dict = {}
@@ -48,6 +158,8 @@ def __init__(self, **kw: Any) -> None:
48158 def main (self ) -> None :
49159 self .stop = False
50160 self .client .login ()
161+ self .endpoints .init ()
162+
51163 while not self .stop :
52164 self .log .debug ('waiting for a lock in the update loop.' )
53165 with self .lock :
@@ -100,9 +212,7 @@ def _get_path(self, path: str) -> Dict:
100212 return result
101213
102214 def get_members (self , data : Dict [str , Any ], path : str ) -> List :
103- _path = data [path ]['@odata.id' ]
104- _data = self ._get_path (_path )
105- return [self ._get_path (member ['@odata.id' ]) for member in _data ['Members' ]]
215+ return [self ._get_path (member ['@odata.id' ]) for member in data ['Members' ]]
106216
107217 def get_system (self ) -> Dict [str , Any ]:
108218 result = {
@@ -117,15 +227,18 @@ def get_system(self) -> Dict[str, Any]:
117227 'fans' : self .get_fans ()
118228 },
119229 'firmwares' : self .get_firmwares (),
120- 'chassis' : {'redfish_endpoint' : f'/redfish/v1{ self .chassis_endpoint } ' } # TODO(guits): not ideal
121230 }
122231 return result
123232
124233 def _update_system (self ) -> None :
125- for endpoint in self .common_endpoints :
126- result = self .client .get_path (endpoint )
127- _endpoint = endpoint .strip ('/' ).split ('/' )[0 ]
128- self ._system [_endpoint ] = result
234+ system_members : Dict [str , Any ] = self .endpoints ['systems' ].get_members_data ()
235+ update_service_members : Endpoint = self .endpoints ['update_service' ]
236+
237+ for member , data in system_members .items ():
238+ self ._system [member ] = data
239+ self ._sys [member ] = dict ()
240+
241+ self ._system [update_service_members .id ] = update_service_members .data
129242
130243 def _update_sn (self ) -> None :
131244 raise NotImplementedError ()
@@ -196,7 +309,7 @@ def get_device_led(self, device: str) -> Dict[str, Any]:
196309
197310 def set_device_led (self , device : str , data : Dict [str , bool ]) -> int :
198311 try :
199- _ , response , status = self .client .query (
312+ _ , _ , status = self .client .query (
200313 data = json .dumps (data ),
201314 method = 'PATCH' ,
202315 endpoint = self ._sys ['storage' ][device ]['redfish_endpoint' ]
@@ -207,7 +320,7 @@ def set_device_led(self, device: str, data: Dict[str, bool]) -> int:
207320 return status
208321
209322 def get_chassis_led (self ) -> Dict [str , Any ]:
210- endpoint = f'/redfish/v1/ { self .chassis_endpoint } '
323+ endpoint = list ( self .endpoints [ 'chassis' ]. get_members_endpoints (). values ())[ 0 ]
211324 try :
212325 result = self .client .query (method = 'GET' ,
213326 endpoint = endpoint ,
@@ -227,10 +340,10 @@ def set_chassis_led(self, data: Dict[str, str]) -> int:
227340 # '{"IndicatorLED": "Lit"}' -> LocationIndicatorActive = false
228341 # '{"IndicatorLED": "Blinking"}' -> LocationIndicatorActive = true
229342 try :
230- _ , response , status = self .client .query (
343+ _ , _ , status = self .client .query (
231344 data = json .dumps (data ),
232345 method = 'PATCH' ,
233- endpoint = f'/redfish/v1 { self .chassis_endpoint } '
346+ endpoint = list ( self .endpoints [ 'chassis' ]. get_members_endpoints (). values ())[ 0 ]
234347 )
235348 except HTTPError as e :
236349 self .log .error (f"Couldn't set the ident chassis LED: { e } " )
@@ -260,7 +373,7 @@ def powercycle(self) -> int:
260373 def create_reboot_job (self , reboot_type : str ) -> str :
261374 data : Dict [str , str ] = dict (RebootJobType = reboot_type )
262375 try :
263- headers , response , status = self .client .query (
376+ headers , _ , _ = self .client .query (
264377 data = json .dumps (data ),
265378 endpoint = self .create_reboot_job_endpoint
266379 )
@@ -273,7 +386,7 @@ def create_reboot_job(self, reboot_type: str) -> str:
273386 def schedule_reboot_job (self , job_id : str ) -> int :
274387 data : Dict [str , Union [List [str ], str ]] = dict (JobArray = [job_id ], StartTimeInterval = 'TIME_NOW' )
275388 try :
276- headers , response , status = self .client .query (
389+ _ , _ , status = self .client .query (
277390 data = json .dumps (data ),
278391 endpoint = self .setup_job_queue_endpoint
279392 )
0 commit comments