github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/openstack/compute/v2/servers/requests.go (about) 1 package servers 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "maps" 9 "net" 10 "regexp" 11 "strings" 12 13 "github.com/vnpaycloud-console/gophercloud/v2" 14 "github.com/vnpaycloud-console/gophercloud/v2/pagination" 15 ) 16 17 // ListOptsBuilder allows extensions to add additional parameters to the 18 // List request. 19 type ListOptsBuilder interface { 20 ToServerListQuery() (string, error) 21 } 22 23 // ListOpts allows the filtering and sorting of paginated collections through 24 // the API. Filtering is achieved by passing in struct field values that map to 25 // the server attributes you want to see returned. Marker and Limit are used 26 // for pagination. 27 type ListOpts struct { 28 // ChangesSince is a time/date stamp for when the server last changed status. 29 ChangesSince string `q:"changes-since"` 30 31 // Image is the name of the image in URL format. 32 Image string `q:"image"` 33 34 // Flavor is the name of the flavor in URL format. 35 Flavor string `q:"flavor"` 36 37 // IP is a regular expression to match the IPv4 address of the server. 38 IP string `q:"ip"` 39 40 // This requires the client to be set to microversion 2.5 or later, unless 41 // the user is an admin. 42 // IP is a regular expression to match the IPv6 address of the server. 43 IP6 string `q:"ip6"` 44 45 // Name of the server as a string; can be queried with regular expressions. 46 // Realize that ?name=bob returns both bob and bobb. If you need to match bob 47 // only, you can use a regular expression matching the syntax of the 48 // underlying database server implemented for Compute. 49 Name string `q:"name"` 50 51 // Status is the value of the status of the server so that you can filter on 52 // "ACTIVE" for example. 53 Status string `q:"status"` 54 55 // Host is the name of the host as a string. 56 Host string `q:"host"` 57 58 // Marker is a UUID of the server at which you want to set a marker. 59 Marker string `q:"marker"` 60 61 // Limit is an integer value for the limit of values to return. 62 Limit int `q:"limit"` 63 64 // AllTenants is a bool to show all tenants. 65 AllTenants bool `q:"all_tenants"` 66 67 // TenantID lists servers for a particular tenant. 68 // Setting "AllTenants = true" is required. 69 TenantID string `q:"tenant_id"` 70 71 // This requires the client to be set to microversion 2.83 or later, unless 72 // the user is an admin. 73 // UserID lists servers for a particular user. 74 UserID string `q:"user_id"` 75 76 // This requires the client to be set to microversion 2.26 or later. 77 // Tags filters on specific server tags. All tags must be present for the server. 78 Tags string `q:"tags"` 79 80 // This requires the client to be set to microversion 2.26 or later. 81 // TagsAny filters on specific server tags. At least one of the tags must be present for the server. 82 TagsAny string `q:"tags-any"` 83 84 // This requires the client to be set to microversion 2.26 or later. 85 // NotTags filters on specific server tags. All tags must be absent for the server. 86 NotTags string `q:"not-tags"` 87 88 // This requires the client to be set to microversion 2.26 or later. 89 // NotTagsAny filters on specific server tags. At least one of the tags must be absent for the server. 90 NotTagsAny string `q:"not-tags-any"` 91 92 // Display servers based on their availability zone (Admin only until microversion 2.82). 93 AvailabilityZone string `q:"availability_zone"` 94 } 95 96 // ToServerListQuery formats a ListOpts into a query string. 97 func (opts ListOpts) ToServerListQuery() (string, error) { 98 q, err := gophercloud.BuildQueryString(opts) 99 return q.String(), err 100 } 101 102 // ListSimple makes a request against the API to list servers accessible to you. 103 func ListSimple(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { 104 url := listURL(client) 105 if opts != nil { 106 query, err := opts.ToServerListQuery() 107 if err != nil { 108 return pagination.Pager{Err: err} 109 } 110 url += query 111 } 112 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { 113 return ServerPage{pagination.LinkedPageBase{PageResult: r}} 114 }) 115 } 116 117 // List makes a request against the API to list servers details accessible to you. 118 func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { 119 url := listDetailURL(client) 120 if opts != nil { 121 query, err := opts.ToServerListQuery() 122 if err != nil { 123 return pagination.Pager{Err: err} 124 } 125 url += query 126 } 127 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { 128 return ServerPage{pagination.LinkedPageBase{PageResult: r}} 129 }) 130 } 131 132 // SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. 133 type SchedulerHintOptsBuilder interface { 134 ToSchedulerHintsMap() (map[string]any, error) 135 } 136 137 // SchedulerHintOpts represents a set of scheduling hints that are passed to the 138 // OpenStack scheduler. 139 type SchedulerHintOpts struct { 140 // Group specifies a Server Group to place the instance in. 141 Group string 142 143 // DifferentHost will place the instance on a compute node that does not 144 // host the given instances. 145 DifferentHost []string 146 147 // SameHost will place the instance on a compute node that hosts the given 148 // instances. 149 SameHost []string 150 151 // Query is a conditional statement that results in compute nodes able to 152 // host the instance. 153 Query []any 154 155 // TargetCell specifies a cell name where the instance will be placed. 156 TargetCell string `json:"target_cell,omitempty"` 157 158 // DifferentCell specifies cells names where an instance should not be placed. 159 DifferentCell []string `json:"different_cell,omitempty"` 160 161 // BuildNearHostIP specifies a subnet of compute nodes to host the instance. 162 BuildNearHostIP string 163 164 // AdditionalProperies are arbitrary key/values that are not validated by nova. 165 AdditionalProperties map[string]any 166 } 167 168 // ToSchedulerHintsMap assembles a request body for scheduler hints. 169 func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]any, error) { 170 sh := make(map[string]any) 171 172 uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") 173 174 if opts.Group != "" { 175 if !uuidRegex.MatchString(opts.Group) { 176 err := gophercloud.ErrInvalidInput{} 177 err.Argument = "servers.schedulerhints.SchedulerHintOpts.Group" 178 err.Value = opts.Group 179 err.Info = "Group must be a UUID" 180 return nil, err 181 } 182 sh["group"] = opts.Group 183 } 184 185 if len(opts.DifferentHost) > 0 { 186 for _, diffHost := range opts.DifferentHost { 187 if !uuidRegex.MatchString(diffHost) { 188 err := gophercloud.ErrInvalidInput{} 189 err.Argument = "servers.schedulerhints.SchedulerHintOpts.DifferentHost" 190 err.Value = opts.DifferentHost 191 err.Info = "The hosts must be in UUID format." 192 return nil, err 193 } 194 } 195 sh["different_host"] = opts.DifferentHost 196 } 197 198 if len(opts.SameHost) > 0 { 199 for _, sameHost := range opts.SameHost { 200 if !uuidRegex.MatchString(sameHost) { 201 err := gophercloud.ErrInvalidInput{} 202 err.Argument = "servers.schedulerhints.SchedulerHintOpts.SameHost" 203 err.Value = opts.SameHost 204 err.Info = "The hosts must be in UUID format." 205 return nil, err 206 } 207 } 208 sh["same_host"] = opts.SameHost 209 } 210 211 /* 212 Query can be something simple like: 213 [">=", "$free_ram_mb", 1024] 214 215 Or more complex like: 216 ['and', 217 ['>=', '$free_ram_mb', 1024], 218 ['>=', '$free_disk_mb', 200 * 1024] 219 ] 220 221 Because of the possible complexity, just make sure the length is a minimum of 3. 222 */ 223 if len(opts.Query) > 0 { 224 if len(opts.Query) < 3 { 225 err := gophercloud.ErrInvalidInput{} 226 err.Argument = "servers.schedulerhints.SchedulerHintOpts.Query" 227 err.Value = opts.Query 228 err.Info = "Must be a conditional statement in the format of [op,variable,value]" 229 return nil, err 230 } 231 232 // The query needs to be sent as a marshalled string. 233 b, err := json.Marshal(opts.Query) 234 if err != nil { 235 err := gophercloud.ErrInvalidInput{} 236 err.Argument = "servers.schedulerhints.SchedulerHintOpts.Query" 237 err.Value = opts.Query 238 err.Info = "Must be a conditional statement in the format of [op,variable,value]" 239 return nil, err 240 } 241 242 sh["query"] = string(b) 243 } 244 245 if opts.TargetCell != "" { 246 sh["target_cell"] = opts.TargetCell 247 } 248 249 if len(opts.DifferentCell) > 0 { 250 sh["different_cell"] = opts.DifferentCell 251 } 252 253 if opts.BuildNearHostIP != "" { 254 if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil { 255 err := gophercloud.ErrInvalidInput{} 256 err.Argument = "servers.schedulerhints.SchedulerHintOpts.BuildNearHostIP" 257 err.Value = opts.BuildNearHostIP 258 err.Info = "Must be a valid subnet in the form 192.168.1.1/24" 259 return nil, err 260 } 261 ipParts := strings.Split(opts.BuildNearHostIP, "/") 262 sh["build_near_host_ip"] = ipParts[0] 263 sh["cidr"] = "/" + ipParts[1] 264 } 265 266 if opts.AdditionalProperties != nil { 267 for k, v := range opts.AdditionalProperties { 268 sh[k] = v 269 } 270 } 271 272 if len(sh) == 0 { 273 return sh, nil 274 } 275 276 return map[string]any{"os:scheduler_hints": sh}, nil 277 } 278 279 // Network is used within CreateOpts to control a new server's network 280 // attachments. 281 type Network struct { 282 // UUID of a network to attach to the newly provisioned server. 283 // Required unless Port is provided. 284 UUID string 285 286 // Port of a neutron network to attach to the newly provisioned server. 287 // Required unless UUID is provided. 288 Port string 289 290 // FixedIP specifies a fixed IPv4 address to be used on this network. 291 FixedIP string 292 293 // Tag may contain an optional device role tag for the server's virtual 294 // network interface. This can be used to identify network interfaces when 295 // multiple networks are connected to one server. 296 // 297 // Requires microversion 2.32 through 2.36 or 2.42 or later. 298 Tag string 299 } 300 301 type ( 302 // DestinationType represents the type of medium being used as the 303 // destination of the bootable device. 304 DestinationType string 305 306 // SourceType represents the type of medium being used as the source of the 307 // bootable device. 308 SourceType string 309 ) 310 311 const ( 312 // DestinationLocal DestinationType is for using an ephemeral disk as the 313 // destination. 314 DestinationLocal DestinationType = "local" 315 316 // DestinationVolume DestinationType is for using a volume as the destination. 317 DestinationVolume DestinationType = "volume" 318 319 // SourceBlank SourceType is for a "blank" or empty source. 320 SourceBlank SourceType = "blank" 321 322 // SourceImage SourceType is for using images as the source of a block device. 323 SourceImage SourceType = "image" 324 325 // SourceSnapshot SourceType is for using a volume snapshot as the source of 326 // a block device. 327 SourceSnapshot SourceType = "snapshot" 328 329 // SourceVolume SourceType is for using a volume as the source of block 330 // device. 331 SourceVolume SourceType = "volume" 332 ) 333 334 // BlockDevice is a structure with options for creating block devices in a 335 // server. The block device may be created from an image, snapshot, new volume, 336 // or existing volume. The destination may be a new volume, existing volume 337 // which will be attached to the instance, ephemeral disk, or boot device. 338 type BlockDevice struct { 339 // SourceType must be one of: "volume", "snapshot", "image", or "blank". 340 SourceType SourceType `json:"source_type" required:"true"` 341 342 // UUID is the unique identifier for the existing volume, snapshot, or 343 // image (see above). 344 UUID string `json:"uuid,omitempty"` 345 346 // BootIndex is the boot index. It defaults to 0. 347 BootIndex int `json:"boot_index"` 348 349 // DeleteOnTermination specifies whether or not to delete the attached volume 350 // when the server is deleted. Defaults to `false`. 351 DeleteOnTermination bool `json:"delete_on_termination"` 352 353 // DestinationType is the type that gets created. Possible values are "volume" 354 // and "local". 355 DestinationType DestinationType `json:"destination_type,omitempty"` 356 357 // GuestFormat specifies the format of the block device. 358 // Not specifying this will cause the device to be formatted to the default in Nova 359 // which is currently vfat. 360 // https://opendev.org/openstack/nova/src/commit/d0b459423dd81644e8d9382b6c87fabaa4f03ad4/nova/privsep/fs.py#L257 361 GuestFormat string `json:"guest_format,omitempty"` 362 363 // VolumeSize is the size of the volume to create (in gigabytes). This can be 364 // omitted for existing volumes. 365 VolumeSize int `json:"volume_size,omitempty"` 366 367 // DeviceType specifies the device type of the block devices. 368 // Examples of this are disk, cdrom, floppy, lun, etc. 369 DeviceType string `json:"device_type,omitempty"` 370 371 // DiskBus is the bus type of the block devices. 372 // Examples of this are ide, usb, virtio, scsi, etc. 373 DiskBus string `json:"disk_bus,omitempty"` 374 375 // VolumeType is the volume type of the block device. 376 // This requires Compute API microversion 2.67 or later. 377 VolumeType string `json:"volume_type,omitempty"` 378 379 // Tag is an arbitrary string that can be applied to a block device. 380 // Information about the device tags can be obtained from the metadata API 381 // and the config drive, allowing devices to be easily identified. 382 // This requires Compute API microversion 2.42 or later. 383 Tag string `json:"tag,omitempty"` 384 } 385 386 // Personality is an array of files that are injected into the server at launch. 387 type Personality []*File 388 389 // File is used within CreateOpts and RebuildOpts to inject a file into the 390 // server at launch. 391 // File implements the json.Marshaler interface, so when a Create or Rebuild 392 // operation is requested, json.Marshal will call File's MarshalJSON method. 393 type File struct { 394 // Path of the file. 395 Path string 396 397 // Contents of the file. Maximum content size is 255 bytes. 398 Contents []byte 399 } 400 401 // MarshalJSON marshals the escaped file, base64 encoding the contents. 402 func (f *File) MarshalJSON() ([]byte, error) { 403 file := struct { 404 Path string `json:"path"` 405 Contents string `json:"contents"` 406 }{ 407 Path: f.Path, 408 Contents: base64.StdEncoding.EncodeToString(f.Contents), 409 } 410 return json.Marshal(file) 411 } 412 413 // DiskConfig represents one of the two possible settings for the DiskConfig 414 // option when creating, rebuilding, or resizing servers: Auto or Manual. 415 type DiskConfig string 416 417 const ( 418 // Auto builds a server with a single partition the size of the target flavor 419 // disk and automatically adjusts the filesystem to fit the entire partition. 420 // Auto may only be used with images and servers that use a single EXT3 421 // partition. 422 Auto DiskConfig = "AUTO" 423 424 // Manual builds a server using whatever partition scheme and filesystem are 425 // present in the source image. If the target flavor disk is larger, the 426 // remaining space is left unpartitioned. This enables images to have non-EXT3 427 // filesystems, multiple partitions, and so on, and enables you to manage the 428 // disk configuration. It also results in slightly shorter boot times. 429 Manual DiskConfig = "MANUAL" 430 ) 431 432 // CreateOptsBuilder allows extensions to add additional parameters to the 433 // Create request. 434 type CreateOptsBuilder interface { 435 ToServerCreateMap() (map[string]any, error) 436 } 437 438 // CreateOpts specifies server creation parameters. 439 type CreateOpts struct { 440 // Name is the name to assign to the newly launched server. 441 Name string `json:"name" required:"true"` 442 443 // ImageRef is the ID or full URL to the image that contains the 444 // server's OS and initial state. 445 // Also optional if using the boot-from-volume extension. 446 ImageRef string `json:"imageRef"` 447 448 // FlavorRef is the ID or full URL to the flavor that describes the server's specs. 449 FlavorRef string `json:"flavorRef"` 450 451 // SecurityGroups lists the names of the security groups to which this server 452 // should belong. 453 SecurityGroups []string `json:"-"` 454 455 // UserData contains configuration information or scripts to use upon launch. 456 // Create will base64-encode it for you, if it isn't already. 457 UserData []byte `json:"-"` 458 459 // AvailabilityZone in which to launch the server. 460 AvailabilityZone string `json:"availability_zone,omitempty"` 461 462 // Networks dictates how this server will be attached to available networks. 463 // By default, the server will be attached to all isolated networks for the 464 // tenant. 465 // Starting with microversion 2.37 networks can also be an "auto" or "none" 466 // string. 467 Networks any `json:"-"` 468 469 // Metadata contains key-value pairs (up to 255 bytes each) to attach to the 470 // server. 471 Metadata map[string]string `json:"metadata,omitempty"` 472 473 // Personality includes files to inject into the server at launch. 474 // Create will base64-encode file contents for you. 475 Personality Personality `json:"personality,omitempty"` 476 477 // ConfigDrive enables metadata injection through a configuration drive. 478 ConfigDrive *bool `json:"config_drive,omitempty"` 479 480 // AdminPass sets the root user password. If not set, a randomly-generated 481 // password will be created and returned in the response. 482 AdminPass string `json:"adminPass,omitempty"` 483 484 // AccessIPv4 specifies an IPv4 address for the instance. 485 AccessIPv4 string `json:"accessIPv4,omitempty"` 486 487 // AccessIPv6 specifies an IPv6 address for the instance. 488 AccessIPv6 string `json:"accessIPv6,omitempty"` 489 490 // Min specifies Minimum number of servers to launch. 491 Min int `json:"min_count,omitempty"` 492 493 // Max specifies Maximum number of servers to launch. 494 Max int `json:"max_count,omitempty"` 495 496 // Tags allows a server to be tagged with single-word metadata. 497 // Requires microversion 2.52 or later. 498 Tags []string `json:"tags,omitempty"` 499 500 // (Available from 2.90) Hostname specifies the hostname to configure for the 501 // instance in the metadata service. Starting with microversion 2.94, this can 502 // be a Fully Qualified Domain Name (FQDN) of up to 255 characters in length. 503 // If not set, OpenStack will derive the server's hostname from the Name field. 504 Hostname string `json:"hostname,omitempty"` 505 506 // BlockDevice describes the mapping of various block devices. 507 BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"` 508 509 // DiskConfig [optional] controls how the created server's disk is partitioned. 510 DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` 511 512 // HypervisorHostname is the name of the hypervisor to which the server is scheduled. 513 HypervisorHostname string `json:"hypervisor_hostname,omitempty"` 514 } 515 516 // ToServerCreateMap assembles a request body based on the contents of a 517 // CreateOpts. 518 func (opts CreateOpts) ToServerCreateMap() (map[string]any, error) { 519 // We intentionally don't envelope the body here since we want to strip 520 // some fields out and modify others 521 b, err := gophercloud.BuildRequestBody(opts, "") 522 if err != nil { 523 return nil, err 524 } 525 526 if opts.UserData != nil { 527 var userData string 528 if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil { 529 userData = base64.StdEncoding.EncodeToString(opts.UserData) 530 } else { 531 userData = string(opts.UserData) 532 } 533 b["user_data"] = &userData 534 } 535 536 if len(opts.SecurityGroups) > 0 { 537 securityGroups := make([]map[string]any, len(opts.SecurityGroups)) 538 for i, groupName := range opts.SecurityGroups { 539 securityGroups[i] = map[string]any{"name": groupName} 540 } 541 b["security_groups"] = securityGroups 542 } 543 544 switch v := opts.Networks.(type) { 545 case []Network: 546 if len(v) > 0 { 547 networks := make([]map[string]any, len(v)) 548 for i, net := range v { 549 networks[i] = make(map[string]any) 550 if net.UUID != "" { 551 networks[i]["uuid"] = net.UUID 552 } 553 if net.Port != "" { 554 networks[i]["port"] = net.Port 555 } 556 if net.FixedIP != "" { 557 networks[i]["fixed_ip"] = net.FixedIP 558 } 559 if net.Tag != "" { 560 networks[i]["tag"] = net.Tag 561 } 562 } 563 b["networks"] = networks 564 } 565 case string: 566 if v == "auto" || v == "none" { 567 b["networks"] = v 568 } else { 569 return nil, fmt.Errorf(`networks must be a slice of Network struct or a string with "auto" or "none" values, current value is %q`, v) 570 } 571 } 572 573 if opts.Min != 0 { 574 b["min_count"] = opts.Min 575 } 576 577 if opts.Max != 0 { 578 b["max_count"] = opts.Max 579 } 580 581 // Now we do our enveloping 582 b = map[string]any{"server": b} 583 584 return b, nil 585 } 586 587 // Create requests a server to be provisioned to the user in the current tenant. 588 func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder, hintOpts SchedulerHintOptsBuilder) (r CreateResult) { 589 b, err := opts.ToServerCreateMap() 590 if err != nil { 591 r.Err = err 592 return 593 } 594 595 if hintOpts != nil { 596 sh, err := hintOpts.ToSchedulerHintsMap() 597 if err != nil { 598 r.Err = err 599 return 600 } 601 maps.Copy(b, sh) 602 } 603 604 resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ 605 OkCodes: []int{200, 202}, 606 }) 607 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 608 return 609 } 610 611 // Delete requests that a server previously provisioned be removed from your 612 // account. 613 func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { 614 resp, err := client.Delete(ctx, deleteURL(client, id), nil) 615 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 616 return 617 } 618 619 // ForceDelete forces the deletion of a server. 620 func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { 621 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"forceDelete": ""}, nil, nil) 622 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 623 return 624 } 625 626 // Get requests details on a single server, by ID. 627 func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { 628 resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ 629 OkCodes: []int{200, 203}, 630 }) 631 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 632 return 633 } 634 635 // UpdateOptsBuilder allows extensions to add additional attributes to the 636 // Update request. 637 type UpdateOptsBuilder interface { 638 ToServerUpdateMap() (map[string]any, error) 639 } 640 641 // UpdateOpts specifies the base attributes that may be updated on an existing 642 // server. 643 type UpdateOpts struct { 644 // Name changes the displayed name of the server. 645 // The server host name will *not* change. 646 // Server names are not constrained to be unique, even within the same tenant. 647 Name string `json:"name,omitempty"` 648 649 // AccessIPv4 provides a new IPv4 address for the instance. 650 AccessIPv4 string `json:"accessIPv4,omitempty"` 651 652 // AccessIPv6 provides a new IPv6 address for the instance. 653 AccessIPv6 string `json:"accessIPv6,omitempty"` 654 } 655 656 // ToServerUpdateMap formats an UpdateOpts structure into a request body. 657 func (opts UpdateOpts) ToServerUpdateMap() (map[string]any, error) { 658 return gophercloud.BuildRequestBody(opts, "server") 659 } 660 661 // Update requests that various attributes of the indicated server be changed. 662 func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { 663 b, err := opts.ToServerUpdateMap() 664 if err != nil { 665 r.Err = err 666 return 667 } 668 resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 669 OkCodes: []int{200}, 670 }) 671 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 672 return 673 } 674 675 // ChangeAdminPassword alters the administrator or root password for a specified 676 // server. 677 func ChangeAdminPassword(ctx context.Context, client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { 678 b := map[string]any{ 679 "changePassword": map[string]string{ 680 "adminPass": newPassword, 681 }, 682 } 683 resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) 684 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 685 return 686 } 687 688 // RebootMethod describes the mechanisms by which a server reboot can be requested. 689 type RebootMethod string 690 691 // These constants determine how a server should be rebooted. 692 // See the Reboot() function for further details. 693 const ( 694 SoftReboot RebootMethod = "SOFT" 695 HardReboot RebootMethod = "HARD" 696 OSReboot = SoftReboot 697 PowerCycle = HardReboot 698 ) 699 700 // RebootOptsBuilder allows extensions to add additional parameters to the 701 // reboot request. 702 type RebootOptsBuilder interface { 703 ToServerRebootMap() (map[string]any, error) 704 } 705 706 // RebootOpts provides options to the reboot request. 707 type RebootOpts struct { 708 // Type is the type of reboot to perform on the server. 709 Type RebootMethod `json:"type" required:"true"` 710 } 711 712 // ToServerRebootMap builds a body for the reboot request. 713 func (opts RebootOpts) ToServerRebootMap() (map[string]any, error) { 714 return gophercloud.BuildRequestBody(opts, "reboot") 715 } 716 717 /* 718 Reboot requests that a given server reboot. 719 720 Two methods exist for rebooting a server: 721 722 HardReboot (aka PowerCycle) starts the server instance by physically cutting 723 power to the machine, or if a VM, terminating it at the hypervisor level. 724 It's done. Caput. Full stop. 725 Then, after a brief while, power is restored or the VM instance restarted. 726 727 SoftReboot (aka OSReboot) simply tells the OS to restart under its own 728 procedure. 729 E.g., in Linux, asking it to enter runlevel 6, or executing 730 "sudo shutdown -r now", or by asking Windows to rtart the machine. 731 */ 732 func Reboot(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) { 733 b, err := opts.ToServerRebootMap() 734 if err != nil { 735 r.Err = err 736 return 737 } 738 resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) 739 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 740 return 741 } 742 743 // RebuildOptsBuilder allows extensions to provide additional parameters to the 744 // rebuild request. 745 type RebuildOptsBuilder interface { 746 ToServerRebuildMap() (map[string]any, error) 747 } 748 749 // RebuildOpts represents the configuration options used in a server rebuild 750 // operation. 751 type RebuildOpts struct { 752 // AdminPass is the server's admin password 753 AdminPass string `json:"adminPass,omitempty"` 754 755 // ImageRef is the ID of the image you want your server to be provisioned on. 756 ImageRef string `json:"imageRef"` 757 758 // Name to set the server to 759 Name string `json:"name,omitempty"` 760 761 // AccessIPv4 [optional] provides a new IPv4 address for the instance. 762 AccessIPv4 string `json:"accessIPv4,omitempty"` 763 764 // AccessIPv6 [optional] provides a new IPv6 address for the instance. 765 AccessIPv6 string `json:"accessIPv6,omitempty"` 766 767 // Metadata [optional] contains key-value pairs (up to 255 bytes each) 768 // to attach to the server. 769 Metadata map[string]string `json:"metadata,omitempty"` 770 771 // Personality [optional] includes files to inject into the server at launch. 772 // Rebuild will base64-encode file contents for you. 773 Personality Personality `json:"personality,omitempty"` 774 775 // DiskConfig controls how the rebuilt server's disk is partitioned. 776 DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` 777 } 778 779 // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON 780 func (opts RebuildOpts) ToServerRebuildMap() (map[string]any, error) { 781 if opts.DiskConfig != "" && opts.DiskConfig != Auto && opts.DiskConfig != Manual { 782 err := gophercloud.ErrInvalidInput{} 783 err.Argument = "servers.RebuildOpts.DiskConfig" 784 err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" 785 return nil, err 786 } 787 788 b, err := gophercloud.BuildRequestBody(opts, "") 789 if err != nil { 790 return nil, err 791 } 792 793 return map[string]any{"rebuild": b}, nil 794 } 795 796 // Rebuild will reprovision the server according to the configuration options 797 // provided in the RebuildOpts struct. 798 func Rebuild(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) { 799 b, err := opts.ToServerRebuildMap() 800 if err != nil { 801 r.Err = err 802 return 803 } 804 resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, nil) 805 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 806 return 807 } 808 809 // ResizeOptsBuilder allows extensions to add additional parameters to the 810 // resize request. 811 type ResizeOptsBuilder interface { 812 ToServerResizeMap() (map[string]any, error) 813 } 814 815 // ResizeOpts represents the configuration options used to control a Resize 816 // operation. 817 type ResizeOpts struct { 818 // FlavorRef is the ID of the flavor you wish your server to become. 819 FlavorRef string `json:"flavorRef" required:"true"` 820 821 // DiskConfig [optional] controls how the resized server's disk is partitioned. 822 DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` 823 } 824 825 // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON 826 // request body for the Resize request. 827 func (opts ResizeOpts) ToServerResizeMap() (map[string]any, error) { 828 if opts.DiskConfig != "" && opts.DiskConfig != Auto && opts.DiskConfig != Manual { 829 err := gophercloud.ErrInvalidInput{} 830 err.Argument = "servers.ResizeOpts.DiskConfig" 831 err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" 832 return nil, err 833 } 834 835 return gophercloud.BuildRequestBody(opts, "resize") 836 } 837 838 // Resize instructs the provider to change the flavor of the server. 839 // 840 // Note that this implies rebuilding it. 841 // 842 // Unfortunately, one cannot pass rebuild parameters to the resize function. 843 // When the resize completes, the server will be in VERIFY_RESIZE state. 844 // While in this state, you can explore the use of the new server's 845 // configuration. If you like it, call ConfirmResize() to commit the resize 846 // permanently. Otherwise, call RevertResize() to restore the old configuration. 847 func Resize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { 848 b, err := opts.ToServerResizeMap() 849 if err != nil { 850 r.Err = err 851 return 852 } 853 resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) 854 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 855 return 856 } 857 858 // ConfirmResize confirms a previous resize operation on a server. 859 // See Resize() for more details. 860 func ConfirmResize(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { 861 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ 862 OkCodes: []int{201, 202, 204}, 863 }) 864 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 865 return 866 } 867 868 // RevertResize cancels a previous resize operation on a server. 869 // See Resize() for more details. 870 func RevertResize(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { 871 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"revertResize": nil}, nil, nil) 872 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 873 return 874 } 875 876 // ResetMetadataOptsBuilder allows extensions to add additional parameters to 877 // the Reset request. 878 type ResetMetadataOptsBuilder interface { 879 ToMetadataResetMap() (map[string]any, error) 880 } 881 882 // MetadataOpts is a map that contains key-value pairs. 883 type MetadataOpts map[string]string 884 885 // ToMetadataResetMap assembles a body for a Reset request based on the contents 886 // of a MetadataOpts. 887 func (opts MetadataOpts) ToMetadataResetMap() (map[string]any, error) { 888 return map[string]any{"metadata": opts}, nil 889 } 890 891 // ToMetadataUpdateMap assembles a body for an Update request based on the 892 // contents of a MetadataOpts. 893 func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]any, error) { 894 return map[string]any{"metadata": opts}, nil 895 } 896 897 // ResetMetadata will create multiple new key-value pairs for the given server 898 // ID. 899 // Note: Using this operation will erase any already-existing metadata and 900 // create the new metadata provided. To keep any already-existing metadata, 901 // use the UpdateMetadatas or UpdateMetadata function. 902 func ResetMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { 903 b, err := opts.ToMetadataResetMap() 904 if err != nil { 905 r.Err = err 906 return 907 } 908 resp, err := client.Put(ctx, metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 909 OkCodes: []int{200}, 910 }) 911 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 912 return 913 } 914 915 // Metadata requests all the metadata for the given server ID. 916 func Metadata(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { 917 resp, err := client.Get(ctx, metadataURL(client, id), &r.Body, nil) 918 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 919 return 920 } 921 922 // UpdateMetadataOptsBuilder allows extensions to add additional parameters to 923 // the Create request. 924 type UpdateMetadataOptsBuilder interface { 925 ToMetadataUpdateMap() (map[string]any, error) 926 } 927 928 // UpdateMetadata updates (or creates) all the metadata specified by opts for 929 // the given server ID. This operation does not affect already-existing metadata 930 // that is not specified by opts. 931 func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { 932 b, err := opts.ToMetadataUpdateMap() 933 if err != nil { 934 r.Err = err 935 return 936 } 937 resp, err := client.Post(ctx, metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 938 OkCodes: []int{200}, 939 }) 940 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 941 return 942 } 943 944 // MetadatumOptsBuilder allows extensions to add additional parameters to the 945 // Create request. 946 type MetadatumOptsBuilder interface { 947 ToMetadatumCreateMap() (map[string]any, string, error) 948 } 949 950 // MetadatumOpts is a map of length one that contains a key-value pair. 951 type MetadatumOpts map[string]string 952 953 // ToMetadatumCreateMap assembles a body for a Create request based on the 954 // contents of a MetadataumOpts. 955 func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]any, string, error) { 956 if len(opts) != 1 { 957 err := gophercloud.ErrInvalidInput{} 958 err.Argument = "servers.MetadatumOpts" 959 err.Info = "Must have 1 and only 1 key-value pair" 960 return nil, "", err 961 } 962 metadatum := map[string]any{"meta": opts} 963 var key string 964 for k := range metadatum["meta"].(MetadatumOpts) { 965 key = k 966 } 967 return metadatum, key, nil 968 } 969 970 // CreateMetadatum will create or update the key-value pair with the given key 971 // for the given server ID. 972 func CreateMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { 973 b, key, err := opts.ToMetadatumCreateMap() 974 if err != nil { 975 r.Err = err 976 return 977 } 978 resp, err := client.Put(ctx, metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ 979 OkCodes: []int{200}, 980 }) 981 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 982 return 983 } 984 985 // Metadatum requests the key-value pair with the given key for the given 986 // server ID. 987 func Metadatum(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { 988 resp, err := client.Get(ctx, metadatumURL(client, id, key), &r.Body, nil) 989 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 990 return 991 } 992 993 // DeleteMetadatum will delete the key-value pair with the given key for the 994 // given server ID. 995 func DeleteMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { 996 resp, err := client.Delete(ctx, metadatumURL(client, id, key), nil) 997 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 998 return 999 } 1000 1001 // ListAddresses makes a request against the API to list the servers IP 1002 // addresses. 1003 func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { 1004 return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page { 1005 return AddressPage{pagination.SinglePageBase(r)} 1006 }) 1007 } 1008 1009 // ListAddressesByNetwork makes a request against the API to list the servers IP 1010 // addresses for the given network. 1011 func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { 1012 return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page { 1013 return NetworkAddressPage{pagination.SinglePageBase(r)} 1014 }) 1015 } 1016 1017 // CreateImageOptsBuilder allows extensions to add additional parameters to the 1018 // CreateImage request. 1019 type CreateImageOptsBuilder interface { 1020 ToServerCreateImageMap() (map[string]any, error) 1021 } 1022 1023 // CreateImageOpts provides options to pass to the CreateImage request. 1024 type CreateImageOpts struct { 1025 // Name of the image/snapshot. 1026 Name string `json:"name" required:"true"` 1027 1028 // Metadata contains key-value pairs (up to 255 bytes each) to attach to 1029 // the created image. 1030 Metadata map[string]string `json:"metadata,omitempty"` 1031 } 1032 1033 // ToServerCreateImageMap formats a CreateImageOpts structure into a request 1034 // body. 1035 func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]any, error) { 1036 return gophercloud.BuildRequestBody(opts, "createImage") 1037 } 1038 1039 // CreateImage makes a request against the nova API to schedule an image to be 1040 // created of the server 1041 func CreateImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { 1042 b, err := opts.ToServerCreateImageMap() 1043 if err != nil { 1044 r.Err = err 1045 return 1046 } 1047 resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ 1048 OkCodes: []int{202}, 1049 }) 1050 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1051 return 1052 } 1053 1054 // GetPassword makes a request against the nova API to get the encrypted 1055 // administrative password. 1056 func GetPassword(ctx context.Context, client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { 1057 resp, err := client.Get(ctx, passwordURL(client, serverId), &r.Body, nil) 1058 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1059 return 1060 } 1061 1062 // ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be 1063 // used as ShowConsoleOutput options 1064 type ShowConsoleOutputOptsBuilder interface { 1065 ToServerShowConsoleOutputMap() (map[string]any, error) 1066 } 1067 1068 // ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder 1069 type ShowConsoleOutputOpts struct { 1070 // The number of lines to fetch from the end of console log. 1071 // All lines will be returned if this is not specified. 1072 Length int `json:"length,omitempty"` 1073 } 1074 1075 // ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body. 1076 func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]any, error) { 1077 return gophercloud.BuildRequestBody(opts, "os-getConsoleOutput") 1078 } 1079 1080 // ShowConsoleOutput makes a request against the nova API to get console log from the server 1081 func ShowConsoleOutput(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) { 1082 b, err := opts.ToServerShowConsoleOutputMap() 1083 if err != nil { 1084 r.Err = err 1085 return 1086 } 1087 resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 1088 OkCodes: []int{200}, 1089 }) 1090 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1091 return 1092 } 1093 1094 // EvacuateOptsBuilder allows extensions to add additional parameters to the 1095 // the Evacuate request. 1096 type EvacuateOptsBuilder interface { 1097 ToEvacuateMap() (map[string]any, error) 1098 } 1099 1100 // EvacuateOpts specifies Evacuate action parameters. 1101 type EvacuateOpts struct { 1102 // The name of the host to which the server is evacuated 1103 Host string `json:"host,omitempty"` 1104 1105 // Indicates whether server is on shared storage 1106 OnSharedStorage bool `json:"onSharedStorage"` 1107 1108 // An administrative password to access the evacuated server 1109 AdminPass string `json:"adminPass,omitempty"` 1110 } 1111 1112 // ToServerGroupCreateMap constructs a request body from CreateOpts. 1113 func (opts EvacuateOpts) ToEvacuateMap() (map[string]any, error) { 1114 return gophercloud.BuildRequestBody(opts, "evacuate") 1115 } 1116 1117 // Evacuate will Evacuate a failed instance to another host. 1118 func Evacuate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts EvacuateOptsBuilder) (r EvacuateResult) { 1119 b, err := opts.ToEvacuateMap() 1120 if err != nil { 1121 r.Err = err 1122 return 1123 } 1124 resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 1125 OkCodes: []int{200}, 1126 }) 1127 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1128 return 1129 } 1130 1131 // InjectNetworkInfo will inject the network info into a server 1132 func InjectNetworkInfo(ctx context.Context, client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { 1133 b := map[string]any{ 1134 "injectNetworkInfo": nil, 1135 } 1136 resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) 1137 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1138 return 1139 } 1140 1141 // Lock is the operation responsible for locking a Compute server. 1142 func Lock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r LockResult) { 1143 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"lock": nil}, nil, nil) 1144 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1145 return 1146 } 1147 1148 // Unlock is the operation responsible for unlocking a Compute server. 1149 func Unlock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnlockResult) { 1150 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unlock": nil}, nil, nil) 1151 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1152 return 1153 } 1154 1155 // Migrate will initiate a migration of the instance to another host. 1156 func Migrate(ctx context.Context, client *gophercloud.ServiceClient, id string) (r MigrateResult) { 1157 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"migrate": nil}, nil, nil) 1158 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1159 return 1160 } 1161 1162 // LiveMigrateOptsBuilder allows extensions to add additional parameters to the 1163 // LiveMigrate request. 1164 type LiveMigrateOptsBuilder interface { 1165 ToLiveMigrateMap() (map[string]any, error) 1166 } 1167 1168 // LiveMigrateOpts specifies parameters of live migrate action. 1169 type LiveMigrateOpts struct { 1170 // The host to which to migrate the server. 1171 // If this parameter is None, the scheduler chooses a host. 1172 Host *string `json:"host"` 1173 1174 // Set to True to migrate local disks by using block migration. 1175 // If the source or destination host uses shared storage and you set 1176 // this value to True, the live migration fails. 1177 BlockMigration *bool `json:"block_migration,omitempty"` 1178 1179 // Set to True to enable over commit when the destination host is checked 1180 // for available disk space. Set to False to disable over commit. This setting 1181 // affects only the libvirt virt driver. 1182 DiskOverCommit *bool `json:"disk_over_commit,omitempty"` 1183 } 1184 1185 // ToLiveMigrateMap constructs a request body from LiveMigrateOpts. 1186 func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]any, error) { 1187 return gophercloud.BuildRequestBody(opts, "os-migrateLive") 1188 } 1189 1190 // LiveMigrate will initiate a live-migration (without rebooting) of the instance to another host. 1191 func LiveMigrate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts LiveMigrateOptsBuilder) (r MigrateResult) { 1192 b, err := opts.ToLiveMigrateMap() 1193 if err != nil { 1194 r.Err = err 1195 return 1196 } 1197 resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) 1198 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1199 return 1200 } 1201 1202 // Pause is the operation responsible for pausing a Compute server. 1203 func Pause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r PauseResult) { 1204 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"pause": nil}, nil, nil) 1205 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1206 return 1207 } 1208 1209 // Unpause is the operation responsible for unpausing a Compute server. 1210 func Unpause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnpauseResult) { 1211 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unpause": nil}, nil, nil) 1212 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1213 return 1214 } 1215 1216 // RescueOptsBuilder is an interface that allows extensions to override the 1217 // default structure of a Rescue request. 1218 type RescueOptsBuilder interface { 1219 ToServerRescueMap() (map[string]any, error) 1220 } 1221 1222 // RescueOpts represents the configuration options used to control a Rescue 1223 // option. 1224 type RescueOpts struct { 1225 // AdminPass is the desired administrative password for the instance in 1226 // RESCUE mode. 1227 // If it's left blank, the server will generate a password. 1228 AdminPass string `json:"adminPass,omitempty"` 1229 1230 // RescueImageRef contains reference on an image that needs to be used as 1231 // rescue image. 1232 // If it's left blank, the server will be rescued with the default image. 1233 RescueImageRef string `json:"rescue_image_ref,omitempty"` 1234 } 1235 1236 // ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON 1237 // request body for the Rescue request. 1238 func (opts RescueOpts) ToServerRescueMap() (map[string]any, error) { 1239 return gophercloud.BuildRequestBody(opts, "rescue") 1240 } 1241 1242 // Rescue instructs the provider to place the server into RESCUE mode. 1243 func Rescue(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { 1244 b, err := opts.ToServerRescueMap() 1245 if err != nil { 1246 r.Err = err 1247 return 1248 } 1249 resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 1250 OkCodes: []int{200}, 1251 }) 1252 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1253 return 1254 } 1255 1256 // Unrescue instructs the provider to return the server from RESCUE mode. 1257 func Unrescue(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnrescueResult) { 1258 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unrescue": nil}, nil, nil) 1259 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1260 return 1261 } 1262 1263 // ResetNetwork will reset the network of a server 1264 func ResetNetwork(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResetNetworkResult) { 1265 b := map[string]any{ 1266 "resetNetwork": nil, 1267 } 1268 resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) 1269 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1270 return 1271 } 1272 1273 // ServerState refers to the states usable in ResetState Action 1274 type ServerState string 1275 1276 const ( 1277 // StateActive returns the state of the server as active 1278 StateActive ServerState = "active" 1279 1280 // StateError returns the state of the server as error 1281 StateError ServerState = "error" 1282 ) 1283 1284 // ResetState will reset the state of a server 1285 func ResetState(ctx context.Context, client *gophercloud.ServiceClient, id string, state ServerState) (r ResetStateResult) { 1286 stateMap := map[string]any{"state": state} 1287 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-resetState": stateMap}, nil, nil) 1288 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1289 return 1290 } 1291 1292 // Shelve is the operation responsible for shelving a Compute server. 1293 func Shelve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveResult) { 1294 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"shelve": nil}, nil, nil) 1295 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1296 return 1297 } 1298 1299 // ShelveOffload is the operation responsible for Shelve-Offload a Compute server. 1300 func ShelveOffload(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { 1301 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"shelveOffload": nil}, nil, nil) 1302 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1303 return 1304 } 1305 1306 // UnshelveOptsBuilder allows extensions to add additional parameters to the 1307 // Unshelve request. 1308 type UnshelveOptsBuilder interface { 1309 ToUnshelveMap() (map[string]any, error) 1310 } 1311 1312 // UnshelveOpts specifies parameters of shelve-offload action. 1313 type UnshelveOpts struct { 1314 // Sets the availability zone to unshelve a server 1315 // Available only after nova 2.77 1316 AvailabilityZone string `json:"availability_zone,omitempty"` 1317 } 1318 1319 func (opts UnshelveOpts) ToUnshelveMap() (map[string]any, error) { 1320 // Key 'availabilty_zone' is required if the unshelve action is an object 1321 // i.e {"unshelve": {}} will be rejected 1322 b, err := gophercloud.BuildRequestBody(opts, "unshelve") 1323 if err != nil { 1324 return nil, err 1325 } 1326 1327 if _, ok := b["unshelve"].(map[string]any)["availability_zone"]; !ok { 1328 b["unshelve"] = nil 1329 } 1330 1331 return b, err 1332 } 1333 1334 // Unshelve is the operation responsible for unshelve a Compute server. 1335 func Unshelve(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UnshelveOptsBuilder) (r UnshelveResult) { 1336 b, err := opts.ToUnshelveMap() 1337 if err != nil { 1338 r.Err = err 1339 return 1340 } 1341 resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) 1342 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1343 return 1344 } 1345 1346 // Start is the operation responsible for starting a Compute server. 1347 func Start(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StartResult) { 1348 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-start": nil}, nil, nil) 1349 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1350 return 1351 } 1352 1353 // Stop is the operation responsible for stopping a Compute server. 1354 func Stop(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StopResult) { 1355 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-stop": nil}, nil, nil) 1356 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1357 return 1358 } 1359 1360 // Suspend is the operation responsible for suspending a Compute server. 1361 func Suspend(ctx context.Context, client *gophercloud.ServiceClient, id string) (r SuspendResult) { 1362 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"suspend": nil}, nil, nil) 1363 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1364 return 1365 } 1366 1367 // Resume is the operation responsible for resuming a Compute server. 1368 func Resume(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResumeResult) { 1369 resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"resume": nil}, nil, nil) 1370 _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 1371 return 1372 }