66 "log"
77 "math"
88 "net/http"
9+ "reflect"
10+ "regexp"
11+ "strings"
912 "time"
1013
1114 "github.com/nod-ai/ADA/redfish-exporter/api/generated/slurmrestdapi"
@@ -50,11 +53,13 @@ type SlurmServerConfig struct {
5053
5154type Client struct {
5255 apiClient * slurmrestdapi.APIClient // slurm URL to client mapping
56+ helper map [string ]reflect.Method
5357}
5458
55- var apiCl * Client // singleton client
59+ var singletonAPICl * Client // singleton client
5660
5761func NewClient (slurmControlNode , slurmUser , slurmToken string ) (* Client , error ) {
62+ c := & Client {}
5863 slConfig := & SlurmServerConfig {
5964 URL : slurmControlNode ,
6065 Username : defaultSlurmUsername ,
@@ -64,125 +69,175 @@ func NewClient(slurmControlNode, slurmUser, slurmToken string) (*Client, error)
6469 slConfig .Username = slurmUser
6570 }
6671 cl := createRestClient (slConfig )
67- c := & Client {apiClient : cl }
72+
73+ // populate the methods required for ping and node update operations
74+ t := reflect .TypeOf (cl .SlurmAPI )
75+ postNodeRe := regexp .MustCompile (fmt .Sprintf (`%s$` , methodPostNode ))
76+ pingRe := regexp .MustCompile (fmt .Sprintf (`%s$` , methodPing ))
77+ for i := 0 ; i < t .NumMethod (); i ++ {
78+ method := t .Method (i )
79+ if postNodeRe .MatchString (method .Name ) {
80+ postNodeExecuteMethod , found := t .MethodByName (withExecuteSuffix (method .Name ))
81+ if ! found {
82+ return nil , fmt .Errorf ("could not find PostNodeExecute method from Slurm REST APIs" )
83+ }
84+
85+ if _ , found := c .helper [methodPostNode ]; ! found {
86+ c .helper [methodPostNode ] = method
87+ c .helper [withExecuteSuffix (methodPostNode )] = postNodeExecuteMethod
88+ }
89+ } else if pingRe .MatchString (method .Name ) {
90+ pingExecuteMethod , found := t .MethodByName (withExecuteSuffix (method .Name ))
91+ if ! found {
92+ return nil , fmt .Errorf ("could not find PingExecute method from Slurm REST APIs" )
93+ }
94+
95+ if _ , found := c .helper [methodPing ]; ! found {
96+ c .helper [methodPing ] = method
97+ c .helper [withExecuteSuffix (methodPing )] = pingExecuteMethod
98+ }
99+ }
100+ }
101+
102+ c .apiClient = cl
68103
69104 log .Printf ("[slurm] created slurm client for node: %v\n " , slurmControlNode )
70105 err := c .getConnectionStatus ()
71106 if err != nil {
72107 log .Printf ("[slurm] error in getting the connection status of the slurm node: %v, err: %+v\n " , slurmControlNode , err )
73108 }
74109
75- apiCl = c
110+ singletonAPICl = c
76111 return c , err
77112}
78113
79114func GetClient () * Client {
80- return apiCl
115+ return singletonAPICl
81116}
82117
83118func (c * Client ) ResumeNode (nodeName string ) error {
84- apiCall := func () (interface {}, * http.Response , error ) {
85- ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Second )
86- jreq := c .apiClient .SlurmAPI .SlurmV0040PostNode (ctx , nodeName )
87- req := slurmrestdapi.V0040UpdateNodeMsg {State : []string {"resume" }}
88- jreq = jreq .V0040UpdateNodeMsg (req )
89- res , resp , err := c .apiClient .SlurmAPI .SlurmV0040PostNodeExecute (jreq )
90- cancel ()
91- if err != nil {
92- return res , resp , err
93- } else if resp .StatusCode != 200 {
94- return res , resp , fmt .Errorf ("invalid status code: %v" , resp .StatusCode )
95- }
96- return res , resp , nil
97- }
98-
99- _ , resp , err := CallWithRetry (apiCall , maxRetries , baseDelay )
100- if err != nil {
101- return err
102- }
103- defer resp .Body .Close ()
104-
105- return nil
119+ return c .updateNodeState (nodeName , "resume" )
106120}
107121
108122func (c * Client ) DrainNode (nodeName string ) error {
123+ return c .updateNodeState (nodeName , "drain" )
124+ }
125+
126+ func (c * Client ) getConnectionStatus () error {
109127 apiCall := func () (interface {}, * http.Response , error ) {
110128 ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Second )
111- jreq := c .apiClient .SlurmAPI .SlurmV0040PostNode (ctx , nodeName )
112- req := slurmrestdapi.V0040UpdateNodeMsg {State : []string {"drain" }}
113- jreq = jreq .V0040UpdateNodeMsg (req )
114- res , resp , err := c .apiClient .SlurmAPI .SlurmV0040PostNodeExecute (jreq )
115- cancel ()
116- if err != nil {
117- return res , resp , err
118- } else if resp .StatusCode != 200 {
119- return res , resp , fmt .Errorf ("invalid status code: %v" , resp .StatusCode )
129+ defer cancel ()
130+
131+ // Step 1: Call the Ping method using reflection
132+ pingVals := c .helper ["Ping" ].Func .Call ([]reflect.Value {
133+ reflect .ValueOf (c .apiClient .SlurmAPI ),
134+ reflect .ValueOf (ctx ),
135+ })
136+
137+ // Check if the call produced results
138+ if len (pingVals ) == 0 {
139+ return nil , nil , fmt .Errorf ("Ping call returned no values" )
120140 }
121- return res , resp , nil
141+
142+ // Step 2: Execute the Ping method with the request
143+ pingResp := c .helper ["PingExecute" ].Func .Call ([]reflect.Value {
144+ reflect .ValueOf (c .apiClient .SlurmAPI ),
145+ pingVals [0 ],
146+ })
147+
148+ // Extract and return the response and error
149+ resp , _ := pingResp [1 ].Interface ().(* http.Response )
150+ err , _ := pingResp [2 ].Interface ().(error )
151+ return pingResp [0 ].Interface (), resp , err
122152 }
123153
124154 _ , resp , err := CallWithRetry (apiCall , maxRetries , baseDelay )
125155 if err != nil {
126- return err
156+ return nil
127157 }
128158 defer resp .Body .Close ()
129159
160+ log .Printf ("[slurm] ping success: %v\n " , resp .StatusCode )
130161 return nil
131162}
132163
133- func (c * Client ) GetNodes () ([]string , error ) {
134- var nodes []string
164+ func (c * Client ) updateNodeState (nodeName , state string ) error {
135165 apiCall := func () (interface {}, * http.Response , error ) {
136166 ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Second )
137- jreq := c .apiClient .SlurmAPI .SlurmV0039GetNodes (ctx )
138- res , resp , err := c .apiClient .SlurmAPI .SlurmV0039GetNodesExecute (jreq )
139- cancel ()
140- if err != nil {
141- return res , resp , err
142- } else if resp .StatusCode != 200 {
143- return res , resp , fmt .Errorf ("invalid status code: %v" , resp .StatusCode )
167+ defer cancel ()
168+
169+ // Step 1: Call the PostNode method using reflection
170+ postNodeVals := c .helper ["PostNode" ].Func .Call ([]reflect.Value {
171+ reflect .ValueOf (c .apiClient .SlurmAPI ),
172+ reflect .ValueOf (ctx ),
173+ reflect .ValueOf (nodeName ),
174+ })
175+
176+ // Check if the call produced results
177+ if len (postNodeVals ) == 0 {
178+ return nil , nil , fmt .Errorf ("PostNode call returned no values" )
144179 }
145- return res , resp , nil
146- }
147-
148- res , resp , err := CallWithRetry (apiCall , maxRetries , baseDelay )
149- if err != nil {
150- return nodes , err
151- }
152- defer resp .Body .Close ()
153-
154- log .Printf ("[slurm] get nodes: %+v\n " , nodes )
155- temp := res .(* slurmrestdapi.V0039NodesResponse )
156- for _ , node := range temp .GetNodes () {
157- nodes = append (nodes , * node .Name )
158- }
159- return nodes , nil
160- }
161180
162- func (c * Client ) getConnectionStatus () error {
163- apiCall := func () (interface {}, * http.Response , error ) {
164- ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Second )
165- jreq := c .apiClient .SlurmAPI .SlurmV0039Ping (ctx )
166- res , resp , err := c .apiClient .SlurmAPI .SlurmV0039PingExecute (jreq )
167- cancel ()
168- if err != nil {
169- return res , resp , err
170- } else if resp .StatusCode != 200 {
171- return res , resp , fmt .Errorf ("invalid status code: %v" , resp .StatusCode )
181+ // Step 2: Find and call the UpdateNodeMsg method on the request object
182+ newInstance := reflect .New (postNodeVals [0 ].Type ()).Elem ()
183+ instanceType := newInstance .Type ()
184+
185+ for i := 0 ; i < instanceType .NumMethod (); i ++ {
186+ method := instanceType .Method (i )
187+ if strings .Contains (method .Name , "UpdateNodeMsg" ) {
188+ // Create a new UpdateNodeMsg request
189+ updateNodeMsgReq := createUpdateNodeMsgRequest (method .Type )
190+ if updateNodeMsgReq .IsValid () {
191+ updateNodeMsgReq .FieldByName ("State" ).Set (reflect .ValueOf ([]string {state }))
192+ }
193+
194+ // Step 3: Call UpdateNodeMsg with the request
195+ updatedNodeVals := method .Func .Call ([]reflect.Value {postNodeVals [0 ], updateNodeMsgReq })
196+ if len (updatedNodeVals ) == 0 {
197+ return nil , nil , fmt .Errorf ("UpdateNodeMsg call returned no values" )
198+ }
199+
200+ // Step 4: Execute the PostNode method with the updated request
201+ postNodeResp := c .helper ["PostNodeExecute" ].Func .Call ([]reflect.Value {
202+ reflect .ValueOf (c .apiClient .SlurmAPI ),
203+ updatedNodeVals [0 ],
204+ })
205+
206+ if len (postNodeResp ) < 3 {
207+ return nil , nil , fmt .Errorf ("PostNodeExecute call returned insufficient values" )
208+ }
209+
210+ // Extract and return the response and error
211+ resp , _ := postNodeResp [1 ].Interface ().(* http.Response )
212+ err , _ := postNodeResp [2 ].Interface ().(error )
213+ return postNodeResp [0 ].Interface (), resp , err
214+ }
172215 }
173- return res , resp , nil
216+
217+ return nil , nil , fmt .Errorf ("no suitable UpdateNodeMsg method found" )
174218 }
175219
220+ // Retry the API call
176221 _ , resp , err := CallWithRetry (apiCall , maxRetries , baseDelay )
177222 if err != nil {
178- return nil
223+ return err
179224 }
180225 defer resp .Body .Close ()
181226
182- log .Printf ("[slurm] ping success: %v\n " , resp .StatusCode )
183227 return nil
184228}
185229
230+ // Helper function to create an UpdateNodeMsg request using reflection
231+ func createUpdateNodeMsgRequest (methodType reflect.Type ) reflect.Value {
232+ for j := 1 ; j < methodType .NumIn (); j ++ { // Start from 1 to skip the receiver
233+ paramType := methodType .In (j )
234+ if strings .Contains (paramType .Name (), "UpdateNodeMsg" ) {
235+ return reflect .New (paramType ).Elem ()
236+ }
237+ }
238+ return reflect.Value {}
239+ }
240+
186241func createRestClient (c * SlurmServerConfig ) * slurmrestdapi.APIClient {
187242 cfg := slurmrestdapi .NewConfiguration ()
188243 cfg .HTTPClient = & http.Client {Timeout : slurmRestClientTimeout }
0 commit comments