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  }