github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/openstack/compute/v2/servers/requests.go (about)

     1  package servers
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  
     7  	"github.com/opentelekomcloud/gophertelekomcloud"
     8  	"github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/flavors"
     9  	"github.com/opentelekomcloud/gophertelekomcloud/openstack/ims/v2/images"
    10  	"github.com/opentelekomcloud/gophertelekomcloud/pagination"
    11  )
    12  
    13  // ListOptsBuilder allows extensions to add additional parameters to the
    14  // List request.
    15  type ListOptsBuilder interface {
    16  	ToServerListQuery() (string, error)
    17  }
    18  
    19  // ListOpts allows the filtering and sorting of paginated collections through
    20  // the API. Filtering is achieved by passing in struct field values that map to
    21  // the server attributes you want to see returned. Marker and Limit are used
    22  // for pagination.
    23  type ListOpts struct {
    24  	// ChangesSince is a time/date stamp for when the server last changed status.
    25  	ChangesSince string `q:"changes-since"`
    26  
    27  	// Image is the name of the image in URL format.
    28  	Image string `q:"image"`
    29  
    30  	// Flavor is the name of the flavor in URL format.
    31  	Flavor string `q:"flavor"`
    32  
    33  	// Name of the server as a string; can be queried with regular expressions.
    34  	// Realize that ?name=bob returns both bob and bobb. If you need to match bob
    35  	// only, you can use a regular expression matching the syntax of the
    36  	// underlying database server implemented for Compute.
    37  	Name string `q:"name"`
    38  
    39  	// Status is the value of the status of the server so that you can filter on
    40  	// "ACTIVE" for example.
    41  	Status string `q:"status"`
    42  
    43  	// Host is the name of the host as a string.
    44  	Host string `q:"host"`
    45  
    46  	// Marker is a UUID of the server at which you want to set a marker.
    47  	Marker string `q:"marker"`
    48  
    49  	// Limit is an integer value for the limit of values to return.
    50  	Limit int `q:"limit"`
    51  
    52  	// AllTenants is a bool to show all tenants.
    53  	AllTenants bool `q:"all_tenants"`
    54  
    55  	// TenantID lists servers for a particular tenant.
    56  	// Setting "AllTenants = true" is required.
    57  	TenantID string `q:"tenant_id"`
    58  }
    59  
    60  // ToServerListQuery formats a ListOpts into a query string.
    61  func (opts ListOpts) ToServerListQuery() (string, error) {
    62  	q, err := golangsdk.BuildQueryString(opts)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  	return q.String(), err
    67  }
    68  
    69  // List makes a request against the API to list servers accessible to you.
    70  func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager {
    71  	url := listDetailURL(client)
    72  	if opts != nil {
    73  		query, err := opts.ToServerListQuery()
    74  		if err != nil {
    75  			return pagination.Pager{Err: err}
    76  		}
    77  		url += query
    78  	}
    79  	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
    80  		return ServerPage{pagination.LinkedPageBase{PageResult: r}}
    81  	})
    82  }
    83  
    84  // CreateOptsBuilder allows extensions to add additional parameters to the
    85  // Create request.
    86  type CreateOptsBuilder interface {
    87  	ToServerCreateMap() (map[string]interface{}, error)
    88  }
    89  
    90  // Network is used within CreateOpts to control a new server's network
    91  // attachments.
    92  type Network struct {
    93  	// UUID of a network to attach to the newly provisioned server.
    94  	// Required unless Port is provided.
    95  	UUID string
    96  
    97  	// Port of a neutron network to attach to the newly provisioned server.
    98  	// Required unless UUID is provided.
    99  	Port string
   100  
   101  	// FixedIP specifies a fixed IPv4 address to be used on this network.
   102  	FixedIP string
   103  }
   104  
   105  // Personality is an array of files that are injected into the server at launch.
   106  type Personality []*File
   107  
   108  // File is used within CreateOpts and RebuildOpts to inject a file into the
   109  // server at launch.
   110  // File implements the json.Marshaler interface, so when a Create or Rebuild
   111  // operation is requested, json.Marshal will call File's MarshalJSON method.
   112  type File struct {
   113  	// Path of the file.
   114  	Path string
   115  
   116  	// Contents of the file. Maximum content size is 255 bytes.
   117  	Contents []byte
   118  }
   119  
   120  // MarshalJSON marshals the escaped file, base64 encoding the contents.
   121  func (f *File) MarshalJSON() ([]byte, error) {
   122  	file := struct {
   123  		Path     string `json:"path"`
   124  		Contents string `json:"contents"`
   125  	}{
   126  		Path:     f.Path,
   127  		Contents: base64.StdEncoding.EncodeToString(f.Contents),
   128  	}
   129  	return json.Marshal(file)
   130  }
   131  
   132  // CreateOpts specifies server creation parameters.
   133  type CreateOpts struct {
   134  	// Name is the name to assign to the newly launched server.
   135  	Name string `json:"name" required:"true"`
   136  
   137  	// ImageRef [optional; required if ImageName is not provided] is the ID or
   138  	// full URL to the image that contains the server's OS and initial state.
   139  	// Also optional if using the boot-from-volume extension.
   140  	ImageRef string `json:"imageRef"`
   141  
   142  	// ImageName [optional; required if ImageRef is not provided] is the name of
   143  	// the image that contains the server's OS and initial state.
   144  	// Also optional if using the boot-from-volume extension.
   145  	ImageName string `json:"-"`
   146  
   147  	// FlavorRef [optional; required if FlavorName is not provided] is the ID or
   148  	// full URL to the flavor that describes the server's specs.
   149  	FlavorRef string `json:"flavorRef"`
   150  
   151  	// FlavorName [optional; required if FlavorRef is not provided] is the name of
   152  	// the flavor that describes the server's specs.
   153  	FlavorName string `json:"-"`
   154  
   155  	// SecurityGroups lists the names of the security groups to which this server
   156  	// should belong.
   157  	SecurityGroups []string `json:"-"`
   158  
   159  	// UserData contains configuration information or scripts to use upon launch.
   160  	// Create will base64-encode it for you, if it isn't already.
   161  	UserData []byte `json:"-"`
   162  
   163  	// AvailabilityZone in which to launch the server.
   164  	AvailabilityZone string `json:"availability_zone,omitempty"`
   165  
   166  	// Networks dictates how this server will be attached to available networks.
   167  	// By default, the server will be attached to all isolated networks for the
   168  	// tenant.
   169  	Networks []Network `json:"-"`
   170  
   171  	// Metadata contains key-value pairs (up to 255 bytes each) to attach to the
   172  	// server.
   173  	Metadata map[string]string `json:"metadata,omitempty"`
   174  
   175  	// Personality includes files to inject into the server at launch.
   176  	// Create will base64-encode file contents for you.
   177  	Personality Personality `json:"personality,omitempty"`
   178  
   179  	// ConfigDrive enables metadata injection through a configuration drive.
   180  	ConfigDrive *bool `json:"config_drive,omitempty"`
   181  
   182  	// AdminPass sets the root user password. If not set, a randomly-generated
   183  	// password will be created and returned in the response.
   184  	AdminPass string `json:"adminPass,omitempty"`
   185  
   186  	// AccessIPv4 specifies an IPv4 address for the instance.
   187  	AccessIPv4 string `json:"accessIPv4,omitempty"`
   188  
   189  	// AccessIPv6 pecifies an IPv6 address for the instance.
   190  	AccessIPv6 string `json:"accessIPv6,omitempty"`
   191  
   192  	// ServiceClient will allow calls to be made to retrieve an image or
   193  	// flavor ID by name.
   194  	ServiceClient *golangsdk.ServiceClient `json:"-"`
   195  }
   196  
   197  // ToServerCreateMap assembles a request body based on the contents of a
   198  // CreateOpts.
   199  func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
   200  	sc := opts.ServiceClient
   201  	opts.ServiceClient = nil
   202  	b, err := golangsdk.BuildRequestBody(opts, "")
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	if opts.UserData != nil {
   208  		var userData string
   209  		if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil {
   210  			userData = base64.StdEncoding.EncodeToString(opts.UserData)
   211  		} else {
   212  			userData = string(opts.UserData)
   213  		}
   214  		b["user_data"] = &userData
   215  	}
   216  
   217  	if len(opts.SecurityGroups) > 0 {
   218  		securityGroups := make([]map[string]interface{}, len(opts.SecurityGroups))
   219  		for i, groupName := range opts.SecurityGroups {
   220  			securityGroups[i] = map[string]interface{}{"name": groupName}
   221  		}
   222  		b["security_groups"] = securityGroups
   223  	}
   224  
   225  	if len(opts.Networks) > 0 {
   226  		networks := make([]map[string]interface{}, len(opts.Networks))
   227  		for i, net := range opts.Networks {
   228  			networks[i] = make(map[string]interface{})
   229  			if net.UUID != "" {
   230  				networks[i]["uuid"] = net.UUID
   231  			}
   232  			if net.Port != "" {
   233  				networks[i]["port"] = net.Port
   234  			}
   235  			if net.FixedIP != "" {
   236  				networks[i]["fixed_ip"] = net.FixedIP
   237  			}
   238  		}
   239  		b["networks"] = networks
   240  	}
   241  
   242  	// If ImageRef isn't provided, check if ImageName was provided to ascertain
   243  	// the image ID.
   244  	if opts.ImageRef == "" {
   245  		if opts.ImageName != "" {
   246  			if sc == nil {
   247  				err := ErrNoClientProvidedForIDByName{}
   248  				err.Argument = "ServiceClient"
   249  				return nil, err
   250  			}
   251  			image, err := images.ListImages(sc, images.ListImagesOpts{
   252  				Name: opts.ImageName,
   253  			})
   254  			if err != nil {
   255  				return nil, err
   256  			}
   257  			b["imageRef"] = image[0].Id
   258  		}
   259  	}
   260  
   261  	// If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID.
   262  	if opts.FlavorRef == "" {
   263  		if opts.FlavorName == "" {
   264  			err := ErrNeitherFlavorIDNorFlavorNameProvided{}
   265  			err.Argument = "FlavorRef/FlavorName"
   266  			return nil, err
   267  		}
   268  		if sc == nil {
   269  			err := ErrNoClientProvidedForIDByName{}
   270  			err.Argument = "ServiceClient"
   271  			return nil, err
   272  		}
   273  		flavorID, err := flavors.IDFromName(sc, opts.FlavorName)
   274  		if err != nil {
   275  			return nil, err
   276  		}
   277  		b["flavorRef"] = flavorID
   278  	}
   279  
   280  	return map[string]interface{}{"server": b}, nil
   281  }
   282  
   283  // Create requests a server to be provisioned to the user in the current tenant.
   284  func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
   285  	reqBody, err := opts.ToServerCreateMap()
   286  	if err != nil {
   287  		r.Err = err
   288  		return
   289  	}
   290  	_, r.Err = client.Post(listURL(client), reqBody, &r.Body, nil)
   291  	return
   292  }
   293  
   294  // Delete requests that a server previously provisioned be removed from your
   295  // account.
   296  func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) {
   297  	_, r.Err = client.Delete(deleteURL(client, id), nil)
   298  	return
   299  }
   300  
   301  // ForceDelete forces the deletion of a server.
   302  func ForceDelete(client *golangsdk.ServiceClient, id string) (r ActionResult) {
   303  	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil)
   304  	return
   305  }
   306  
   307  // Get requests details on a single server, by ID.
   308  func Get(client *golangsdk.ServiceClient, id string) (r GetResult) {
   309  	_, r.Err = client.Get(getURL(client, id), &r.Body, &golangsdk.RequestOpts{
   310  		OkCodes: []int{200, 203},
   311  	})
   312  	return
   313  }
   314  
   315  func GetNICs(client *golangsdk.ServiceClient, id string) (r GetNICResult) {
   316  	_, r.Err = client.Get(getNICManagementURL(client, id), &r.Body, &golangsdk.RequestOpts{
   317  		OkCodes: []int{200},
   318  	})
   319  	return
   320  }
   321  
   322  // UpdateOptsBuilder allows extensions to add additional attributes to the
   323  // Update request.
   324  type UpdateOptsBuilder interface {
   325  	ToServerUpdateMap() (map[string]interface{}, error)
   326  }
   327  
   328  // UpdateOpts specifies the base attributes that may be updated on an existing
   329  // server.
   330  type UpdateOpts struct {
   331  	// Name changes the displayed name of the server.
   332  	// The server host name will *not* change.
   333  	// Server names are not constrained to be unique, even within the same tenant.
   334  	Name string `json:"name,omitempty"`
   335  
   336  	// AccessIPv4 provides a new IPv4 address for the instance.
   337  	AccessIPv4 string `json:"accessIPv4,omitempty"`
   338  
   339  	// AccessIPv6 provides a new IPv6 address for the instance.
   340  	AccessIPv6 string `json:"accessIPv6,omitempty"`
   341  }
   342  
   343  // ToServerUpdateMap formats an UpdateOpts structure into a request body.
   344  func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) {
   345  	return golangsdk.BuildRequestBody(opts, "server")
   346  }
   347  
   348  // Update requests that various attributes of the indicated server be changed.
   349  func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
   350  	b, err := opts.ToServerUpdateMap()
   351  	if err != nil {
   352  		r.Err = err
   353  		return
   354  	}
   355  	_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{
   356  		OkCodes: []int{200},
   357  	})
   358  	return
   359  }
   360  
   361  // ChangeAdminPassword alters the administrator or root password for a specified
   362  // server.
   363  func ChangeAdminPassword(client *golangsdk.ServiceClient, id, newPassword string) (r ActionResult) {
   364  	b := map[string]interface{}{
   365  		"changePassword": map[string]string{
   366  			"adminPass": newPassword,
   367  		},
   368  	}
   369  	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
   370  	return
   371  }
   372  
   373  // RebootMethod describes the mechanisms by which a server reboot can be requested.
   374  type RebootMethod string
   375  
   376  // These constants determine how a server should be rebooted.
   377  // See the Reboot() function for further details.
   378  const (
   379  	SoftReboot RebootMethod = "SOFT"
   380  	HardReboot RebootMethod = "HARD"
   381  	OSReboot                = SoftReboot
   382  	PowerCycle              = HardReboot
   383  )
   384  
   385  // RebootOptsBuilder allows extensions to add additional parameters to the
   386  // reboot request.
   387  type RebootOptsBuilder interface {
   388  	ToServerRebootMap() (map[string]interface{}, error)
   389  }
   390  
   391  // RebootOpts provides options to the reboot request.
   392  type RebootOpts struct {
   393  	// Type is the type of reboot to perform on the server.
   394  	Type RebootMethod `json:"type" required:"true"`
   395  }
   396  
   397  // ToServerRebootMap builds a body for the reboot request.
   398  func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) {
   399  	return golangsdk.BuildRequestBody(opts, "reboot")
   400  }
   401  
   402  /*
   403  Reboot requests that a given server reboot.
   404  
   405  Two methods exist for rebooting a server:
   406  
   407  HardReboot (aka PowerCycle) starts the server instance by physically cutting
   408  power to the machine, or if a VM, terminating it at the hypervisor level.
   409  It's done. Caput. Full stop.
   410  Then, after a brief while, power is rtored or the VM instance restarted.
   411  
   412  SoftReboot (aka OSReboot) simply tells the OS to restart under its own
   413  procedure.
   414  E.g., in Linux, asking it to enter runlevel 6, or executing
   415  "sudo shutdown -r now", or by asking Windows to rtart the machine.
   416  */
   417  func Reboot(client *golangsdk.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) {
   418  	b, err := opts.ToServerRebootMap()
   419  	if err != nil {
   420  		r.Err = err
   421  		return
   422  	}
   423  	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
   424  	return
   425  }
   426  
   427  // RebuildOptsBuilder allows extensions to provide additional parameters to the
   428  // rebuild request.
   429  type RebuildOptsBuilder interface {
   430  	ToServerRebuildMap() (map[string]interface{}, error)
   431  }
   432  
   433  // RebuildOpts represents the configuration options used in a server rebuild
   434  // operation.
   435  type RebuildOpts struct {
   436  	// AdminPass is the server's admin password
   437  	AdminPass string `json:"adminPass,omitempty"`
   438  
   439  	// ImageID is the ID of the image you want your server to be provisioned on.
   440  	ImageID string `json:"imageRef"`
   441  
   442  	// ImageName is readable name of an image.
   443  	ImageName string `json:"-"`
   444  
   445  	// Name to set the server to
   446  	Name string `json:"name,omitempty"`
   447  
   448  	// AccessIPv4 [optional] provides a new IPv4 address for the instance.
   449  	AccessIPv4 string `json:"accessIPv4,omitempty"`
   450  
   451  	// AccessIPv6 [optional] provides a new IPv6 address for the instance.
   452  	AccessIPv6 string `json:"accessIPv6,omitempty"`
   453  
   454  	// Metadata [optional] contains key-value pairs (up to 255 bytes each)
   455  	// to attach to the server.
   456  	Metadata map[string]string `json:"metadata,omitempty"`
   457  
   458  	// Personality [optional] includes files to inject into the server at launch.
   459  	// Rebuild will base64-encode file contents for you.
   460  	Personality Personality `json:"personality,omitempty"`
   461  
   462  	// ServiceClient will allow calls to be made to retrieve an image or
   463  	// flavor ID by name.
   464  	ServiceClient *golangsdk.ServiceClient `json:"-"`
   465  }
   466  
   467  // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
   468  func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
   469  	b, err := golangsdk.BuildRequestBody(opts, "")
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  
   474  	// If ImageRef isn't provided, check if ImageName was provided to ascertain
   475  	// the image ID.
   476  	if opts.ImageID == "" {
   477  		if opts.ImageName != "" {
   478  			if opts.ServiceClient == nil {
   479  				err := ErrNoClientProvidedForIDByName{}
   480  				err.Argument = "ServiceClient"
   481  				return nil, err
   482  			}
   483  			image, err := images.ListImages(opts.ServiceClient, images.ListImagesOpts{
   484  				Name: opts.ImageName,
   485  			})
   486  			if err != nil {
   487  				return nil, err
   488  			}
   489  			b["imageRef"] = image[0].Id
   490  		}
   491  	}
   492  
   493  	return map[string]interface{}{"rebuild": b}, nil
   494  }
   495  
   496  // Rebuild will reprovision the server according to the configuration options
   497  // provided in the RebuildOpts struct.
   498  func Rebuild(client *golangsdk.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) {
   499  	b, err := opts.ToServerRebuildMap()
   500  	if err != nil {
   501  		r.Err = err
   502  		return
   503  	}
   504  	_, r.Err = client.Post(actionURL(client, id), b, &r.Body, nil)
   505  	return
   506  }
   507  
   508  // ResizeOptsBuilder allows extensions to add additional parameters to the
   509  // resize request.
   510  type ResizeOptsBuilder interface {
   511  	ToServerResizeMap() (map[string]interface{}, error)
   512  }
   513  
   514  // ResizeOpts represents the configuration options used to control a Resize
   515  // operation.
   516  type ResizeOpts struct {
   517  	// FlavorRef is the ID of the flavor you wish your server to become.
   518  	FlavorRef string `json:"flavorRef" required:"true"`
   519  }
   520  
   521  // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON
   522  // request body for the Resize request.
   523  func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) {
   524  	return golangsdk.BuildRequestBody(opts, "resize")
   525  }
   526  
   527  // Resize instructs the provider to change the flavor of the server.
   528  //
   529  // Note that this implies rebuilding it.
   530  //
   531  // Unfortunately, one cannot pass rebuild parameters to the resize function.
   532  // When the resize completes, the server will be in VERIFY_RESIZE state.
   533  // While in this state, you can explore the use of the new server's
   534  // configuration. If you like it, call ConfirmResize() to commit the resize
   535  // permanently. Otherwise, call RevertResize() to restore the old configuration.
   536  func Resize(client *golangsdk.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) {
   537  	b, err := opts.ToServerResizeMap()
   538  	if err != nil {
   539  		r.Err = err
   540  		return
   541  	}
   542  	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
   543  	return
   544  }
   545  
   546  // ConfirmResize confirms a previous resize operation on a server.
   547  // See Resize() for more details.
   548  func ConfirmResize(client *golangsdk.ServiceClient, id string) (r ActionResult) {
   549  	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &golangsdk.RequestOpts{
   550  		OkCodes: []int{201, 202, 204},
   551  	})
   552  	return
   553  }
   554  
   555  // RevertResize cancels a previous resize operation on a server.
   556  // See Resize() for more details.
   557  func RevertResize(client *golangsdk.ServiceClient, id string) (r ActionResult) {
   558  	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil)
   559  	return
   560  }
   561  
   562  // ResetMetadataOptsBuilder allows extensions to add additional parameters to
   563  // the Reset request.
   564  type ResetMetadataOptsBuilder interface {
   565  	ToMetadataResetMap() (map[string]interface{}, error)
   566  }
   567  
   568  // MetadataOpts is a map that contains key-value pairs.
   569  type MetadataOpts map[string]string
   570  
   571  // ToMetadataResetMap assembles a body for a Reset request based on the contents
   572  // of a MetadataOpts.
   573  func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) {
   574  	return map[string]interface{}{"metadata": opts}, nil
   575  }
   576  
   577  // ToMetadataUpdateMap assembles a body for an Update request based on the
   578  // contents of a MetadataOpts.
   579  func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) {
   580  	return map[string]interface{}{"metadata": opts}, nil
   581  }
   582  
   583  // ResetMetadata will create multiple new key-value pairs for the given server
   584  // ID.
   585  // Note: Using this operation will erase any already-existing metadata and
   586  // create the new metadata provided. To keep any already-existing metadata,
   587  // use the UpdateMetadatas or UpdateMetadata function.
   588  func ResetMetadata(client *golangsdk.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) {
   589  	b, err := opts.ToMetadataResetMap()
   590  	if err != nil {
   591  		r.Err = err
   592  		return
   593  	}
   594  	_, r.Err = client.Put(metadataURL(client, id), b, &r.Body, &golangsdk.RequestOpts{
   595  		OkCodes: []int{200},
   596  	})
   597  	return
   598  }
   599  
   600  // Metadata requests all the metadata for the given server ID.
   601  func Metadata(client *golangsdk.ServiceClient, id string) (r GetMetadataResult) {
   602  	_, r.Err = client.Get(metadataURL(client, id), &r.Body, nil)
   603  	return
   604  }
   605  
   606  // UpdateMetadataOptsBuilder allows extensions to add additional parameters to
   607  // the Create request.
   608  type UpdateMetadataOptsBuilder interface {
   609  	ToMetadataUpdateMap() (map[string]interface{}, error)
   610  }
   611  
   612  // UpdateMetadata updates (or creates) all the metadata specified by opts for
   613  // the given server ID. This operation does not affect already-existing metadata
   614  // that is not specified by opts.
   615  func UpdateMetadata(client *golangsdk.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) {
   616  	b, err := opts.ToMetadataUpdateMap()
   617  	if err != nil {
   618  		r.Err = err
   619  		return
   620  	}
   621  	_, r.Err = client.Post(metadataURL(client, id), b, &r.Body, &golangsdk.RequestOpts{
   622  		OkCodes: []int{200},
   623  	})
   624  	return
   625  }
   626  
   627  // MetadatumOptsBuilder allows extensions to add additional parameters to the
   628  // Create request.
   629  type MetadatumOptsBuilder interface {
   630  	ToMetadatumCreateMap() (map[string]interface{}, string, error)
   631  }
   632  
   633  // MetadatumOpts is a map of length one that contains a key-value pair.
   634  type MetadatumOpts map[string]string
   635  
   636  // ToMetadatumCreateMap assembles a body for a Create request based on the
   637  // contents of a MetadataumOpts.
   638  func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) {
   639  	if len(opts) != 1 {
   640  		err := golangsdk.ErrInvalidInput{}
   641  		err.Argument = "servers.MetadatumOpts"
   642  		err.Info = "Must have 1 and only 1 key-value pair"
   643  		return nil, "", err
   644  	}
   645  	metadatum := map[string]interface{}{"meta": opts}
   646  	var key string
   647  	for k := range metadatum["meta"].(MetadatumOpts) {
   648  		key = k
   649  	}
   650  	return metadatum, key, nil
   651  }
   652  
   653  // CreateMetadatum will create or update the key-value pair with the given key
   654  // for the given server ID.
   655  func CreateMetadatum(client *golangsdk.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) {
   656  	b, key, err := opts.ToMetadatumCreateMap()
   657  	if err != nil {
   658  		r.Err = err
   659  		return
   660  	}
   661  	_, r.Err = client.Put(metadatumURL(client, id, key), b, &r.Body, &golangsdk.RequestOpts{
   662  		OkCodes: []int{200},
   663  	})
   664  	return
   665  }
   666  
   667  // Metadatum requests the key-value pair with the given key for the given
   668  // server ID.
   669  func Metadatum(client *golangsdk.ServiceClient, id, key string) (r GetMetadatumResult) {
   670  	_, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil)
   671  	return
   672  }
   673  
   674  // DeleteMetadatum will delete the key-value pair with the given key for the
   675  // given server ID.
   676  func DeleteMetadatum(client *golangsdk.ServiceClient, id, key string) (r DeleteMetadatumResult) {
   677  	_, r.Err = client.Delete(metadatumURL(client, id, key), nil)
   678  	return
   679  }
   680  
   681  // ListAddresses makes a request against the API to list the servers IP
   682  // addresses.
   683  func ListAddresses(client *golangsdk.ServiceClient, id string) pagination.Pager {
   684  	return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page {
   685  		return AddressPage{pagination.SinglePageBase(r)}
   686  	})
   687  }
   688  
   689  // ListAddressesByNetwork makes a request against the API to list the servers IP
   690  // addresses for the given network.
   691  func ListAddressesByNetwork(client *golangsdk.ServiceClient, id, network string) pagination.Pager {
   692  	return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page {
   693  		return NetworkAddressPage{pagination.SinglePageBase(r)}
   694  	})
   695  }
   696  
   697  // CreateImageOptsBuilder allows extensions to add additional parameters to the
   698  // CreateImage request.
   699  type CreateImageOptsBuilder interface {
   700  	ToServerCreateImageMap() (map[string]interface{}, error)
   701  }
   702  
   703  // CreateImageOpts provides options to pass to the CreateImage request.
   704  type CreateImageOpts struct {
   705  	// Name of the image/snapshot.
   706  	Name string `json:"name" required:"true"`
   707  
   708  	// Metadata contains key-value pairs (up to 255 bytes each) to attach to
   709  	// the created image.
   710  	Metadata map[string]string `json:"metadata,omitempty"`
   711  }
   712  
   713  // ToServerCreateImageMap formats a CreateImageOpts structure into a request
   714  // body.
   715  func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) {
   716  	return golangsdk.BuildRequestBody(opts, "createImage")
   717  }
   718  
   719  // CreateImage makes a request against the nova API to schedule an image to be
   720  // created of the server
   721  func CreateImage(client *golangsdk.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) {
   722  	b, err := opts.ToServerCreateImageMap()
   723  	if err != nil {
   724  		r.Err = err
   725  		return
   726  	}
   727  	resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{
   728  		OkCodes: []int{202},
   729  	})
   730  	if err != nil {
   731  		r.Err = err
   732  		return
   733  	}
   734  	r.Header = resp.Header
   735  	return
   736  }
   737  
   738  // IDFromName is a convenience function that returns a server's ID given its name.
   739  func IDFromName(client *golangsdk.ServiceClient, name string) (string, error) {
   740  	count := 0
   741  	id := ""
   742  
   743  	listOpts := ListOpts{
   744  		Name: name,
   745  	}
   746  
   747  	allPages, err := List(client, listOpts).AllPages()
   748  	if err != nil {
   749  		return "", err
   750  	}
   751  
   752  	all, err := ExtractServers(allPages)
   753  	if err != nil {
   754  		return "", err
   755  	}
   756  
   757  	for _, f := range all {
   758  		if f.Name == name {
   759  			count++
   760  			id = f.ID
   761  		}
   762  	}
   763  
   764  	switch count {
   765  	case 0:
   766  		return "", golangsdk.ErrResourceNotFound{Name: name, ResourceType: "server"}
   767  	case 1:
   768  		return id, nil
   769  	default:
   770  		return "", golangsdk.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "server"}
   771  	}
   772  }
   773  
   774  // GetPassword makes a request against the nova API to get the encrypted
   775  // administrative password.
   776  func GetPassword(client *golangsdk.ServiceClient, serverId string) (r GetPasswordResult) {
   777  	_, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil)
   778  	return
   779  }
   780  
   781  // ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be
   782  // used as ShowConsoleOutput options
   783  type ShowConsoleOutputOptsBuilder interface {
   784  	ToServerShowConsoleOutputMap() (map[string]interface{}, error)
   785  }
   786  
   787  // ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder
   788  type ShowConsoleOutputOpts struct {
   789  	// The number of lines to fetch from the end of console log.
   790  	// All lines will be returned if this is not specified.
   791  	Length int `json:"length,omitempty"`
   792  }
   793  
   794  // ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body.
   795  func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]interface{}, error) {
   796  	return golangsdk.BuildRequestBody(opts, "os-getConsoleOutput")
   797  }
   798  
   799  // ShowConsoleOutput makes a request against the nova API to get console log from the server
   800  func ShowConsoleOutput(client *golangsdk.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) {
   801  	b, err := opts.ToServerShowConsoleOutputMap()
   802  	if err != nil {
   803  		r.Err = err
   804  		return
   805  	}
   806  	_, r.Err = client.Post(actionURL(client, id), b, &r.Body, &golangsdk.RequestOpts{
   807  		OkCodes: []int{200},
   808  	})
   809  	return
   810  }