github.com/LINBIT/golinstor@v0.52.0/client/resourcedefinition.go (about) 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 }