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