github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/openstack/compute/v2/servers/results.go (about) 1 package servers 2 3 import ( 4 "crypto/rsa" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "net/url" 9 "path" 10 "time" 11 12 "github.com/vnpaycloud-console/gophercloud/v2" 13 "github.com/vnpaycloud-console/gophercloud/v2/pagination" 14 ) 15 16 type serverResult struct { 17 gophercloud.Result 18 } 19 20 // Extract interprets any serverResult as a Server, if possible. 21 func (r serverResult) Extract() (*Server, error) { 22 var s Server 23 err := r.ExtractInto(&s) 24 return &s, err 25 } 26 27 func (r serverResult) ExtractInto(v any) error { 28 return r.Result.ExtractIntoStructPtr(v, "server") 29 } 30 31 func ExtractServersInto(r pagination.Page, v any) error { 32 return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers") 33 } 34 35 // CreateResult is the response from a Create operation. Call its Extract 36 // method to interpret it as a Server. 37 type CreateResult struct { 38 serverResult 39 } 40 41 // GetResult is the response from a Get operation. Call its Extract 42 // method to interpret it as a Server. 43 type GetResult struct { 44 serverResult 45 } 46 47 // UpdateResult is the response from an Update operation. Call its Extract 48 // method to interpret it as a Server. 49 type UpdateResult struct { 50 serverResult 51 } 52 53 // DeleteResult is the response from a Delete operation. Call its ExtractErr 54 // method to determine if the call succeeded or failed. 55 type DeleteResult struct { 56 gophercloud.ErrResult 57 } 58 59 // RebuildResult is the response from a Rebuild operation. Call its Extract 60 // method to interpret it as a Server. 61 type RebuildResult struct { 62 serverResult 63 } 64 65 // ActionResult represents the result of server action operations, like reboot. 66 // Call its ExtractErr method to determine if the action succeeded or failed. 67 type ActionResult struct { 68 gophercloud.ErrResult 69 } 70 71 // CreateImageResult is the response from a CreateImage operation. Call its 72 // ExtractImageID method to retrieve the ID of the newly created image. 73 type CreateImageResult struct { 74 gophercloud.Result 75 } 76 77 // ShowConsoleOutputResult represents the result of console output from a server 78 type ShowConsoleOutputResult struct { 79 gophercloud.Result 80 } 81 82 // Extract will return the console output from a ShowConsoleOutput request. 83 func (r ShowConsoleOutputResult) Extract() (string, error) { 84 var s struct { 85 Output string `json:"output"` 86 } 87 88 err := r.ExtractInto(&s) 89 return s.Output, err 90 } 91 92 // GetPasswordResult represent the result of a get os-server-password operation. 93 // Call its ExtractPassword method to retrieve the password. 94 type GetPasswordResult struct { 95 gophercloud.Result 96 } 97 98 // ExtractPassword gets the encrypted password. 99 // If privateKey != nil the password is decrypted with the private key. 100 // If privateKey == nil the encrypted password is returned and can be decrypted 101 // with: 102 // 103 // echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key> 104 func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) { 105 var s struct { 106 Password string `json:"password"` 107 } 108 err := r.ExtractInto(&s) 109 if err == nil && privateKey != nil && s.Password != "" { 110 return decryptPassword(s.Password, privateKey) 111 } 112 return s.Password, err 113 } 114 115 func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) { 116 b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword))) 117 118 n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword)) 119 if err != nil { 120 return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err) 121 } 122 password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n]) 123 if err != nil { 124 return "", fmt.Errorf("Failed to decrypt password: %s", err) 125 } 126 127 return string(password), nil 128 } 129 130 // ExtractImageID gets the ID of the newly created server image from the header. 131 func (r CreateImageResult) ExtractImageID() (string, error) { 132 if r.Err != nil { 133 return "", r.Err 134 } 135 // Get the image id from the header 136 u, err := url.ParseRequestURI(r.Header.Get("Location")) 137 if err != nil { 138 return "", err 139 } 140 imageID := path.Base(u.Path) 141 if imageID == "." || imageID == "/" { 142 return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u) 143 } 144 return imageID, nil 145 } 146 147 // Server represents a server/instance in the OpenStack cloud. 148 type Server struct { 149 // ID uniquely identifies this server amongst all other servers, 150 // including those not accessible to the current tenant. 151 ID string `json:"id"` 152 153 // TenantID identifies the tenant owning this server resource. 154 TenantID string `json:"tenant_id"` 155 156 // UserID uniquely identifies the user account owning the tenant. 157 UserID string `json:"user_id"` 158 159 // Name contains the human-readable name for the server. 160 Name string `json:"name"` 161 162 // Updated and Created contain ISO-8601 timestamps of when the state of the 163 // server last changed, and when it was created. 164 Updated time.Time `json:"updated"` 165 Created time.Time `json:"created"` 166 167 // HostID is the host where the server is located in the cloud. 168 HostID string `json:"hostid"` 169 170 // Status contains the current operational status of the server, 171 // such as IN_PROGRESS or ACTIVE. 172 Status string `json:"status"` 173 174 // Progress ranges from 0..100. 175 // A request made against the server completes only once Progress reaches 100. 176 Progress int `json:"progress"` 177 178 // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, 179 // suitable for remote access for administration. 180 AccessIPv4 string `json:"accessIPv4"` 181 AccessIPv6 string `json:"accessIPv6"` 182 183 // Image refers to a JSON object, which itself indicates the OS image used to 184 // deploy the server. 185 Image map[string]any `json:"-"` 186 187 // Flavor refers to a JSON object, which itself indicates the hardware 188 // configuration of the deployed server. 189 Flavor map[string]any `json:"flavor"` 190 191 // Addresses includes a list of all IP addresses assigned to the server, 192 // keyed by pool. 193 Addresses map[string]any `json:"addresses"` 194 195 // Metadata includes a list of all user-specified key-value pairs attached 196 // to the server. 197 Metadata map[string]string `json:"metadata"` 198 199 // Links includes HTTP references to the itself, useful for passing along to 200 // other APIs that might want a server reference. 201 Links []any `json:"links"` 202 203 // KeyName indicates which public key was injected into the server on launch. 204 KeyName string `json:"key_name"` 205 206 // AdminPass will generally be empty (""). However, it will contain the 207 // administrative password chosen when provisioning a new server without a 208 // set AdminPass setting in the first place. 209 // Note that this is the ONLY time this field will be valid. 210 AdminPass string `json:"adminPass"` 211 212 // SecurityGroups includes the security groups that this instance has applied 213 // to it. 214 SecurityGroups []map[string]any `json:"security_groups"` 215 216 // AttachedVolumes includes the volume attachments of this instance 217 AttachedVolumes []AttachedVolume `json:"os-extended-volumes:volumes_attached"` 218 219 // Fault contains failure information about a server. 220 Fault Fault `json:"fault"` 221 222 // Tags is a slice/list of string tags in a server. 223 // The requires microversion 2.26 or later. 224 Tags *[]string `json:"tags"` 225 226 // ServerGroups is a slice of strings containing the UUIDs of the 227 // server groups to which the server belongs. Currently this can 228 // contain at most one entry. 229 // New in microversion 2.71 230 ServerGroups *[]string `json:"server_groups"` 231 232 // Host is the host/hypervisor that the instance is hosted on. 233 Host string `json:"OS-EXT-SRV-ATTR:host"` 234 235 // InstanceName is the name of the instance. 236 InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` 237 238 // HypervisorHostname is the hostname of the host/hypervisor that the 239 // instance is hosted on. 240 HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` 241 242 // ReservationID is the reservation ID of the instance. 243 // This requires microversion 2.3 or later. 244 ReservationID *string `json:"OS-EXT-SRV-ATTR:reservation_id"` 245 246 // LaunchIndex is the launch index of the instance. 247 // This requires microversion 2.3 or later. 248 LaunchIndex *int `json:"OS-EXT-SRV-ATTR:launch_index"` 249 250 // RAMDiskID is the ID of the RAM disk image of the instance. 251 // This requires microversion 2.3 or later. 252 RAMDiskID *string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` 253 254 // KernelID is the ID of the kernel image of the instance. 255 // This requires microversion 2.3 or later. 256 KernelID *string `json:"OS-EXT-SRV-ATTR:kernel_id"` 257 258 // Hostname is the hostname of the instance. 259 // This requires microversion 2.3 or later. 260 Hostname *string `json:"OS-EXT-SRV-ATTR:hostname"` 261 262 // RootDeviceName is the name of the root device of the instance. 263 // This requires microversion 2.3 or later. 264 RootDeviceName *string `json:"OS-EXT-SRV-ATTR:root_device_name"` 265 266 // Userdata is the userdata of the instance. 267 // This requires microversion 2.3 or later. 268 Userdata *string `json:"OS-EXT-SRV-ATTR:user_data"` 269 270 TaskState string `json:"OS-EXT-STS:task_state"` 271 VmState string `json:"OS-EXT-STS:vm_state"` 272 PowerState PowerState `json:"OS-EXT-STS:power_state"` 273 274 LaunchedAt time.Time `json:"-"` 275 TerminatedAt time.Time `json:"-"` 276 277 // DiskConfig is the disk configuration of the server. 278 DiskConfig DiskConfig `json:"OS-DCF:diskConfig"` 279 280 // AvailabilityZone is the availabilty zone the server is in. 281 AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` 282 283 // Locked indicates the lock status of the server 284 // This requires microversion 2.9 or later 285 Locked *bool `json:"locked"` 286 } 287 288 type AttachedVolume struct { 289 ID string `json:"id"` 290 } 291 292 type Fault struct { 293 Code int `json:"code"` 294 Created time.Time `json:"created"` 295 Details string `json:"details"` 296 Message string `json:"message"` 297 } 298 299 type PowerState int 300 301 type ServerExtendedStatusExt struct { 302 TaskState string `json:"OS-EXT-STS:task_state"` 303 VmState string `json:"OS-EXT-STS:vm_state"` 304 PowerState PowerState `json:"OS-EXT-STS:power_state"` 305 } 306 307 const ( 308 NOSTATE = iota 309 RUNNING 310 _UNUSED1 311 PAUSED 312 SHUTDOWN 313 _UNUSED2 314 CRASHED 315 SUSPENDED 316 ) 317 318 func (r PowerState) String() string { 319 switch r { 320 case NOSTATE: 321 return "NOSTATE" 322 case RUNNING: 323 return "RUNNING" 324 case PAUSED: 325 return "PAUSED" 326 case SHUTDOWN: 327 return "SHUTDOWN" 328 case CRASHED: 329 return "CRASHED" 330 case SUSPENDED: 331 return "SUSPENDED" 332 case _UNUSED1, _UNUSED2: 333 return "_UNUSED" 334 default: 335 return "N/A" 336 } 337 } 338 339 func (r *Server) UnmarshalJSON(b []byte) error { 340 type tmp Server 341 var s struct { 342 tmp 343 Image any `json:"image"` 344 LaunchedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:launched_at"` 345 TerminatedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:terminated_at"` 346 } 347 err := json.Unmarshal(b, &s) 348 if err != nil { 349 return err 350 } 351 352 *r = Server(s.tmp) 353 354 switch t := s.Image.(type) { 355 case map[string]any: 356 r.Image = t 357 case string: 358 switch t { 359 case "": 360 r.Image = nil 361 } 362 } 363 364 r.LaunchedAt = time.Time(s.LaunchedAt) 365 r.TerminatedAt = time.Time(s.TerminatedAt) 366 367 return err 368 } 369 370 // ServerPage abstracts the raw results of making a List() request against 371 // the API. As OpenStack extensions may freely alter the response bodies of 372 // structures returned to the client, you may only safely access the data 373 // provided through the ExtractServers call. 374 type ServerPage struct { 375 pagination.LinkedPageBase 376 } 377 378 // IsEmpty returns true if a page contains no Server results. 379 func (r ServerPage) IsEmpty() (bool, error) { 380 if r.StatusCode == 204 { 381 return true, nil 382 } 383 384 s, err := ExtractServers(r) 385 return len(s) == 0, err 386 } 387 388 // NextPageURL uses the response's embedded link reference to navigate to the 389 // next page of results. 390 func (r ServerPage) NextPageURL() (string, error) { 391 var s struct { 392 Links []gophercloud.Link `json:"servers_links"` 393 } 394 err := r.ExtractInto(&s) 395 if err != nil { 396 return "", err 397 } 398 return gophercloud.ExtractNextURL(s.Links) 399 } 400 401 // ExtractServers interprets the results of a single page from a List() call, 402 // producing a slice of Server entities. 403 func ExtractServers(r pagination.Page) ([]Server, error) { 404 var s []Server 405 err := ExtractServersInto(r, &s) 406 return s, err 407 } 408 409 // MetadataResult contains the result of a call for (potentially) multiple 410 // key-value pairs. Call its Extract method to interpret it as a 411 // map[string]interface. 412 type MetadataResult struct { 413 gophercloud.Result 414 } 415 416 // GetMetadataResult contains the result of a Get operation. Call its Extract 417 // method to interpret it as a map[string]interface. 418 type GetMetadataResult struct { 419 MetadataResult 420 } 421 422 // ResetMetadataResult contains the result of a Reset operation. Call its 423 // Extract method to interpret it as a map[string]interface. 424 type ResetMetadataResult struct { 425 MetadataResult 426 } 427 428 // UpdateMetadataResult contains the result of an Update operation. Call its 429 // Extract method to interpret it as a map[string]interface. 430 type UpdateMetadataResult struct { 431 MetadataResult 432 } 433 434 // MetadatumResult contains the result of a call for individual a single 435 // key-value pair. 436 type MetadatumResult struct { 437 gophercloud.Result 438 } 439 440 // GetMetadatumResult contains the result of a Get operation. Call its Extract 441 // method to interpret it as a map[string]interface. 442 type GetMetadatumResult struct { 443 MetadatumResult 444 } 445 446 // CreateMetadatumResult contains the result of a Create operation. Call its 447 // Extract method to interpret it as a map[string]interface. 448 type CreateMetadatumResult struct { 449 MetadatumResult 450 } 451 452 // DeleteMetadatumResult contains the result of a Delete operation. Call its 453 // ExtractErr method to determine if the call succeeded or failed. 454 type DeleteMetadatumResult struct { 455 gophercloud.ErrResult 456 } 457 458 // Extract interprets any MetadataResult as a Metadata, if possible. 459 func (r MetadataResult) Extract() (map[string]string, error) { 460 var s struct { 461 Metadata map[string]string `json:"metadata"` 462 } 463 err := r.ExtractInto(&s) 464 return s.Metadata, err 465 } 466 467 // Extract interprets any MetadatumResult as a Metadatum, if possible. 468 func (r MetadatumResult) Extract() (map[string]string, error) { 469 var s struct { 470 Metadatum map[string]string `json:"meta"` 471 } 472 err := r.ExtractInto(&s) 473 return s.Metadatum, err 474 } 475 476 // Address represents an IP address. 477 type Address struct { 478 Version int `json:"version"` 479 Address string `json:"addr"` 480 } 481 482 // AddressPage abstracts the raw results of making a ListAddresses() request 483 // against the API. As OpenStack extensions may freely alter the response bodies 484 // of structures returned to the client, you may only safely access the data 485 // provided through the ExtractAddresses call. 486 type AddressPage struct { 487 pagination.SinglePageBase 488 } 489 490 // IsEmpty returns true if an AddressPage contains no networks. 491 func (r AddressPage) IsEmpty() (bool, error) { 492 if r.StatusCode == 204 { 493 return true, nil 494 } 495 496 addresses, err := ExtractAddresses(r) 497 return len(addresses) == 0, err 498 } 499 500 // ExtractAddresses interprets the results of a single page from a 501 // ListAddresses() call, producing a map of addresses. 502 func ExtractAddresses(r pagination.Page) (map[string][]Address, error) { 503 var s struct { 504 Addresses map[string][]Address `json:"addresses"` 505 } 506 err := (r.(AddressPage)).ExtractInto(&s) 507 return s.Addresses, err 508 } 509 510 // NetworkAddressPage abstracts the raw results of making a 511 // ListAddressesByNetwork() request against the API. 512 // As OpenStack extensions may freely alter the response bodies of structures 513 // returned to the client, you may only safely access the data provided through 514 // the ExtractAddresses call. 515 type NetworkAddressPage struct { 516 pagination.SinglePageBase 517 } 518 519 // IsEmpty returns true if a NetworkAddressPage contains no addresses. 520 func (r NetworkAddressPage) IsEmpty() (bool, error) { 521 if r.StatusCode == 204 { 522 return true, nil 523 } 524 525 addresses, err := ExtractNetworkAddresses(r) 526 return len(addresses) == 0, err 527 } 528 529 // ExtractNetworkAddresses interprets the results of a single page from a 530 // ListAddressesByNetwork() call, producing a slice of addresses. 531 func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) { 532 var s map[string][]Address 533 err := (r.(NetworkAddressPage)).ExtractInto(&s) 534 if err != nil { 535 return nil, err 536 } 537 538 var key string 539 for k := range s { 540 key = k 541 } 542 543 return s[key], err 544 } 545 546 // EvacuateResult is the response from an Evacuate operation. 547 // Call its ExtractAdminPass method to retrieve the admin password of the instance. 548 // The admin password will be an empty string if the cloud is not configured to inject admin passwords.. 549 type EvacuateResult struct { 550 gophercloud.Result 551 } 552 553 func (r EvacuateResult) ExtractAdminPass() (string, error) { 554 var s struct { 555 AdminPass string `json:"adminPass"` 556 } 557 err := r.ExtractInto(&s) 558 if err != nil && err.Error() == "EOF" { 559 return "", nil 560 } 561 return s.AdminPass, err 562 } 563 564 // InjectNetworkResult is the response of a InjectNetworkInfo operation. Call 565 // its ExtractErr method to determine if the request suceeded or failed. 566 type InjectNetworkResult struct { 567 gophercloud.ErrResult 568 } 569 570 // LockResult and UnlockResult are the responses from a Lock and Unlock 571 // operations respectively. Call their ExtractErr methods to determine if the 572 // requests suceeded or failed. 573 type LockResult struct { 574 gophercloud.ErrResult 575 } 576 577 type UnlockResult struct { 578 gophercloud.ErrResult 579 } 580 581 // MigrateResult is the response from a Migrate operation. Call its ExtractErr 582 // method to determine if the request suceeded or failed. 583 type MigrateResult struct { 584 gophercloud.ErrResult 585 } 586 587 // PauseResult is the response from a Pause operation. Call its ExtractErr 588 // method to determine if the request succeeded or failed. 589 type PauseResult struct { 590 gophercloud.ErrResult 591 } 592 593 // UnpauseResult is the response from an Unpause operation. Call its ExtractErr 594 // method to determine if the request succeeded or failed. 595 type UnpauseResult struct { 596 gophercloud.ErrResult 597 } 598 599 type commonResult struct { 600 gophercloud.Result 601 } 602 603 // RescueResult is the response from a Rescue operation. Call its Extract 604 // method to retrieve adminPass for a rescued server. 605 type RescueResult struct { 606 commonResult 607 } 608 609 // UnrescueResult is the response from an UnRescue operation. Call its ExtractErr 610 // method to determine if the call succeeded or failed. 611 type UnrescueResult struct { 612 gophercloud.ErrResult 613 } 614 615 // Extract interprets any RescueResult as an AdminPass, if possible. 616 func (r RescueResult) Extract() (string, error) { 617 var s struct { 618 AdminPass string `json:"adminPass"` 619 } 620 err := r.ExtractInto(&s) 621 return s.AdminPass, err 622 } 623 624 // ResetResult is the response of a ResetNetwork operation. Call its ExtractErr 625 // method to determine if the request suceeded or failed. 626 type ResetNetworkResult struct { 627 gophercloud.ErrResult 628 } 629 630 // ResetResult is the response of a ResetState operation. Call its ExtractErr 631 // method to determine if the request suceeded or failed. 632 type ResetStateResult struct { 633 gophercloud.ErrResult 634 } 635 636 // ShelveResult is the response from a Shelve operation. Call its ExtractErr 637 // method to determine if the request succeeded or failed. 638 type ShelveResult struct { 639 gophercloud.ErrResult 640 } 641 642 // ShelveOffloadResult is the response from a Shelve operation. Call its ExtractErr 643 // method to determine if the request succeeded or failed. 644 type ShelveOffloadResult struct { 645 gophercloud.ErrResult 646 } 647 648 // UnshelveResult is the response from Stop operation. Call its ExtractErr 649 // method to determine if the request succeeded or failed. 650 type UnshelveResult struct { 651 gophercloud.ErrResult 652 } 653 654 // StartResult is the response from a Start operation. Call its ExtractErr 655 // method to determine if the request succeeded or failed. 656 type StartResult struct { 657 gophercloud.ErrResult 658 } 659 660 // StopResult is the response from Stop operation. Call its ExtractErr 661 // method to determine if the request succeeded or failed. 662 type StopResult struct { 663 gophercloud.ErrResult 664 } 665 666 // SuspendResult is the response from a Suspend operation. Call its 667 // ExtractErr method to determine if the request succeeded or failed. 668 type SuspendResult struct { 669 gophercloud.ErrResult 670 } 671 672 // ResumeResult is the response from an Unsuspend operation. Call 673 // its ExtractErr method to determine if the request succeeded or failed. 674 type ResumeResult struct { 675 gophercloud.ErrResult 676 }