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  }