@@ -18,113 +18,42 @@ package ironic
1818
1919import (
2020 "context"
21- "crypto/tls"
2221 "fmt"
23- "net/http"
24- "strings"
25- "time"
2622
2723 "github.com/gophercloud/gophercloud/v2"
28- "github.com/gophercloud/gophercloud/v2/openstack/baremetal/noauth"
2924 "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes"
25+ "github.com/gophercloud/utils/v2/openstack/clientconfig"
3026)
3127
32- // microversion is the Ironic API version required for power management flows.
33- const microversion = "1.69"
34-
3528// Client talks to Ironic over REST via gophercloud.
36- type Client struct {
37- serviceClient * gophercloud.ServiceClient
38- }
39-
40- // ClientOptions configures the Ironic client.
41- type ClientOptions struct {
42- InsecureSkipVerify bool
43- HTTPClient * http.Client
44- }
45-
46- type tokenRoundTripper struct {
47- rt http.RoundTripper
48- token string
49- }
50-
51- func (t * tokenRoundTripper ) RoundTrip (req * http.Request ) (* http.Response , error ) {
52- req = req .Clone (req .Context ())
53- req .Header .Set ("X-Auth-Token" , t .token )
54- return t .rt .RoundTrip (req )
55- }
56-
57- // ensureV1Suffix returns base URL with trailing /v1 for gophercloud noauth.
58- func ensureV1Suffix (endpoint string ) string {
59- endpoint = strings .TrimSuffix (endpoint , "/" )
60- if strings .HasSuffix (endpoint , "/v1" ) {
61- return endpoint
62- }
63- return endpoint + "/v1"
64- }
65-
66- // NewClient creates an Ironic client with no-auth (typical for in-cluster Ironic with no Keystone).
67- func NewClient (ironicEndpoint string , opts ClientOptions ) (* Client , error ) {
68- return newClient (ensureV1Suffix (ironicEndpoint ), "" , opts )
69- }
70-
71- // NewClientWithToken creates an Ironic client that sends X-Auth-Token on every request.
72- func NewClientWithToken (ironicEndpoint , authToken string , opts ClientOptions ) (* Client , error ) {
73- return newClient (ensureV1Suffix (ironicEndpoint ), authToken , opts )
74- }
75-
76- func newClient (ironicEndpoint , authToken string , opts ClientOptions ) (* Client , error ) {
77- client , err := noauth .NewBareMetalNoAuth (noauth.EndpointOpts {
78- IronicEndpoint : ironicEndpoint ,
79- })
80- if err != nil {
81- return nil , fmt .Errorf ("create ironic client: %w" , err )
29+ type (
30+ Client struct {
31+ serviceClient * gophercloud.ServiceClient
8232 }
8333
84- client .Microversion = microversion
85-
86- baseTransport := http .DefaultTransport
87- if opts .InsecureSkipVerify {
88- if t , ok := baseTransport .(* http.Transport ); ok {
89- clone := t .Clone ()
90- clone .TLSClientConfig = & tls.Config {InsecureSkipVerify : true } //nolint:gosec
91- baseTransport = clone
92- } else {
93- baseTransport = & http.Transport {
94- TLSClientConfig : & tls.Config {InsecureSkipVerify : true }, //nolint:gosec
95- }
96- }
34+ TargetPowerState struct {
35+ powerstate nodes.TargetPowerState
9736 }
37+ )
9838
99- transport := baseTransport
100- if authToken != "" {
101- transport = & tokenRoundTripper {rt : baseTransport , token : authToken }
102- }
39+ var (
40+ PowerOn = TargetPowerState {nodes .PowerOn }
41+ PowerOff = TargetPowerState {nodes .PowerOff }
42+ Rebooting = TargetPowerState {nodes .Rebooting }
43+ SoftPowerOff = TargetPowerState {nodes .SoftPowerOff }
44+ SoftRebooting = TargetPowerState {nodes .SoftRebooting }
45+ )
10346
104- if opts .HTTPClient != nil {
105- client .HTTPClient = * opts .HTTPClient
106- rt := opts .HTTPClient .Transport
107- if rt == nil {
108- rt = baseTransport
109- } else if opts .InsecureSkipVerify {
110- if t , ok := rt .(* http.Transport ); ok {
111- clone := t .Clone ()
112- clone .TLSClientConfig = & tls.Config {InsecureSkipVerify : true } //nolint:gosec
113- rt = clone
114- }
115- }
116- if authToken != "" {
117- rt = & tokenRoundTripper {rt : rt , token : authToken }
118- }
119- client .HTTPClient .Transport = rt
120- } else {
121- client .HTTPClient = http.Client {
122- Transport : transport ,
123- Timeout : 60 * time .Second ,
124- }
47+ // NewClient creates an Ironic client with no-auth (typical for in-cluster Ironic with no Keystone).
48+ func NewClient () (* Client , error ) {
49+ client , err := clientconfig .NewServiceClient (context .TODO (), "baremetal" , nil )
50+ if err != nil {
51+ return nil , fmt .Errorf ("failed to create baremetal client: %w" , err )
12552 }
12653
127- return & Client {serviceClient : client }, nil
54+ return & Client {
55+ serviceClient : client ,
56+ }, nil
12857}
12958
13059// GetNode fetches a node by UUID or name from Ironic.
@@ -136,27 +65,15 @@ func (c *Client) GetNode(ctx context.Context, nodeID string) (*nodes.Node, error
13665 return node , nil
13766}
13867
68+ func (c * Client ) GetEndpoint () string {
69+ return c .serviceClient .Endpoint
70+ }
71+
13972// SetPowerState requests power on or off for the node via Ironic.
140- // target accepts "on"/"off" or Ironic values "power on"/"power off".
141- func (c * Client ) SetPowerState (ctx context.Context , nodeID , target string ) error {
142- t := normalizePowerTarget (target )
143- if t == "" {
144- return fmt .Errorf ("invalid power target %q: use on, off, power on, or power off" , target )
145- }
146- res := nodes .ChangePowerState (ctx , c .serviceClient , nodeID , nodes.PowerStateOpts {Target : t })
73+ func (c * Client ) SetPowerState (ctx context.Context , nodeID string , target TargetPowerState ) error {
74+ res := nodes .ChangePowerState (ctx , c .serviceClient , nodeID , nodes.PowerStateOpts {Target : target .powerstate })
14775 if err := res .ExtractErr (); err != nil {
148- return fmt .Errorf ("set power state on node %s: %w" , nodeID , err )
76+ return fmt .Errorf ("failed to set power state on node %s: %w" , nodeID , err )
14977 }
15078 return nil
15179}
152-
153- func normalizePowerTarget (s string ) nodes.TargetPowerState {
154- switch strings .ToLower (strings .TrimSpace (s )) {
155- case "on" , "power on" :
156- return nodes .PowerOn
157- case "off" , "power off" :
158- return nodes .PowerOff
159- default :
160- return ""
161- }
162- }
0 commit comments