github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/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/opentelekomcloud/gophertelekomcloud" 13 "github.com/opentelekomcloud/gophertelekomcloud/pagination" 14 ) 15 16 type serverResult struct { 17 golangsdk.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 interface{}) error { 28 return r.Result.ExtractIntoStructPtr(v, "server") 29 } 30 31 func ExtractServersInto(r pagination.Page, v interface{}) 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 // GetNICResult is the response from a Get operation. Call its Extract 48 // method to interpret it as a NIC. 49 type GetNICResult struct { 50 serverResult 51 } 52 53 // UpdateResult is the response from an Update operation. Call its Extract 54 // method to interpret it as a Server. 55 type UpdateResult struct { 56 serverResult 57 } 58 59 // DeleteResult is the response from a Delete operation. Call its ExtractErr 60 // method to determine if the call succeeded or failed. 61 type DeleteResult struct { 62 golangsdk.ErrResult 63 } 64 65 // RebuildResult is the response from a Rebuild operation. Call its Extract 66 // method to interpret it as a Server. 67 type RebuildResult struct { 68 serverResult 69 } 70 71 // ActionResult represents the result of server action operations, like reboot. 72 // Call its ExtractErr method to determine if the action succeeded or failed. 73 type ActionResult struct { 74 golangsdk.ErrResult 75 } 76 77 // CreateImageResult is the response from a CreateImage operation. Call its 78 // ExtractImageID method to retrieve the ID of the newly created image. 79 type CreateImageResult struct { 80 golangsdk.Result 81 } 82 83 // ShowConsoleOutputResult represents the result of console output from a server 84 type ShowConsoleOutputResult struct { 85 golangsdk.Result 86 } 87 88 // Extract will return the console output from a ShowConsoleOutput request. 89 func (r ShowConsoleOutputResult) Extract() (string, error) { 90 var s struct { 91 Output string `json:"output"` 92 } 93 94 err := r.ExtractInto(&s) 95 return s.Output, err 96 } 97 98 // GetPasswordResult represent the result of a get os-server-password operation. 99 // Call its ExtractPassword method to retrieve the password. 100 type GetPasswordResult struct { 101 golangsdk.Result 102 } 103 104 // ExtractPassword gets the encrypted password. 105 // If privateKey != nil the password is decrypted with the private key. 106 // If privateKey == nil the encrypted password is returned and can be decrypted 107 // with: 108 // echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key> 109 func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) { 110 var s struct { 111 Password string `json:"password"` 112 } 113 err := r.ExtractInto(&s) 114 if err == nil && privateKey != nil && s.Password != "" { 115 return decryptPassword(s.Password, privateKey) 116 } 117 return s.Password, err 118 } 119 120 func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) { 121 b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword))) 122 123 n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword)) 124 if err != nil { 125 return "", fmt.Errorf("failed to base64 decode encrypted password: %w", err) 126 } 127 password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n]) 128 if err != nil { 129 return "", fmt.Errorf("failed to decrypt password: %w", err) 130 } 131 132 return string(password), nil 133 } 134 135 // ExtractImageID gets the ID of the newly created server image from the header. 136 func (r CreateImageResult) ExtractImageID() (string, error) { 137 if r.Err != nil { 138 return "", r.Err 139 } 140 // Get the image id from the header 141 u, err := url.ParseRequestURI(r.Header.Get("Location")) 142 if err != nil { 143 return "", err 144 } 145 imageID := path.Base(u.Path) 146 if imageID == "." || imageID == "/" { 147 return "", fmt.Errorf("failed to parse the ID of newly created image: %s", u) 148 } 149 return imageID, nil 150 } 151 152 // Server represents a server/instance in the OpenStack cloud. 153 type Server struct { 154 // ID uniquely identifies this server amongst all other servers, 155 // including those not accessible to the current tenant. 156 ID string `json:"id"` 157 158 // TenantID identifies the tenant owning this server resource. 159 TenantID string `json:"tenant_id"` 160 161 // UserID uniquely identifies the user account owning the tenant. 162 UserID string `json:"user_id"` 163 164 // Name contains the human-readable name for the server. 165 Name string `json:"name"` 166 167 // Updated and Created contain ISO-8601 timestamps of when the state of the 168 // server last changed, and when it was created. 169 Updated time.Time `json:"updated"` 170 Created time.Time `json:"created"` 171 172 // HostID is the host where the server is located in the cloud. 173 HostID string `json:"hostid"` 174 175 // Status contains the current operational status of the server, 176 // such as IN_PROGRESS or ACTIVE. 177 Status string `json:"status"` 178 179 // Progress ranges from 0..100. 180 // A request made against the server completes only once Progress reaches 100. 181 Progress int `json:"progress"` 182 183 // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, 184 // suitable for remote access for administration. 185 AccessIPv4 string `json:"accessIPv4"` 186 AccessIPv6 string `json:"accessIPv6"` 187 188 // Image refers to a JSON object, which itself indicates the OS image used to 189 // deploy the server. 190 Image map[string]interface{} `json:"-"` 191 192 // Flavor refers to a JSON object, which itself indicates the hardware 193 // configuration of the deployed server. 194 Flavor map[string]interface{} `json:"flavor"` 195 196 // Addresses includes a list of all IP addresses assigned to the server, 197 // keyed by pool. 198 Addresses map[string]interface{} `json:"addresses"` 199 200 // Metadata includes a list of all user-specified key-value pairs attached 201 // to the server. 202 Metadata map[string]string `json:"metadata"` 203 204 // Links includes HTTP references to the itself, useful for passing along to 205 // other APIs that might want a server reference. 206 Links []interface{} `json:"links"` 207 208 // KeyName indicates which public key was injected into the server on launch. 209 KeyName string `json:"key_name"` 210 211 // AdminPass will generally be empty (""). However, it will contain the 212 // administrative password chosen when provisioning a new server without a 213 // set AdminPass setting in the first place. 214 // Note that this is the ONLY time this field will be valid. 215 AdminPass string `json:"adminPass"` 216 217 // SecurityGroups includes the security groups that this instance has applied 218 // to it. 219 SecurityGroups []map[string]interface{} `json:"security_groups"` 220 221 // Fault contains failure information about a server. 222 Fault Fault `json:"fault"` 223 224 // VolumeAttached includes the volumes that attached to the server. 225 VolumesAttached []map[string]string `json:"os-extended-volumes:volumes_attached"` 226 } 227 228 type Fault struct { 229 Code int `json:"code"` 230 Created time.Time `json:"created"` 231 Details string `json:"details"` 232 Message string `json:"message"` 233 } 234 235 func (r *Server) UnmarshalJSON(b []byte) error { 236 type tmp Server 237 var s struct { 238 tmp 239 Image interface{} `json:"image"` 240 } 241 err := json.Unmarshal(b, &s) 242 if err != nil { 243 return err 244 } 245 246 *r = Server(s.tmp) 247 248 switch t := s.Image.(type) { 249 case map[string]interface{}: 250 r.Image = t 251 case string: 252 switch t { 253 case "": 254 r.Image = nil 255 } 256 } 257 258 return err 259 } 260 261 // Extract interprets any serverResult as a Server, if possible. 262 func (r GetNICResult) Extract() ([]NIC, error) { 263 var s []NIC 264 err := r.ExtractIntoSlicePtr(&s, "interfaceAttachments") 265 return s, err 266 } 267 268 type NIC struct { 269 PortState string `json:"port_state"` 270 FixedIPs []FixedIP `json:"fixed_ips"` 271 PortID string `json:"port_id"` 272 NetID string `json:"net_id"` 273 MACAddress string `json:"mac_addr"` 274 } 275 276 type FixedIP struct { 277 SubnetID string `json:"subnet_id"` 278 IPAddress string `json:"ip_address"` 279 } 280 281 // ServerPage abstracts the raw results of making a List() request against 282 // the API. As OpenStack extensions may freely alter the response bodies of 283 // structures returned to the client, you may only safely access the data 284 // provided through the ExtractServers call. 285 type ServerPage struct { 286 pagination.LinkedPageBase 287 } 288 289 // IsEmpty returns true if a page contains no Server results. 290 func (r ServerPage) IsEmpty() (bool, error) { 291 s, err := ExtractServers(r) 292 return len(s) == 0, err 293 } 294 295 // NextPageURL uses the response's embedded link reference to navigate to the 296 // next page of results. 297 func (r ServerPage) NextPageURL() (string, error) { 298 var s struct { 299 Links []golangsdk.Link `json:"servers_links"` 300 } 301 err := r.ExtractInto(&s) 302 if err != nil { 303 return "", err 304 } 305 return golangsdk.ExtractNextURL(s.Links) 306 } 307 308 // ExtractServers interprets the results of a single page from a List() call, 309 // producing a slice of Server entities. 310 func ExtractServers(r pagination.Page) ([]Server, error) { 311 var s []Server 312 err := ExtractServersInto(r, &s) 313 return s, err 314 } 315 316 // MetadataResult contains the result of a call for (potentially) multiple 317 // key-value pairs. Call its Extract method to interpret it as a 318 // map[string]interface. 319 type MetadataResult struct { 320 golangsdk.Result 321 } 322 323 // GetMetadataResult contains the result of a Get operation. Call its Extract 324 // method to interpret it as a map[string]interface. 325 type GetMetadataResult struct { 326 MetadataResult 327 } 328 329 // ResetMetadataResult contains the result of a Reset operation. Call its 330 // Extract method to interpret it as a map[string]interface. 331 type ResetMetadataResult struct { 332 MetadataResult 333 } 334 335 // UpdateMetadataResult contains the result of an Update operation. Call its 336 // Extract method to interpret it as a map[string]interface. 337 type UpdateMetadataResult struct { 338 MetadataResult 339 } 340 341 // MetadatumResult contains the result of a call for individual a single 342 // key-value pair. 343 type MetadatumResult struct { 344 golangsdk.Result 345 } 346 347 // GetMetadatumResult contains the result of a Get operation. Call its Extract 348 // method to interpret it as a map[string]interface. 349 type GetMetadatumResult struct { 350 MetadatumResult 351 } 352 353 // CreateMetadatumResult contains the result of a Create operation. Call its 354 // Extract method to interpret it as a map[string]interface. 355 type CreateMetadatumResult struct { 356 MetadatumResult 357 } 358 359 // DeleteMetadatumResult contains the result of a Delete operation. Call its 360 // ExtractErr method to determine if the call succeeded or failed. 361 type DeleteMetadatumResult struct { 362 golangsdk.ErrResult 363 } 364 365 // Extract interprets any MetadataResult as a Metadata, if possible. 366 func (r MetadataResult) Extract() (map[string]string, error) { 367 var s struct { 368 Metadata map[string]string `json:"metadata"` 369 } 370 err := r.ExtractInto(&s) 371 return s.Metadata, err 372 } 373 374 // Extract interprets any MetadatumResult as a Metadatum, if possible. 375 func (r MetadatumResult) Extract() (map[string]string, error) { 376 var s struct { 377 Metadatum map[string]string `json:"meta"` 378 } 379 err := r.ExtractInto(&s) 380 return s.Metadatum, err 381 } 382 383 // Address represents an IP address. 384 type Address struct { 385 Version int `json:"version"` 386 Address string `json:"addr"` 387 } 388 389 // AddressPage abstracts the raw results of making a ListAddresses() request 390 // against the API. As OpenStack extensions may freely alter the response bodies 391 // of structures returned to the client, you may only safely access the data 392 // provided through the ExtractAddresses call. 393 type AddressPage struct { 394 pagination.SinglePageBase 395 } 396 397 // IsEmpty returns true if an AddressPage contains no networks. 398 func (r AddressPage) IsEmpty() (bool, error) { 399 addresses, err := ExtractAddresses(r) 400 return len(addresses) == 0, err 401 } 402 403 // ExtractAddresses interprets the results of a single page from a 404 // ListAddresses() call, producing a map of addresses. 405 func ExtractAddresses(r pagination.Page) (map[string][]Address, error) { 406 var s struct { 407 Addresses map[string][]Address `json:"addresses"` 408 } 409 err := (r.(AddressPage)).ExtractInto(&s) 410 return s.Addresses, err 411 } 412 413 // NetworkAddressPage abstracts the raw results of making a 414 // ListAddressesByNetwork() request against the API. 415 // As OpenStack extensions may freely alter the response bodies of structures 416 // returned to the client, you may only safely access the data provided through 417 // the ExtractAddresses call. 418 type NetworkAddressPage struct { 419 pagination.SinglePageBase 420 } 421 422 // IsEmpty returns true if a NetworkAddressPage contains no addresses. 423 func (r NetworkAddressPage) IsEmpty() (bool, error) { 424 addresses, err := ExtractNetworkAddresses(r) 425 return len(addresses) == 0, err 426 } 427 428 // ExtractNetworkAddresses interprets the results of a single page from a 429 // ListAddressesByNetwork() call, producing a slice of addresses. 430 func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) { 431 var s map[string][]Address 432 err := (r.(NetworkAddressPage)).ExtractInto(&s) 433 if err != nil { 434 return nil, err 435 } 436 437 var key string 438 for k := range s { 439 key = k 440 } 441 442 return s[key], err 443 }