github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/openstack/blockstorage/v2/volumes/requests.go (about)

     1  package volumes
     2  
     3  import (
     4  	"context"
     5  	"maps"
     6  	"regexp"
     7  
     8  	"github.com/vnpaycloud-console/gophercloud/v2"
     9  	"github.com/vnpaycloud-console/gophercloud/v2/pagination"
    10  )
    11  
    12  // SchedulerHintOptsBuilder builds the scheduler hints into a serializable format.
    13  type SchedulerHintOptsBuilder interface {
    14  	ToSchedulerHintsMap() (map[string]any, error)
    15  }
    16  
    17  // SchedulerHintOpts contains options for providing scheduler hints
    18  // when creating a Volume. This object is passed to the volumes.Create function.
    19  // For more information about these parameters, see the Volume object.
    20  type SchedulerHintOpts struct {
    21  	// DifferentHost will place the volume on a different back-end that does not
    22  	// host the given volumes.
    23  	DifferentHost []string
    24  
    25  	// SameHost will place the volume on a back-end that hosts the given volumes.
    26  	SameHost []string
    27  
    28  	// LocalToInstance will place volume on same host on a given instance
    29  	LocalToInstance string
    30  
    31  	// Query is a conditional statement that results in back-ends able to
    32  	// host the volume.
    33  	Query string
    34  
    35  	// AdditionalProperies are arbitrary key/values that are not validated by nova.
    36  	AdditionalProperties map[string]any
    37  }
    38  
    39  // ToSchedulerHintsMap assembles a request body for scheduler hints
    40  func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]any, error) {
    41  	sh := make(map[string]any)
    42  
    43  	uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
    44  
    45  	if len(opts.DifferentHost) > 0 {
    46  		for _, diffHost := range opts.DifferentHost {
    47  			if !uuidRegex.MatchString(diffHost) {
    48  				err := gophercloud.ErrInvalidInput{}
    49  				err.Argument = "volumes.SchedulerHintOpts.DifferentHost"
    50  				err.Value = opts.DifferentHost
    51  				err.Info = "The hosts must be in UUID format."
    52  				return nil, err
    53  			}
    54  		}
    55  		sh["different_host"] = opts.DifferentHost
    56  	}
    57  
    58  	if len(opts.SameHost) > 0 {
    59  		for _, sameHost := range opts.SameHost {
    60  			if !uuidRegex.MatchString(sameHost) {
    61  				err := gophercloud.ErrInvalidInput{}
    62  				err.Argument = "volumes.SchedulerHintOpts.SameHost"
    63  				err.Value = opts.SameHost
    64  				err.Info = "The hosts must be in UUID format."
    65  				return nil, err
    66  			}
    67  		}
    68  		sh["same_host"] = opts.SameHost
    69  	}
    70  
    71  	if opts.LocalToInstance != "" {
    72  		if !uuidRegex.MatchString(opts.LocalToInstance) {
    73  			err := gophercloud.ErrInvalidInput{}
    74  			err.Argument = "volumes.SchedulerHintOpts.LocalToInstance"
    75  			err.Value = opts.LocalToInstance
    76  			err.Info = "The instance must be in UUID format."
    77  			return nil, err
    78  		}
    79  		sh["local_to_instance"] = opts.LocalToInstance
    80  	}
    81  
    82  	if opts.Query != "" {
    83  		sh["query"] = opts.Query
    84  	}
    85  
    86  	if opts.AdditionalProperties != nil {
    87  		for k, v := range opts.AdditionalProperties {
    88  			sh[k] = v
    89  		}
    90  	}
    91  
    92  	if len(sh) == 0 {
    93  		return sh, nil
    94  	}
    95  
    96  	return map[string]any{"OS-SCH-HNT:scheduler_hints": sh}, nil
    97  }
    98  
    99  // CreateOptsBuilder allows extensions to add additional parameters to the
   100  // Create request.
   101  type CreateOptsBuilder interface {
   102  	ToVolumeCreateMap() (map[string]any, error)
   103  }
   104  
   105  // CreateOpts contains options for creating a Volume. This object is passed to
   106  // the volumes.Create function. For more information about these parameters,
   107  // see the Volume object.
   108  type CreateOpts struct {
   109  	// The size of the volume, in GB
   110  	Size int `json:"size" required:"true"`
   111  	// The availability zone
   112  	AvailabilityZone string `json:"availability_zone,omitempty"`
   113  	// ConsistencyGroupID is the ID of a consistency group
   114  	ConsistencyGroupID string `json:"consistencygroup_id,omitempty"`
   115  	// The volume description
   116  	Description string `json:"description,omitempty"`
   117  	// One or more metadata key and value pairs to associate with the volume
   118  	Metadata map[string]string `json:"metadata,omitempty"`
   119  	// The volume name
   120  	Name string `json:"name,omitempty"`
   121  	// the ID of the existing volume snapshot
   122  	SnapshotID string `json:"snapshot_id,omitempty"`
   123  	// SourceReplica is a UUID of an existing volume to replicate with
   124  	SourceReplica string `json:"source_replica,omitempty"`
   125  	// the ID of the existing volume
   126  	SourceVolID string `json:"source_volid,omitempty"`
   127  	// The ID of the image from which you want to create the volume.
   128  	// Required to create a bootable volume.
   129  	ImageID string `json:"imageRef,omitempty"`
   130  	// The associated volume type
   131  	VolumeType string `json:"volume_type,omitempty"`
   132  }
   133  
   134  // ToVolumeCreateMap assembles a request body based on the contents of a
   135  // CreateOpts.
   136  func (opts CreateOpts) ToVolumeCreateMap() (map[string]any, error) {
   137  	return gophercloud.BuildRequestBody(opts, "volume")
   138  }
   139  
   140  // Create will create a new Volume based on the values in CreateOpts. To extract
   141  // the Volume object from the response, call the Extract method on the
   142  // CreateResult.
   143  func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder, hintOpts SchedulerHintOptsBuilder) (r CreateResult) {
   144  	b, err := opts.ToVolumeCreateMap()
   145  	if err != nil {
   146  		r.Err = err
   147  		return
   148  	}
   149  
   150  	if hintOpts != nil {
   151  		sh, err := hintOpts.ToSchedulerHintsMap()
   152  		if err != nil {
   153  			r.Err = err
   154  			return
   155  		}
   156  		maps.Copy(b, sh)
   157  	}
   158  
   159  	resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{
   160  		OkCodes: []int{202},
   161  	})
   162  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   163  	return
   164  }
   165  
   166  // DeleteOptsBuilder allows extensions to add additional parameters to the
   167  // Delete request.
   168  type DeleteOptsBuilder interface {
   169  	ToVolumeDeleteQuery() (string, error)
   170  }
   171  
   172  // DeleteOpts contains options for deleting a Volume. This object is passed to
   173  // the volumes.Delete function.
   174  type DeleteOpts struct {
   175  	// Delete all snapshots of this volume as well.
   176  	Cascade bool `q:"cascade"`
   177  }
   178  
   179  // ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string.
   180  func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) {
   181  	q, err := gophercloud.BuildQueryString(opts)
   182  	return q.String(), err
   183  }
   184  
   185  // Delete will delete the existing Volume with the provided ID.
   186  func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) {
   187  	url := deleteURL(client, id)
   188  	if opts != nil {
   189  		query, err := opts.ToVolumeDeleteQuery()
   190  		if err != nil {
   191  			r.Err = err
   192  			return
   193  		}
   194  		url += query
   195  	}
   196  	resp, err := client.Delete(ctx, url, nil)
   197  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   198  	return
   199  }
   200  
   201  // Get retrieves the Volume with the provided ID. To extract the Volume object
   202  // from the response, call the Extract method on the GetResult.
   203  func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) {
   204  	resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil)
   205  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   206  	return
   207  }
   208  
   209  // ListOptsBuilder allows extensions to add additional parameters to the List
   210  // request.
   211  type ListOptsBuilder interface {
   212  	ToVolumeListQuery() (string, error)
   213  }
   214  
   215  // ListOpts holds options for listing Volumes. It is passed to the volumes.List
   216  // function.
   217  type ListOpts struct {
   218  	// AllTenants will retrieve volumes of all tenants/projects.
   219  	AllTenants bool `q:"all_tenants"`
   220  
   221  	// Metadata will filter results based on specified metadata.
   222  	Metadata map[string]string `q:"metadata"`
   223  
   224  	// Name will filter by the specified volume name.
   225  	Name string `q:"name"`
   226  
   227  	// Status will filter by the specified status.
   228  	Status string `q:"status"`
   229  
   230  	// TenantID will filter by a specific tenant/project ID.
   231  	// Setting AllTenants is required for this.
   232  	TenantID string `q:"project_id"`
   233  
   234  	// Comma-separated list of sort keys and optional sort directions in the
   235  	// form of <key>[:<direction>].
   236  	Sort string `q:"sort"`
   237  
   238  	// Requests a page size of items.
   239  	Limit int `q:"limit"`
   240  
   241  	// Used in conjunction with limit to return a slice of items.
   242  	Offset int `q:"offset"`
   243  
   244  	// The ID of the last-seen item.
   245  	Marker string `q:"marker"`
   246  }
   247  
   248  // ToVolumeListQuery formats a ListOpts into a query string.
   249  func (opts ListOpts) ToVolumeListQuery() (string, error) {
   250  	q, err := gophercloud.BuildQueryString(opts)
   251  	return q.String(), err
   252  }
   253  
   254  // List returns Volumes optionally limited by the conditions provided in ListOpts.
   255  func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
   256  	url := listURL(client)
   257  	if opts != nil {
   258  		query, err := opts.ToVolumeListQuery()
   259  		if err != nil {
   260  			return pagination.Pager{Err: err}
   261  		}
   262  		url += query
   263  	}
   264  
   265  	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
   266  		return VolumePage{pagination.LinkedPageBase{PageResult: r}}
   267  	})
   268  }
   269  
   270  // UpdateOptsBuilder allows extensions to add additional parameters to the
   271  // Update request.
   272  type UpdateOptsBuilder interface {
   273  	ToVolumeUpdateMap() (map[string]any, error)
   274  }
   275  
   276  // UpdateOpts contain options for updating an existing Volume. This object is passed
   277  // to the volumes.Update function. For more information about the parameters, see
   278  // the Volume object.
   279  type UpdateOpts struct {
   280  	Name        *string           `json:"name,omitempty"`
   281  	Description *string           `json:"description,omitempty"`
   282  	Metadata    map[string]string `json:"metadata,omitempty"`
   283  }
   284  
   285  // ToVolumeUpdateMap assembles a request body based on the contents of an
   286  // UpdateOpts.
   287  func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]any, error) {
   288  	return gophercloud.BuildRequestBody(opts, "volume")
   289  }
   290  
   291  // Update will update the Volume with provided information. To extract the updated
   292  // Volume from the response, call the Extract method on the UpdateResult.
   293  func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
   294  	b, err := opts.ToVolumeUpdateMap()
   295  	if err != nil {
   296  		r.Err = err
   297  		return
   298  	}
   299  	resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
   300  		OkCodes: []int{200},
   301  	})
   302  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   303  	return
   304  }
   305  
   306  // AttachOptsBuilder allows extensions to add additional parameters to the
   307  // Attach request.
   308  type AttachOptsBuilder interface {
   309  	ToVolumeAttachMap() (map[string]any, error)
   310  }
   311  
   312  // AttachMode describes the attachment mode for volumes.
   313  type AttachMode string
   314  
   315  // These constants determine how a volume is attached.
   316  const (
   317  	ReadOnly  AttachMode = "ro"
   318  	ReadWrite AttachMode = "rw"
   319  )
   320  
   321  // AttachOpts contains options for attaching a Volume.
   322  type AttachOpts struct {
   323  	// The mountpoint of this volume.
   324  	MountPoint string `json:"mountpoint,omitempty"`
   325  
   326  	// The nova instance ID, can't set simultaneously with HostName.
   327  	InstanceUUID string `json:"instance_uuid,omitempty"`
   328  
   329  	// The hostname of baremetal host, can't set simultaneously with InstanceUUID.
   330  	HostName string `json:"host_name,omitempty"`
   331  
   332  	// Mount mode of this volume.
   333  	Mode AttachMode `json:"mode,omitempty"`
   334  }
   335  
   336  // ToVolumeAttachMap assembles a request body based on the contents of a
   337  // AttachOpts.
   338  func (opts AttachOpts) ToVolumeAttachMap() (map[string]any, error) {
   339  	return gophercloud.BuildRequestBody(opts, "os-attach")
   340  }
   341  
   342  // Attach will attach a volume based on the values in AttachOpts.
   343  func Attach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) {
   344  	b, err := opts.ToVolumeAttachMap()
   345  	if err != nil {
   346  		r.Err = err
   347  		return
   348  	}
   349  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   350  		OkCodes: []int{202},
   351  	})
   352  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   353  	return
   354  }
   355  
   356  // BeginDetaching will mark the volume as detaching.
   357  func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) {
   358  	b := map[string]any{"os-begin_detaching": make(map[string]any)}
   359  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   360  		OkCodes: []int{202},
   361  	})
   362  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   363  	return
   364  }
   365  
   366  // DetachOptsBuilder allows extensions to add additional parameters to the
   367  // Detach request.
   368  type DetachOptsBuilder interface {
   369  	ToVolumeDetachMap() (map[string]any, error)
   370  }
   371  
   372  // DetachOpts contains options for detaching a Volume.
   373  type DetachOpts struct {
   374  	// AttachmentID is the ID of the attachment between a volume and instance.
   375  	AttachmentID string `json:"attachment_id,omitempty"`
   376  }
   377  
   378  // ToVolumeDetachMap assembles a request body based on the contents of a
   379  // DetachOpts.
   380  func (opts DetachOpts) ToVolumeDetachMap() (map[string]any, error) {
   381  	return gophercloud.BuildRequestBody(opts, "os-detach")
   382  }
   383  
   384  // Detach will detach a volume based on volume ID.
   385  func Detach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) {
   386  	b, err := opts.ToVolumeDetachMap()
   387  	if err != nil {
   388  		r.Err = err
   389  		return
   390  	}
   391  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   392  		OkCodes: []int{202},
   393  	})
   394  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   395  	return
   396  }
   397  
   398  // Reserve will reserve a volume based on volume ID.
   399  func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ReserveResult) {
   400  	b := map[string]any{"os-reserve": make(map[string]any)}
   401  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   402  		OkCodes: []int{200, 201, 202},
   403  	})
   404  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   405  	return
   406  }
   407  
   408  // Unreserve will unreserve a volume based on volume ID.
   409  func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnreserveResult) {
   410  	b := map[string]any{"os-unreserve": make(map[string]any)}
   411  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   412  		OkCodes: []int{200, 201, 202},
   413  	})
   414  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   415  	return
   416  }
   417  
   418  // InitializeConnectionOptsBuilder allows extensions to add additional parameters to the
   419  // InitializeConnection request.
   420  type InitializeConnectionOptsBuilder interface {
   421  	ToVolumeInitializeConnectionMap() (map[string]any, error)
   422  }
   423  
   424  // InitializeConnectionOpts hosts options for InitializeConnection.
   425  // The fields are specific to the storage driver in use and the destination
   426  // attachment.
   427  type InitializeConnectionOpts struct {
   428  	IP        string   `json:"ip,omitempty"`
   429  	Host      string   `json:"host,omitempty"`
   430  	Initiator string   `json:"initiator,omitempty"`
   431  	Wwpns     []string `json:"wwpns,omitempty"`
   432  	Wwnns     string   `json:"wwnns,omitempty"`
   433  	Multipath *bool    `json:"multipath,omitempty"`
   434  	Platform  string   `json:"platform,omitempty"`
   435  	OSType    string   `json:"os_type,omitempty"`
   436  }
   437  
   438  // ToVolumeInitializeConnectionMap assembles a request body based on the contents of a
   439  // InitializeConnectionOpts.
   440  func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]any, error) {
   441  	b, err := gophercloud.BuildRequestBody(opts, "connector")
   442  	return map[string]any{"os-initialize_connection": b}, err
   443  }
   444  
   445  // InitializeConnection initializes an iSCSI connection by volume ID.
   446  func InitializeConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) {
   447  	b, err := opts.ToVolumeInitializeConnectionMap()
   448  	if err != nil {
   449  		r.Err = err
   450  		return
   451  	}
   452  	resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
   453  		OkCodes: []int{200, 201, 202},
   454  	})
   455  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   456  	return
   457  }
   458  
   459  // TerminateConnectionOptsBuilder allows extensions to add additional parameters to the
   460  // TerminateConnection request.
   461  type TerminateConnectionOptsBuilder interface {
   462  	ToVolumeTerminateConnectionMap() (map[string]any, error)
   463  }
   464  
   465  // TerminateConnectionOpts hosts options for TerminateConnection.
   466  type TerminateConnectionOpts struct {
   467  	IP        string   `json:"ip,omitempty"`
   468  	Host      string   `json:"host,omitempty"`
   469  	Initiator string   `json:"initiator,omitempty"`
   470  	Wwpns     []string `json:"wwpns,omitempty"`
   471  	Wwnns     string   `json:"wwnns,omitempty"`
   472  	Multipath *bool    `json:"multipath,omitempty"`
   473  	Platform  string   `json:"platform,omitempty"`
   474  	OSType    string   `json:"os_type,omitempty"`
   475  }
   476  
   477  // ToVolumeTerminateConnectionMap assembles a request body based on the contents of a
   478  // TerminateConnectionOpts.
   479  func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]any, error) {
   480  	b, err := gophercloud.BuildRequestBody(opts, "connector")
   481  	return map[string]any{"os-terminate_connection": b}, err
   482  }
   483  
   484  // TerminateConnection terminates an iSCSI connection by volume ID.
   485  func TerminateConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) {
   486  	b, err := opts.ToVolumeTerminateConnectionMap()
   487  	if err != nil {
   488  		r.Err = err
   489  		return
   490  	}
   491  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   492  		OkCodes: []int{202},
   493  	})
   494  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   495  	return
   496  }
   497  
   498  // ExtendSizeOptsBuilder allows extensions to add additional parameters to the
   499  // ExtendSize request.
   500  type ExtendSizeOptsBuilder interface {
   501  	ToVolumeExtendSizeMap() (map[string]any, error)
   502  }
   503  
   504  // ExtendSizeOpts contains options for extending the size of an existing Volume.
   505  // This object is passed to the volumes.ExtendSize function.
   506  type ExtendSizeOpts struct {
   507  	// NewSize is the new size of the volume, in GB.
   508  	NewSize int `json:"new_size" required:"true"`
   509  }
   510  
   511  // ToVolumeExtendSizeMap assembles a request body based on the contents of an
   512  // ExtendSizeOpts.
   513  func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]any, error) {
   514  	return gophercloud.BuildRequestBody(opts, "os-extend")
   515  }
   516  
   517  // ExtendSize will extend the size of the volume based on the provided information.
   518  // This operation does not return a response body.
   519  func ExtendSize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) {
   520  	b, err := opts.ToVolumeExtendSizeMap()
   521  	if err != nil {
   522  		r.Err = err
   523  		return
   524  	}
   525  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   526  		OkCodes: []int{202},
   527  	})
   528  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   529  	return
   530  }
   531  
   532  // UploadImageOptsBuilder allows extensions to add additional parameters to the
   533  // UploadImage request.
   534  type UploadImageOptsBuilder interface {
   535  	ToVolumeUploadImageMap() (map[string]any, error)
   536  }
   537  
   538  // UploadImageOpts contains options for uploading a Volume to image storage.
   539  type UploadImageOpts struct {
   540  	// Container format, may be bare, ofv, ova, etc.
   541  	ContainerFormat string `json:"container_format,omitempty"`
   542  
   543  	// Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc.
   544  	DiskFormat string `json:"disk_format,omitempty"`
   545  
   546  	// The name of image that will be stored in glance.
   547  	ImageName string `json:"image_name,omitempty"`
   548  
   549  	// Force image creation, usable if volume attached to instance.
   550  	Force bool `json:"force,omitempty"`
   551  
   552  	// Visibility defines who can see/use the image.
   553  	// supported since 3.1 microversion
   554  	Visibility string `json:"visibility,omitempty"`
   555  
   556  	// whether the image is not deletable.
   557  	// supported since 3.1 microversion
   558  	Protected bool `json:"protected,omitempty"`
   559  }
   560  
   561  // ToVolumeUploadImageMap assembles a request body based on the contents of a
   562  // UploadImageOpts.
   563  func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]any, error) {
   564  	return gophercloud.BuildRequestBody(opts, "os-volume_upload_image")
   565  }
   566  
   567  // UploadImage will upload an image based on the values in UploadImageOptsBuilder.
   568  func UploadImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) {
   569  	b, err := opts.ToVolumeUploadImageMap()
   570  	if err != nil {
   571  		r.Err = err
   572  		return
   573  	}
   574  	resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
   575  		OkCodes: []int{202},
   576  	})
   577  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   578  	return
   579  }
   580  
   581  // ForceDelete will delete the volume regardless of state.
   582  func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) {
   583  	resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-force_delete": ""}, nil, nil)
   584  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   585  	return
   586  }
   587  
   588  // ImageMetadataOptsBuilder allows extensions to add additional parameters to the
   589  // ImageMetadataRequest request.
   590  type ImageMetadataOptsBuilder interface {
   591  	ToImageMetadataMap() (map[string]any, error)
   592  }
   593  
   594  // ImageMetadataOpts contains options for setting image metadata to a volume.
   595  type ImageMetadataOpts struct {
   596  	// The image metadata to add to the volume as a set of metadata key and value pairs.
   597  	Metadata map[string]string `json:"metadata"`
   598  }
   599  
   600  // ToImageMetadataMap assembles a request body based on the contents of a
   601  // ImageMetadataOpts.
   602  func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]any, error) {
   603  	return gophercloud.BuildRequestBody(opts, "os-set_image_metadata")
   604  }
   605  
   606  // SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder.
   607  func SetImageMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) {
   608  	b, err := opts.ToImageMetadataMap()
   609  	if err != nil {
   610  		r.Err = err
   611  		return
   612  	}
   613  	resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
   614  		OkCodes: []int{200},
   615  	})
   616  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   617  	return
   618  }
   619  
   620  // BootableOpts contains options for setting bootable status to a volume.
   621  type BootableOpts struct {
   622  	// Enables or disables the bootable attribute. You can boot an instance from a bootable volume.
   623  	Bootable bool `json:"bootable"`
   624  }
   625  
   626  // ToBootableMap assembles a request body based on the contents of a
   627  // BootableOpts.
   628  func (opts BootableOpts) ToBootableMap() (map[string]any, error) {
   629  	return gophercloud.BuildRequestBody(opts, "os-set_bootable")
   630  }
   631  
   632  // SetBootable will set bootable status on a volume based on the values in BootableOpts
   633  func SetBootable(ctx context.Context, client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) {
   634  	b, err := opts.ToBootableMap()
   635  	if err != nil {
   636  		r.Err = err
   637  		return
   638  	}
   639  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   640  		OkCodes: []int{200},
   641  	})
   642  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   643  	return
   644  }
   645  
   646  // MigrationPolicy type represents a migration_policy when changing types.
   647  type MigrationPolicy string
   648  
   649  // Supported attributes for MigrationPolicy attribute for changeType operations.
   650  const (
   651  	MigrationPolicyNever    MigrationPolicy = "never"
   652  	MigrationPolicyOnDemand MigrationPolicy = "on-demand"
   653  )
   654  
   655  // ChangeTypeOptsBuilder allows extensions to add additional parameters to the
   656  // ChangeType request.
   657  type ChangeTypeOptsBuilder interface {
   658  	ToVolumeChangeTypeMap() (map[string]any, error)
   659  }
   660  
   661  // ChangeTypeOpts contains options for changing the type of an existing Volume.
   662  // This object is passed to the volumes.ChangeType function.
   663  type ChangeTypeOpts struct {
   664  	// NewType is the name of the new volume type of the volume.
   665  	NewType string `json:"new_type" required:"true"`
   666  
   667  	// MigrationPolicy specifies if the volume should be migrated when it is
   668  	// re-typed. Possible values are "on-demand" or "never". If not specified,
   669  	// the default is "never".
   670  	MigrationPolicy MigrationPolicy `json:"migration_policy,omitempty"`
   671  }
   672  
   673  // ToVolumeChangeTypeMap assembles a request body based on the contents of an
   674  // ChangeTypeOpts.
   675  func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]any, error) {
   676  	return gophercloud.BuildRequestBody(opts, "os-retype")
   677  }
   678  
   679  // ChangeType will change the volume type of the volume based on the provided information.
   680  // This operation does not return a response body.
   681  func ChangeType(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) {
   682  	b, err := opts.ToVolumeChangeTypeMap()
   683  	if err != nil {
   684  		r.Err = err
   685  		return
   686  	}
   687  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   688  		OkCodes: []int{202},
   689  	})
   690  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   691  	return
   692  }
   693  
   694  // ReImageOpts contains options for Re-image a volume.
   695  type ReImageOpts struct {
   696  	// New image id
   697  	ImageID string `json:"image_id"`
   698  	// set true to re-image volumes in reserved state
   699  	ReImageReserved bool `json:"reimage_reserved"`
   700  }
   701  
   702  // ToReImageMap assembles a request body based on the contents of a ReImageOpts.
   703  func (opts ReImageOpts) ToReImageMap() (map[string]any, error) {
   704  	return gophercloud.BuildRequestBody(opts, "os-reimage")
   705  }
   706  
   707  // ReImage will re-image a volume based on the values in ReImageOpts
   708  func ReImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) {
   709  	b, err := opts.ToReImageMap()
   710  	if err != nil {
   711  		r.Err = err
   712  		return
   713  	}
   714  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   715  		OkCodes: []int{202},
   716  	})
   717  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   718  	return
   719  }
   720  
   721  // ResetStatusOptsBuilder allows extensions to add additional parameters to the
   722  // ResetStatus request.
   723  type ResetStatusOptsBuilder interface {
   724  	ToResetStatusMap() (map[string]any, error)
   725  }
   726  
   727  // ResetStatusOpts contains options for resetting a Volume status.
   728  // For more information about these parameters, please, refer to the Block Storage API V3,
   729  // Volume Actions, ResetStatus volume documentation.
   730  type ResetStatusOpts struct {
   731  	// Status is a volume status to reset to.
   732  	Status string `json:"status"`
   733  	// MigrationStatus is a volume migration status to reset to.
   734  	MigrationStatus string `json:"migration_status,omitempty"`
   735  	// AttachStatus is a volume attach status to reset to.
   736  	AttachStatus string `json:"attach_status,omitempty"`
   737  }
   738  
   739  // ToResetStatusMap assembles a request body based on the contents of a
   740  // ResetStatusOpts.
   741  func (opts ResetStatusOpts) ToResetStatusMap() (map[string]any, error) {
   742  	return gophercloud.BuildRequestBody(opts, "os-reset_status")
   743  }
   744  
   745  // ResetStatus will reset the existing volume status. ResetStatusResult contains only the error.
   746  // To extract it, call the ExtractErr method on the ResetStatusResult.
   747  func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) {
   748  	b, err := opts.ToResetStatusMap()
   749  	if err != nil {
   750  		r.Err = err
   751  		return
   752  	}
   753  
   754  	resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{
   755  		OkCodes: []int{202},
   756  	})
   757  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   758  	return
   759  }