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  }