Michal Kobus | a1c3ca9 | 2019-08-21 14:29:23 +0200 | [diff] [blame] | 1 | package nodes |
| 2 | |
| 3 | import ( |
Michal Kobus | f611358 | 2019-09-09 15:58:21 +0200 | [diff] [blame^] | 4 | "fmt" |
| 5 | |
Michal Kobus | a1c3ca9 | 2019-08-21 14:29:23 +0200 | [diff] [blame] | 6 | "gerrit.mcp.mirantis.net/debian/gophercloud.git" |
| 7 | "gerrit.mcp.mirantis.net/debian/gophercloud.git/pagination" |
| 8 | ) |
| 9 | |
Michal Kobus | f611358 | 2019-09-09 15:58:21 +0200 | [diff] [blame^] | 10 | // ListOptsBuilder allows extensions to add additional parameters to the |
| 11 | // List request. |
| 12 | type 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 |
| 18 | type ProvisionState string |
| 19 | |
| 20 | const ( |
| 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. |
| 49 | type TargetProvisionState string |
| 50 | |
| 51 | const ( |
| 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. |
| 68 | type 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. |
| 116 | func (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. |
| 122 | func 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 Kobus | a1c3ca9 | 2019-08-21 14:29:23 +0200 | [diff] [blame] | 133 | }) |
| 134 | } |
Michal Kobus | f611358 | 2019-09-09 15:58:21 +0200 | [diff] [blame^] | 135 | |
| 136 | // ToNodeListDetailQuery formats a ListOpts into a query string for the list details API. |
| 137 | func (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. |
| 149 | func 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. |
| 166 | func 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. |
| 175 | type CreateOptsBuilder interface { |
| 176 | ToNodeCreateMap() (map[string]interface{}, error) |
| 177 | } |
| 178 | |
| 179 | // CreateOpts specifies node creation parameters. |
| 180 | type 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. |
| 246 | func (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 |
| 256 | func 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 | |
| 267 | type Patch interface { |
| 268 | ToNodeUpdateMap() (map[string]interface{}, error) |
| 269 | } |
| 270 | |
| 271 | // UpdateOpts is a slice of Patches used to update a node |
| 272 | type UpdateOpts []Patch |
| 273 | |
| 274 | type UpdateOp string |
| 275 | |
| 276 | const ( |
| 277 | ReplaceOp UpdateOp = "replace" |
| 278 | AddOp UpdateOp = "add" |
| 279 | RemoveOp UpdateOp = "remove" |
| 280 | ) |
| 281 | |
| 282 | type UpdateOperation struct { |
| 283 | Op UpdateOp `json:"op" required:"true"` |
| 284 | Path string `json:"path" required:"true"` |
| 285 | Value interface{} `json:"value,omitempty"` |
| 286 | } |
| 287 | |
| 288 | func (opts UpdateOperation) ToNodeUpdateMap() (map[string]interface{}, error) { |
| 289 | return gophercloud.BuildRequestBody(opts, "") |
| 290 | } |
| 291 | |
| 292 | // Update requests that a node be updated |
| 293 | func 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 |
| 312 | func 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. |
| 319 | func 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. |
| 328 | func 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 | |
| 335 | type 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. |
| 342 | type BootDeviceOptsBuilder interface { |
| 343 | ToBootDeviceMap() (map[string]interface{}, error) |
| 344 | } |
| 345 | |
| 346 | // ToBootDeviceSetMap assembles a request body based on the contents of a BootDeviceOpts. |
| 347 | func (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. |
| 358 | func 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. |
| 372 | func 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. |
| 380 | func 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. |
| 390 | type 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. |
| 398 | type 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. |
| 404 | type 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. |
| 412 | type 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. |
| 420 | func (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. |
| 431 | func 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 | |
| 444 | type TargetPowerState string |
| 445 | |
| 446 | // TargetPowerState is used when changing the power state of a node. |
| 447 | const ( |
| 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. |
| 456 | type PowerStateOptsBuilder interface { |
| 457 | ToPowerStateMap() (map[string]interface{}, error) |
| 458 | } |
| 459 | |
| 460 | // PowerStateOpts for a request to change a node's power state. |
| 461 | type 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. |
| 467 | func (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. |
| 477 | func 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. |
| 491 | type RAIDConfigOpts struct { |
| 492 | LogicalDisks []LogicalDisk `json:"logical_disks"` |
| 493 | } |
| 494 | |
| 495 | // RAIDConfigOptsBuilder allows extensions to modify a set RAID config request. |
| 496 | type RAIDConfigOptsBuilder interface { |
| 497 | ToRAIDConfigMap() (map[string]interface{}, error) |
| 498 | } |
| 499 | |
| 500 | // RAIDLevel type is used to specify the RAID level for a logical disk. |
| 501 | type RAIDLevel string |
| 502 | |
| 503 | const ( |
| 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. |
| 515 | type DiskType string |
| 516 | |
| 517 | const ( |
| 518 | HDD DiskType = "hdd" |
| 519 | SSD DiskType = "ssd" |
| 520 | ) |
| 521 | |
| 522 | // InterfaceType is used to specify the interface for a logical disk. |
| 523 | type InterfaceType string |
| 524 | |
| 525 | const ( |
| 526 | SATA InterfaceType = "sata" |
| 527 | SCSI InterfaceType = "scsi" |
| 528 | SAS InterfaceType = "sas" |
| 529 | ) |
| 530 | |
| 531 | type 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 | |
| 564 | func (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. |
| 582 | func 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 | } |