blob: d247bf934195438884d6655f91dc7f7dd809cf89 [file] [log] [blame]
Michal Kobusa1c3ca92019-08-21 14:29:23 +02001package nodes
2
3import (
Michal Kobusf6113582019-09-09 15:58:21 +02004 "fmt"
5
Michal Kobusa1c3ca92019-08-21 14:29:23 +02006 "gerrit.mcp.mirantis.net/debian/gophercloud.git"
7 "gerrit.mcp.mirantis.net/debian/gophercloud.git/pagination"
8)
9
Michal Kobusf6113582019-09-09 15:58:21 +020010// ListOptsBuilder allows extensions to add additional parameters to the
11// List request.
12type ListOptsBuilder interface {
13 ToNodeListQuery() (string, error)
14 ToNodeListDetailQuery() (string, error)
15}
16
17// Provision state reports the current provision state of the node, these are only used in filtering
18type ProvisionState string
19
20const (
21 Enroll ProvisionState = "enroll"
22 Verifying ProvisionState = "verifying"
23 Manageable ProvisionState = "manageable"
24 Available ProvisionState = "available"
25 Active ProvisionState = "active"
26 DeployWait ProvisionState = "wait call-back"
27 Deploying ProvisionState = "deploying"
28 DeployFail ProvisionState = "deploy failed"
29 DeployDone ProvisionState = "deploy complete"
30 Deleting ProvisionState = "deleting"
31 Deleted ProvisionState = "deleted"
32 Cleaning ProvisionState = "cleaning"
33 CleanWait ProvisionState = "clean wait"
34 CleanFail ProvisionState = "clean failed"
35 Error ProvisionState = "error"
36 Rebuild ProvisionState = "rebuild"
37 Inspecting ProvisionState = "inspecting"
38 InspectFail ProvisionState = "inspect failed"
39 InspectWait ProvisionState = "inspect wait"
40 Adopting ProvisionState = "adopting"
41 AdoptFail ProvisionState = "adopt failed"
42 Rescue ProvisionState = "rescue"
43 RescueFail ProvisionState = "rescue failed"
44 Rescuing ProvisionState = "rescuing"
45 UnrescueFail ProvisionState = "unrescue failed"
46)
47
48// TargetProvisionState is used when setting the provision state for a node.
49type TargetProvisionState string
50
51const (
52 TargetActive TargetProvisionState = "active"
53 TargetDeleted TargetProvisionState = "deleted"
54 TargetManage TargetProvisionState = "manage"
55 TargetProvide TargetProvisionState = "provide"
56 TargetInspect TargetProvisionState = "inspect"
57 TargetAbort TargetProvisionState = "abort"
58 TargetClean TargetProvisionState = "clean"
59 TargetAdopt TargetProvisionState = "adopt"
60 TargetRescue TargetProvisionState = "rescue"
61 TargetUnrescue TargetProvisionState = "unrescue"
62)
63
64// ListOpts allows the filtering and sorting of paginated collections through
65// the API. Filtering is achieved by passing in struct field values that map to
66// the node attributes you want to see returned. Marker and Limit are used
67// for pagination.
68type ListOpts struct {
69 // Filter the list by specific instance UUID
70 InstanceUUID string `q:"instance_uuid"`
71
72 // Filter the list by chassis UUID
73 ChassisUUID string `q:"chassis_uuid"`
74
75 // Filter the list by maintenance set to True or False
76 Maintenance bool `q:"maintenance"`
77
78 // Nodes which are, or are not, associated with an instance_uuid.
79 Associated bool `q:"associated"`
80
81 // Only return those with the specified provision_state.
82 ProvisionState ProvisionState `q:"provision_state"`
83
84 // Filter the list with the specified driver.
85 Driver string `q:"driver"`
86
87 // Filter the list with the specified resource class.
88 ResourceClass string `q:"resource_class"`
89
90 // Filter the list with the specified conductor_group.
91 ConductorGroup string `q:"conductor_group"`
92
93 // Filter the list with the specified fault.
94 Fault string `q:"fault"`
95
96 // One or more fields to be returned in the response.
97 Fields []string `q:"fields"`
98
99 // Requests a page size of items.
100 Limit int `q:"limit"`
101
102 // The ID of the last-seen item.
103 Marker string `q:"marker"`
104
105 // Sorts the response by the requested sort direction.
106 SortDir string `q:"sort_dir"`
107
108 // Sorts the response by the this attribute value.
109 SortKey string `q:"sort_key"`
110
111 // A string or UUID of the tenant who owns the baremetal node.
112 Owner string `q:"owner"`
113}
114
115// ToNodeListQuery formats a ListOpts into a query string.
116func (opts ListOpts) ToNodeListQuery() (string, error) {
117 q, err := gophercloud.BuildQueryString(opts)
118 return q.String(), err
119}
120
121// List makes a request against the API to list nodes accessible to you.
122func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
123 url := listURL(client)
124 if opts != nil {
125 query, err := opts.ToNodeListQuery()
126 if err != nil {
127 return pagination.Pager{Err: err}
128 }
129 url += query
130 }
131 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
132 return NodePage{pagination.LinkedPageBase{PageResult: r}}
Michal Kobusa1c3ca92019-08-21 14:29:23 +0200133 })
134}
Michal Kobusf6113582019-09-09 15:58:21 +0200135
136// ToNodeListDetailQuery formats a ListOpts into a query string for the list details API.
137func (opts ListOpts) ToNodeListDetailQuery() (string, error) {
138 // Detail endpoint can't filter by Fields
139 if len(opts.Fields) > 0 {
140 return "", fmt.Errorf("fields is not a valid option when getting a detailed listing of nodes")
141 }
142
143 q, err := gophercloud.BuildQueryString(opts)
144 return q.String(), err
145}
146
147// Return a list of bare metal Nodes with complete details. Some filtering is possible by passing in flags in ListOpts,
148// but you cannot limit by the fields returned.
149func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
150 // This URL is deprecated. In the future, we should compare the microversion and if >= 1.43, hit the listURL
151 // with ListOpts{Detail: true,}
152 url := listDetailURL(client)
153 if opts != nil {
154 query, err := opts.ToNodeListDetailQuery()
155 if err != nil {
156 return pagination.Pager{Err: err}
157 }
158 url += query
159 }
160 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
161 return NodePage{pagination.LinkedPageBase{PageResult: r}}
162 })
163}
164
165// Get requests details on a single node, by ID.
166func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
167 _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{
168 OkCodes: []int{200},
169 })
170 return
171}
172
173// CreateOptsBuilder allows extensions to add additional parameters to the
174// Create request.
175type CreateOptsBuilder interface {
176 ToNodeCreateMap() (map[string]interface{}, error)
177}
178
179// CreateOpts specifies node creation parameters.
180type CreateOpts struct {
181 // The boot interface for a Node, e.g. “pxe”.
182 BootInterface string `json:"boot_interface,omitempty"`
183
184 // The conductor group for a node. Case-insensitive string up to 255 characters, containing a-z, 0-9, _, -, and ..
185 ConductorGroup string `json:"conductor_group,omitempty"`
186
187 // The console interface for a node, e.g. “no-console”.
188 ConsoleInterface string `json:"console_interface,omitempty"`
189
190 // The deploy interface for a node, e.g. “iscsi”.
191 DeployInterface string `json:"deploy_interface,omitempty"`
192
193 // All the metadata required by the driver to manage this Node. List of fields varies between drivers, and can
194 // be retrieved from the /v1/drivers/<DRIVER_NAME>/properties resource.
195 DriverInfo map[string]interface{} `json:"driver_info,omitempty"`
196
197 // name of the driver used to manage this Node.
198 Driver string `json:"driver,omitempty"`
199
200 // A set of one or more arbitrary metadata key and value pairs.
201 Extra map[string]interface{} `json:"extra,omitempty"`
202
203 // The interface used for node inspection, e.g. “no-inspect”.
204 InspectInterface string `json:"inspect_interface,omitempty"`
205
206 // Interface for out-of-band node management, e.g. “ipmitool”.
207 ManagementInterface string `json:"management_interface,omitempty"`
208
209 // Human-readable identifier for the Node resource. May be undefined. Certain words are reserved.
210 Name string `json:"name,omitempty"`
211
212 // Which Network Interface provider to use when plumbing the network connections for this Node.
213 NetworkInterface string `json:"network_interface,omitempty"`
214
215 // Interface used for performing power actions on the node, e.g. “ipmitool”.
216 PowerInterface string `json:"power_interface,omitempty"`
217
218 // Physical characteristics of this Node. Populated during inspection, if performed. Can be edited via the REST
219 // API at any time.
220 Properties map[string]interface{} `json:"properties,omitempty"`
221
222 // Interface used for configuring RAID on this node, e.g. “no-raid”.
223 RAIDInterface string `json:"raid_interface,omitempty"`
224
225 // The interface used for node rescue, e.g. “no-rescue”.
226 RescueInterface string `json:"rescue_interface,omitempty"`
227
228 // A string which can be used by external schedulers to identify this Node as a unit of a specific type
229 // of resource.
230 ResourceClass string `json:"resource_class,omitempty"`
231
232 // Interface used for attaching and detaching volumes on this node, e.g. “cinder”.
233 StorageInterface string `json:"storage_interface,omitempty"`
234
235 // The UUID for the resource.
236 UUID string `json:"uuid,omitempty"`
237
238 // Interface for vendor-specific functionality on this node, e.g. “no-vendor”.
239 VendorInterface string `json:"vendor_interface,omitempty"`
240
241 // A string or UUID of the tenant who owns the baremetal node.
242 Owner string `json:"owner,omitempty"`
243}
244
245// ToNodeCreateMap assembles a request body based on the contents of a CreateOpts.
246func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) {
247 body, err := gophercloud.BuildRequestBody(opts, "")
248 if err != nil {
249 return nil, err
250 }
251
252 return body, nil
253}
254
255// Create requests a node to be created
256func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
257 reqBody, err := opts.ToNodeCreateMap()
258 if err != nil {
259 r.Err = err
260 return
261 }
262
263 _, r.Err = client.Post(createURL(client), reqBody, &r.Body, nil)
264 return
265}
266
267type Patch interface {
268 ToNodeUpdateMap() (map[string]interface{}, error)
269}
270
271// UpdateOpts is a slice of Patches used to update a node
272type UpdateOpts []Patch
273
274type UpdateOp string
275
276const (
277 ReplaceOp UpdateOp = "replace"
278 AddOp UpdateOp = "add"
279 RemoveOp UpdateOp = "remove"
280)
281
282type UpdateOperation struct {
283 Op UpdateOp `json:"op" required:"true"`
284 Path string `json:"path" required:"true"`
285 Value interface{} `json:"value,omitempty"`
286}
287
288func (opts UpdateOperation) ToNodeUpdateMap() (map[string]interface{}, error) {
289 return gophercloud.BuildRequestBody(opts, "")
290}
291
292// Update requests that a node be updated
293func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) {
294 body := make([]map[string]interface{}, len(opts))
295 for i, patch := range opts {
296 result, err := patch.ToNodeUpdateMap()
297 if err != nil {
298 r.Err = err
299 return
300 }
301
302 body[i] = result
303 }
304 _, r.Err = client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{
305 JSONBody: &body,
306 OkCodes: []int{200},
307 })
308 return
309}
310
311// Delete requests that a node be removed
312func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
313 _, r.Err = client.Delete(deleteURL(client, id), nil)
314 return
315}
316
317// Request that Ironic validate whether the Node’s driver has enough information to manage the Node. This polls each
318// interface on the driver, and returns the status of that interface.
319func Validate(client *gophercloud.ServiceClient, id string) (r ValidateResult) {
320 _, r.Err = client.Get(validateURL(client, id), &r.Body, &gophercloud.RequestOpts{
321 OkCodes: []int{200},
322 })
323 return
324}
325
326// Inject NMI (Non-Masking Interrupts) for the given Node. This feature can be used for hardware diagnostics, and
327// actual support depends on a driver.
328func InjectNMI(client *gophercloud.ServiceClient, id string) (r InjectNMIResult) {
329 _, r.Err = client.Put(injectNMIURL(client, id), map[string]string{}, nil, &gophercloud.RequestOpts{
330 OkCodes: []int{204},
331 })
332 return
333}
334
335type BootDeviceOpts struct {
336 BootDevice string `json:"boot_device"` // e.g., 'pxe', 'disk', etc.
337 Persistent bool `json:"persistent"` // Whether this is one-time or not
338}
339
340// BootDeviceOptsBuilder allows extensions to add additional parameters to the
341// SetBootDevice request.
342type BootDeviceOptsBuilder interface {
343 ToBootDeviceMap() (map[string]interface{}, error)
344}
345
346// ToBootDeviceSetMap assembles a request body based on the contents of a BootDeviceOpts.
347func (opts BootDeviceOpts) ToBootDeviceMap() (map[string]interface{}, error) {
348 body, err := gophercloud.BuildRequestBody(opts, "")
349 if err != nil {
350 return nil, err
351 }
352
353 return body, nil
354}
355
356// Set the boot device for the given Node, and set it persistently or for one-time boot. The exact behaviour
357// of this depends on the hardware driver.
358func SetBootDevice(client *gophercloud.ServiceClient, id string, bootDevice BootDeviceOptsBuilder) (r SetBootDeviceResult) {
359 reqBody, err := bootDevice.ToBootDeviceMap()
360 if err != nil {
361 r.Err = err
362 return
363 }
364
365 _, r.Err = client.Put(bootDeviceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
366 OkCodes: []int{204},
367 })
368 return
369}
370
371// Get the current boot device for the given Node.
372func GetBootDevice(client *gophercloud.ServiceClient, id string) (r BootDeviceResult) {
373 _, r.Err = client.Get(bootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{
374 OkCodes: []int{200},
375 })
376 return
377}
378
379// Retrieve the acceptable set of supported boot devices for a specific Node.
380func GetSupportedBootDevices(client *gophercloud.ServiceClient, id string) (r SupportedBootDeviceResult) {
381 _, r.Err = client.Get(supportedBootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{
382 OkCodes: []int{200},
383 })
384 return
385}
386
387// A cleaning step has required keys ‘interface’ and ‘step’, and optional key ‘args’. If specified,
388// the value for ‘args’ is a keyword variable argument dictionary that is passed to the cleaning step
389// method.
390type CleanStep struct {
391 Interface string `json:"interface" required:"true"`
392 Step string `json:"step" required:"true"`
393 Args map[string]interface{} `json:"args,omitempty"`
394}
395
396// ProvisionStateOptsBuilder allows extensions to add additional parameters to the
397// ChangeProvisionState request.
398type ProvisionStateOptsBuilder interface {
399 ToProvisionStateMap() (map[string]interface{}, error)
400}
401
402// Starting with Ironic API version 1.56, a configdrive may be a JSON object with structured data.
403// Prior to this version, it must be a base64-encoded, gzipped ISO9660 image.
404type ConfigDrive struct {
405 MetaData map[string]interface{} `json:"meta_data,omitempty"`
406 NetworkData map[string]interface{} `json:"network_data,omitempty"`
407 UserData interface{} `json:"user_data,omitempty"`
408}
409
410// ProvisionStateOpts for a request to change a node's provision state. A config drive should be base64-encoded
411// gzipped ISO9660 image.
412type ProvisionStateOpts struct {
413 Target TargetProvisionState `json:"target" required:"true"`
414 ConfigDrive interface{} `json:"configdrive,omitempty"`
415 CleanSteps []CleanStep `json:"clean_steps,omitempty"`
416 RescuePassword string `json:"rescue_password,omitempty"`
417}
418
419// ToProvisionStateMap assembles a request body based on the contents of a CreateOpts.
420func (opts ProvisionStateOpts) ToProvisionStateMap() (map[string]interface{}, error) {
421 body, err := gophercloud.BuildRequestBody(opts, "")
422 if err != nil {
423 return nil, err
424 }
425
426 return body, nil
427}
428
429// Request a change to the Node’s provision state. Acceptable target states depend on the Node’s current provision
430// state. More detailed documentation of the Ironic State Machine is available in the developer docs.
431func ChangeProvisionState(client *gophercloud.ServiceClient, id string, opts ProvisionStateOptsBuilder) (r ChangeStateResult) {
432 reqBody, err := opts.ToProvisionStateMap()
433 if err != nil {
434 r.Err = err
435 return
436 }
437
438 _, r.Err = client.Put(provisionStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
439 OkCodes: []int{202},
440 })
441 return
442}
443
444type TargetPowerState string
445
446// TargetPowerState is used when changing the power state of a node.
447const (
448 PowerOn TargetPowerState = "power on"
449 PowerOff TargetPowerState = "power off"
450 Rebooting TargetPowerState = "rebooting"
451 SoftPowerOff TargetPowerState = "soft power off"
452 SoftRebooting TargetPowerState = "soft rebooting"
453)
454
455// PowerStateOptsBuilder allows extensions to add additional parameters to the ChangePowerState request.
456type PowerStateOptsBuilder interface {
457 ToPowerStateMap() (map[string]interface{}, error)
458}
459
460// PowerStateOpts for a request to change a node's power state.
461type PowerStateOpts struct {
462 Target TargetPowerState `json:"target" required:"true"`
463 Timeout int `json:"timeout,omitempty"`
464}
465
466// ToPowerStateMap assembles a request body based on the contents of a PowerStateOpts.
467func (opts PowerStateOpts) ToPowerStateMap() (map[string]interface{}, error) {
468 body, err := gophercloud.BuildRequestBody(opts, "")
469 if err != nil {
470 return nil, err
471 }
472
473 return body, nil
474}
475
476// Request to change a Node's power state.
477func ChangePowerState(client *gophercloud.ServiceClient, id string, opts PowerStateOptsBuilder) (r ChangePowerStateResult) {
478 reqBody, err := opts.ToPowerStateMap()
479 if err != nil {
480 r.Err = err
481 return
482 }
483
484 _, r.Err = client.Put(powerStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
485 OkCodes: []int{202},
486 })
487 return
488}
489
490// This is the desired RAID configuration on the bare metal node.
491type RAIDConfigOpts struct {
492 LogicalDisks []LogicalDisk `json:"logical_disks"`
493}
494
495// RAIDConfigOptsBuilder allows extensions to modify a set RAID config request.
496type RAIDConfigOptsBuilder interface {
497 ToRAIDConfigMap() (map[string]interface{}, error)
498}
499
500// RAIDLevel type is used to specify the RAID level for a logical disk.
501type RAIDLevel string
502
503const (
504 RAID0 RAIDLevel = "0"
505 RAID1 RAIDLevel = "1"
506 RAID2 RAIDLevel = "2"
507 RAID5 RAIDLevel = "5"
508 RAID6 RAIDLevel = "6"
509 RAID10 RAIDLevel = "1+0"
510 RAID50 RAIDLevel = "5+0"
511 RAID60 RAIDLevel = "6+0"
512)
513
514// DiskType is used to specify the disk type for a logical disk, e.g. hdd or ssd.
515type DiskType string
516
517const (
518 HDD DiskType = "hdd"
519 SSD DiskType = "ssd"
520)
521
522// InterfaceType is used to specify the interface for a logical disk.
523type InterfaceType string
524
525const (
526 SATA InterfaceType = "sata"
527 SCSI InterfaceType = "scsi"
528 SAS InterfaceType = "sas"
529)
530
531type LogicalDisk struct {
532 // Size (Integer) of the logical disk to be created in GiB. If unspecified, "MAX" will be used.
533 SizeGB *int `json:"size_gb"`
534
535 // RAID level for the logical disk.
536 RAIDLevel RAIDLevel `json:"raid_level" required:"true"`
537
538 // Name of the volume. Should be unique within the Node. If not specified, volume name will be auto-generated.
539 VolumeName string `json:"volume_name,omitempty"`
540
541 // Set to true if this is the root volume. At most one logical disk can have this set to true.
542 IsRootVolume *bool `json:"is_root_volume,omitempty"`
543
544 // Set to true if this logical disk can share physical disks with other logical disks.
545 SharePhysicalDisks *bool `json:"share_physical_disks,omitempty"`
546
547 // If this is not specified, disk type will not be a criterion to find backing physical disks
548 DiskType DiskType `json:"disk_type,omitempty"`
549
550 // If this is not specified, interface type will not be a criterion to find backing physical disks.
551 InterfaceType InterfaceType `json:"interface_type,omitempty"`
552
553 // Integer, number of disks to use for the logical disk. Defaults to minimum number of disks required
554 // for the particular RAID level.
555 NumberOfPhysicalDisks int `json:"number_of_physical_disks,omitempty"`
556
557 // The name of the controller as read by the RAID interface.
558 Controller string `json:"controller,omitempty"`
559
560 // A list of physical disks to use as read by the RAID interface.
561 PhysicalDisks []string `json:"physical_disks,omitempty"`
562}
563
564func (opts RAIDConfigOpts) ToRAIDConfigMap() (map[string]interface{}, error) {
565 body, err := gophercloud.BuildRequestBody(opts, "")
566 if err != nil {
567 return nil, err
568 }
569
570 for _, v := range body["logical_disks"].([]interface{}) {
571 if logicalDisk, ok := v.(map[string]interface{}); ok {
572 if logicalDisk["size_gb"] == nil {
573 logicalDisk["size_gb"] = "MAX"
574 }
575 }
576 }
577
578 return body, nil
579}
580
581// Request to change a Node's RAID config.
582func SetRAIDConfig(client *gophercloud.ServiceClient, id string, raidConfigOptsBuilder RAIDConfigOptsBuilder) (r ChangeStateResult) {
583 reqBody, err := raidConfigOptsBuilder.ToRAIDConfigMap()
584 if err != nil {
585 r.Err = err
586 return
587 }
588
589 _, r.Err = client.Put(raidConfigURL(client, id), reqBody, nil, &gophercloud.RequestOpts{
590 OkCodes: []int{204},
591 })
592 return
593}