...

Source file src/github.com/LINBIT/golinstor/client/resourcedefinition.go

Documentation: github.com/LINBIT/golinstor/client

     1  // A REST client to interact with LINSTOR's REST API
     2  // Copyright (C) LINBIT HA-Solutions GmbH
     3  // All Rights Reserved.
     4  // Author: Roland Kammerer <roland.kammerer@linbit.com>
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     7  // not use this file except in compliance with the License. You may obtain
     8  // a copy of the License at
     9  //
    10  // http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    14  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    15  // License for the specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package client
    19  
    20  import (
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"net/url"
    25  	"strconv"
    26  
    27  	"github.com/google/go-querystring/query"
    28  
    29  	"github.com/LINBIT/golinstor/clonestatus"
    30  	"github.com/LINBIT/golinstor/devicelayerkind"
    31  )
    32  
    33  // ResourceDefinitionService is a struct for the client pointer
    34  type ResourceDefinitionService struct {
    35  	client *Client
    36  }
    37  
    38  // ResourceDefinition is a struct to store the information about a resource-definition
    39  type ResourceDefinition struct {
    40  	Name string `json:"name,omitempty"`
    41  	// External name can be used to have native resource names. If you need to store a non Linstor compatible resource name use this field and Linstor will generate a compatible name.
    42  	ExternalName string `json:"external_name,omitempty"`
    43  	// A string to string property map.
    44  	Props     map[string]string         `json:"props,omitempty"`
    45  	Flags     []string                  `json:"flags,omitempty"`
    46  	LayerData []ResourceDefinitionLayer `json:"layer_data,omitempty"`
    47  	// unique object id
    48  	Uuid string `json:"uuid,omitempty"`
    49  	// name of the linked resource group, if there is a link
    50  	ResourceGroupName string `json:"resource_group_name,omitempty"`
    51  }
    52  
    53  // ResourceDefinitionCreate is a struct for holding the data needed to create a resource-defintion
    54  type ResourceDefinitionCreate struct {
    55  	// drbd port for resources
    56  	DrbdPort int32 `json:"drbd_port,omitempty"`
    57  	// drbd resource secret
    58  	DrbdSecret         string             `json:"drbd_secret,omitempty"`
    59  	DrbdTransportType  string             `json:"drbd_transport_type,omitempty"`
    60  	ResourceDefinition ResourceDefinition `json:"resource_definition"`
    61  }
    62  
    63  // ResourceDefinitionLayer is a struct for the storing the layertype of a resource-defintion
    64  type ResourceDefinitionLayer struct {
    65  	Type devicelayerkind.DeviceLayerKind `json:"type,omitempty"`
    66  	Data *DrbdResourceDefinitionLayer    `json:"data,omitempty"`
    67  }
    68  
    69  // DrbdResourceDefinitionLayer is a struct which contains the information about the layertype of a resource-definition on drbd level
    70  type DrbdResourceDefinitionLayer struct {
    71  	ResourceNameSuffix string `json:"resource_name_suffix,omitempty"`
    72  	PeerSlots          int32  `json:"peer_slots,omitempty"`
    73  	AlStripes          int64  `json:"al_stripes,omitempty"`
    74  	// used drbd port for this resource
    75  	Port          int32  `json:"port,omitempty"`
    76  	TransportType string `json:"transport_type,omitempty"`
    77  	// drbd resource secret
    78  	Secret string `json:"secret,omitempty"`
    79  	Down   bool   `json:"down,omitempty"`
    80  }
    81  
    82  // VolumeDefinitionCreate is a struct used for creating volume-definitions
    83  type VolumeDefinitionCreate struct {
    84  	VolumeDefinition VolumeDefinition `json:"volume_definition"`
    85  	DrbdMinorNumber  int32            `json:"drbd_minor_number,omitempty"`
    86  }
    87  
    88  // VolumeDefinition is a struct which is used to store volume-definition properties
    89  type VolumeDefinition struct {
    90  	VolumeNumber *int32 `json:"volume_number,omitempty"`
    91  	// Size of the volume in Kibi.
    92  	SizeKib uint64 `json:"size_kib"`
    93  	// A string to string property map.
    94  	Props     map[string]string       `json:"props,omitempty"`
    95  	Flags     []string                `json:"flags,omitempty"`
    96  	LayerData []VolumeDefinitionLayer `json:"layer_data,omitempty"`
    97  	// unique object id
    98  	Uuid string `json:"uuid,omitempty"`
    99  }
   100  
   101  type VolumeDefinitionModify struct {
   102  	SizeKib uint64 `json:"size_kib,omitempty"`
   103  	GenericPropsModify
   104  	// To add a flag just specify the flag name, to remove a flag prepend it with a '-'.  Flags:   * GROSS_SIZE
   105  	Flags []string `json:"flags,omitempty"`
   106  }
   107  
   108  // VolumeDefinitionLayer is a struct for the layer-type of a volume-definition
   109  type VolumeDefinitionLayer struct {
   110  	Type devicelayerkind.DeviceLayerKind `json:"type"`
   111  	Data OneOfDrbdVolumeDefinition       `json:"data,omitempty"`
   112  }
   113  
   114  // DrbdVolumeDefinition is a struct containing volume-definition on drbd level
   115  type DrbdVolumeDefinition struct {
   116  	ResourceNameSuffix string `json:"resource_name_suffix,omitempty"`
   117  	VolumeNumber       int32  `json:"volume_number,omitempty"`
   118  	MinorNumber        int32  `json:"minor_number,omitempty"`
   119  }
   120  
   121  type ResourceDefinitionCloneRequest struct {
   122  	Name         string `json:"name,omitempty"`
   123  	ExternalName string `json:"external_name,omitempty"`
   124  }
   125  
   126  type ResourceDefinitionCloneStarted struct {
   127  	// Path for clone status
   128  	Location string `json:"location"`
   129  	// name of the source resource
   130  	SourceName string `json:"source_name"`
   131  	// name of the clone resource
   132  	CloneName string       `json:"clone_name"`
   133  	Messages  *[]ApiCallRc `json:"messages,omitempty"`
   134  }
   135  
   136  type ResourceDefinitionCloneStatus struct {
   137  	Status clonestatus.CloneStatus `json:"status"`
   138  }
   139  
   140  type ResourceDefinitionSyncStatus struct {
   141  	SyncedOnAll bool `json:"synced_on_all"`
   142  }
   143  
   144  // custom code
   145  
   146  type ResourceDefinitionWithVolumeDefinition struct {
   147  	ResourceDefinition
   148  	VolumeDefinitions []VolumeDefinition `json:"volume_definitions,omitempty"`
   149  }
   150  
   151  type RDGetAllRequest struct {
   152  	// ResourceDefinitions filters the returned resource definitions by the given names
   153  	ResourceDefinitions []string `url:"resource_definitions,omitempty"`
   154  	// Props filters the returned resource definitions on their property values (uses key=value syntax)
   155  	Props  []string `url:"props,omitempty"`
   156  	Offset int      `url:"offset,omitempty"`
   157  	Limit  int      `url:"offset,omitempty"`
   158  	// WithVolumeDefinitions, if set to true, LINSTOR will also include volume definitions in the response.
   159  	WithVolumeDefinitions bool `url:"with_volume_definitions,omitempty"`
   160  }
   161  
   162  // ResourceDefinitionProvider acts as an abstraction for a
   163  // ResourceDefinitionService. It can be swapped out for another
   164  // ResourceDefinitionService implementation, for example for testing.
   165  type ResourceDefinitionProvider interface {
   166  	// GetAll lists all resource-definitions
   167  	GetAll(ctx context.Context, request RDGetAllRequest) ([]ResourceDefinitionWithVolumeDefinition, error)
   168  	// Get return information about a resource-defintion
   169  	Get(ctx context.Context, resDefName string, opts ...*ListOpts) (ResourceDefinition, error)
   170  	// Create adds a new resource-definition
   171  	Create(ctx context.Context, resDef ResourceDefinitionCreate) error
   172  	// Modify allows to modify a resource-definition
   173  	Modify(ctx context.Context, resDefName string, props GenericPropsModify) error
   174  	// Delete completely deletes a resource-definition
   175  	Delete(ctx context.Context, resDefName string) error
   176  	// GetVolumeDefinitions returns all volume-definitions of a resource-definition
   177  	GetVolumeDefinitions(ctx context.Context, resDefName string, opts ...*ListOpts) ([]VolumeDefinition, error)
   178  	// GetVolumeDefinition shows the properties of a specific volume-definition
   179  	GetVolumeDefinition(ctx context.Context, resDefName string, volNr int, opts ...*ListOpts) (VolumeDefinition, error)
   180  	// CreateVolumeDefinition adds a volume-definition to a resource-definition. Only the size is required.
   181  	CreateVolumeDefinition(ctx context.Context, resDefName string, volDef VolumeDefinitionCreate) error
   182  	// ModifyVolumeDefinition give the abilty to modify a specific volume-definition
   183  	ModifyVolumeDefinition(ctx context.Context, resDefName string, volNr int, props VolumeDefinitionModify) error
   184  	// DeleteVolumeDefinition deletes a specific volume-definition
   185  	DeleteVolumeDefinition(ctx context.Context, resDefName string, volNr int) error
   186  	// GetPropsInfos gets meta information about the properties that can be
   187  	// set on a resource definition.
   188  	GetPropsInfos(ctx context.Context, opts ...*ListOpts) ([]PropsInfo, error)
   189  	// GetDRBDProxyPropsInfos gets meta information about the properties
   190  	// that can be set on a resource definition for drbd proxy.
   191  	GetDRBDProxyPropsInfos(ctx context.Context, resDefName string, opts ...*ListOpts) ([]PropsInfo, error)
   192  	// AttachExternalFile adds an external file to the resource definition. This
   193  	// means that the file will be deployed to every node the resource is deployed on.
   194  	AttachExternalFile(ctx context.Context, resDefName string, filePath string) error
   195  	// DetachExternalFile removes a binding between an external file and a resource definition.
   196  	// This means that the file will no longer be deployed on every node the resource
   197  	// is deployed on.
   198  	DetachExternalFile(ctx context.Context, resDefName string, filePath string) error
   199  	// Clone starts cloning a resource definition and all resources using a method optimized for the storage driver.
   200  	Clone(ctx context.Context, srcResDef string, request ResourceDefinitionCloneRequest) (ResourceDefinitionCloneStarted, error)
   201  	// CloneStatus fetches the current status of a clone operation started by Clone.
   202  	CloneStatus(ctx context.Context, srcResDef, targetResDef string) (ResourceDefinitionCloneStatus, error)
   203  	// SyncStatus checks if a resource is currently in sync on all nodes
   204  	SyncStatus(ctx context.Context, resDef string) (ResourceDefinitionSyncStatus, error)
   205  }
   206  
   207  var _ ResourceDefinitionProvider = &ResourceDefinitionService{}
   208  
   209  // resourceDefinitionLayerIn is a struct for resource-definitions
   210  type resourceDefinitionLayerIn struct {
   211  	Type devicelayerkind.DeviceLayerKind `json:"type,omitempty"`
   212  	Data json.RawMessage                 `json:"data,omitempty"`
   213  }
   214  
   215  // UnmarshalJSON is needed for the unmarshal interface for ResourceDefinitionLayer types
   216  func (rd *ResourceDefinitionLayer) UnmarshalJSON(b []byte) error {
   217  	var rdIn resourceDefinitionLayerIn
   218  	if err := json.Unmarshal(b, &rdIn); err != nil {
   219  		return err
   220  	}
   221  
   222  	rd.Type = rdIn.Type
   223  	switch rd.Type {
   224  	case devicelayerkind.Drbd:
   225  		dst := new(DrbdResourceDefinitionLayer)
   226  		if rdIn.Data != nil {
   227  			if err := json.Unmarshal(rdIn.Data, &dst); err != nil {
   228  				return err
   229  			}
   230  		}
   231  		rd.Data = dst
   232  	case devicelayerkind.Luks, devicelayerkind.Storage, devicelayerkind.Nvme, devicelayerkind.Writecache, devicelayerkind.Cache, devicelayerkind.Exos: // valid types, but do not set data
   233  	default:
   234  		return fmt.Errorf("'%+v' is not a valid type to Unmarshal", rd.Type)
   235  	}
   236  
   237  	return nil
   238  }
   239  
   240  // volumeDefinitionLayerIn is a struct for volume-defintion-layers
   241  type volumeDefinitionLayerIn struct {
   242  	Type devicelayerkind.DeviceLayerKind `json:"type,omitempty"`
   243  	Data json.RawMessage                 `json:"data,omitempty"`
   244  }
   245  
   246  // UnmarshalJSON is needed for the unmarshal interface for VolumeDefinitionLayer types
   247  func (vd *VolumeDefinitionLayer) UnmarshalJSON(b []byte) error {
   248  	var vdIn volumeDefinitionLayerIn
   249  	if err := json.Unmarshal(b, &vdIn); err != nil {
   250  		return err
   251  	}
   252  
   253  	vd.Type = vdIn.Type
   254  	switch vd.Type {
   255  	case devicelayerkind.Drbd:
   256  		dst := new(DrbdVolumeDefinition)
   257  		if vdIn.Data != nil {
   258  			if err := json.Unmarshal(vdIn.Data, &dst); err != nil {
   259  				return err
   260  			}
   261  		}
   262  		vd.Data = dst
   263  	case devicelayerkind.Luks, devicelayerkind.Storage, devicelayerkind.Nvme, devicelayerkind.Writecache, devicelayerkind.Cache, devicelayerkind.Exos: // valid types, but do not set data
   264  	default:
   265  		return fmt.Errorf("'%+v' is not a valid type to Unmarshal", vd.Type)
   266  	}
   267  
   268  	return nil
   269  }
   270  
   271  // OneOfDrbdVolumeDefinition is used to prevent other layertypes than drbd-volume-defintion
   272  type OneOfDrbdVolumeDefinition interface {
   273  	isOneOfDrbdVolumeDefinition()
   274  }
   275  
   276  // Function used if volume-defintion-layertype is correct
   277  func (d *DrbdVolumeDefinition) isOneOfDrbdVolumeDefinition() {}
   278  
   279  // GetAll lists all resource-definitions
   280  func (n *ResourceDefinitionService) GetAll(ctx context.Context, request RDGetAllRequest) ([]ResourceDefinitionWithVolumeDefinition, error) {
   281  	val, err := query.Values(request)
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  
   286  	var resDefs []ResourceDefinitionWithVolumeDefinition
   287  	_, err = n.client.doGET(ctx, "/v1/resource-definitions?"+val.Encode(), &resDefs)
   288  	return resDefs, err
   289  }
   290  
   291  // Get return information about a resource-defintion
   292  func (n *ResourceDefinitionService) Get(ctx context.Context, resDefName string, opts ...*ListOpts) (ResourceDefinition, error) {
   293  	var resDef ResourceDefinition
   294  	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDefName, &resDef, opts...)
   295  	return resDef, err
   296  }
   297  
   298  // Create adds a new resource-definition
   299  func (n *ResourceDefinitionService) Create(ctx context.Context, resDef ResourceDefinitionCreate) error {
   300  	_, err := n.client.doPOST(ctx, "/v1/resource-definitions", resDef)
   301  	return err
   302  }
   303  
   304  // Modify allows to modify a resource-definition
   305  func (n *ResourceDefinitionService) Modify(ctx context.Context, resDefName string, props GenericPropsModify) error {
   306  	_, err := n.client.doPUT(ctx, "/v1/resource-definitions/"+resDefName, props)
   307  	return err
   308  }
   309  
   310  // Delete completely deletes a resource-definition
   311  func (n *ResourceDefinitionService) Delete(ctx context.Context, resDefName string) error {
   312  	_, err := n.client.doDELETE(ctx, "/v1/resource-definitions/"+resDefName, nil)
   313  	return err
   314  }
   315  
   316  // GetVolumeDefinitions returns all volume-definitions of a resource-definition
   317  func (n *ResourceDefinitionService) GetVolumeDefinitions(ctx context.Context, resDefName string, opts ...*ListOpts) ([]VolumeDefinition, error) {
   318  	var volDefs []VolumeDefinition
   319  	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions", &volDefs, opts...)
   320  	return volDefs, err
   321  }
   322  
   323  // GetVolumeDefinition shows the properties of a specific volume-definition
   324  func (n *ResourceDefinitionService) GetVolumeDefinition(ctx context.Context, resDefName string, volNr int, opts ...*ListOpts) (VolumeDefinition, error) {
   325  	var volDef VolumeDefinition
   326  	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions/"+strconv.Itoa(volNr), &volDef, opts...)
   327  	return volDef, err
   328  }
   329  
   330  // CreateVolumeDefinition adds a volume-definition to a resource-definition. Only the size is required.
   331  func (n *ResourceDefinitionService) CreateVolumeDefinition(ctx context.Context, resDefName string, volDef VolumeDefinitionCreate) error {
   332  	_, err := n.client.doPOST(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions", volDef)
   333  	return err
   334  }
   335  
   336  // ModifyVolumeDefinition give the abilty to modify a specific volume-definition
   337  func (n *ResourceDefinitionService) ModifyVolumeDefinition(ctx context.Context, resDefName string, volNr int, props VolumeDefinitionModify) error {
   338  	_, err := n.client.doPUT(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions/"+strconv.Itoa(volNr), props)
   339  	return err
   340  }
   341  
   342  // DeleteVolumeDefinition deletes a specific volume-definition
   343  func (n *ResourceDefinitionService) DeleteVolumeDefinition(ctx context.Context, resDefName string, volNr int) error {
   344  	_, err := n.client.doDELETE(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions/"+strconv.Itoa(volNr), nil)
   345  	return err
   346  }
   347  
   348  // GetPropsInfos gets meta information about the properties that can be set on
   349  // a resource definition.
   350  func (n *ResourceDefinitionService) GetPropsInfos(ctx context.Context, opts ...*ListOpts) ([]PropsInfo, error) {
   351  	var infos []PropsInfo
   352  	_, err := n.client.doGET(ctx, "/v1/resource-definitions/properties/info", &infos, opts...)
   353  	return infos, err
   354  }
   355  
   356  // GetDRBDProxyPropsInfos gets meta information about the properties that can
   357  // be set on a resource definition for drbd proxy.
   358  func (n *ResourceDefinitionService) GetDRBDProxyPropsInfos(ctx context.Context, resDefName string, opts ...*ListOpts) ([]PropsInfo, error) {
   359  	var infos []PropsInfo
   360  	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDefName+"/drbd-proxy/properties/info", &infos, opts...)
   361  	return infos, err
   362  }
   363  
   364  // AttachExternalFile adds an external file to the resource definition. This
   365  // means that the file will be deployed to every node the resource is deployed on.
   366  func (n *ResourceDefinitionService) AttachExternalFile(ctx context.Context, resDefName string, filePath string) error {
   367  	_, err := n.client.doPOST(ctx, "/v1/resource-definitions/"+resDefName+"/files/"+url.QueryEscape(filePath), nil)
   368  	return err
   369  }
   370  
   371  // DetachExternalFile removes a binding between an external file and a resource definition.
   372  // This means that the file will no longer be deployed on every node the resource
   373  // is deployed on.
   374  func (n *ResourceDefinitionService) DetachExternalFile(ctx context.Context, resDefName string, filePath string) error {
   375  	_, err := n.client.doDELETE(ctx, "/v1/resource-definitions/"+resDefName+"/files/"+url.QueryEscape(filePath), nil)
   376  	return err
   377  }
   378  
   379  // Clone starts cloning a resource definition and all resources using a method optimized for the storage driver.
   380  func (n *ResourceDefinitionService) Clone(ctx context.Context, srcResDef string, request ResourceDefinitionCloneRequest) (ResourceDefinitionCloneStarted, error) {
   381  	var resp ResourceDefinitionCloneStarted
   382  
   383  	req, err := n.client.newRequest("POST", "/v1/resource-definitions/"+srcResDef+"/clone", request)
   384  	if err != nil {
   385  		return ResourceDefinitionCloneStarted{}, err
   386  	}
   387  
   388  	_, err = n.client.do(ctx, req, &resp)
   389  	if err != nil {
   390  		return ResourceDefinitionCloneStarted{}, err
   391  	}
   392  
   393  	return resp, nil
   394  }
   395  
   396  // CloneStatus fetches the current status of a clone operation started by Clone.
   397  func (n *ResourceDefinitionService) CloneStatus(ctx context.Context, srcResDef, targetResDef string) (ResourceDefinitionCloneStatus, error) {
   398  	var status ResourceDefinitionCloneStatus
   399  	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+srcResDef+"/clone/"+targetResDef, &status)
   400  	return status, err
   401  }
   402  
   403  // SyncStatus checks if a resource is currently in sync on all nodes
   404  func (n *ResourceDefinitionService) SyncStatus(ctx context.Context, resDef string) (ResourceDefinitionSyncStatus, error) {
   405  	var status ResourceDefinitionSyncStatus
   406  	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDef+"/sync-status", &status)
   407  	return status, err
   408  }
   409  

View as plain text