File: resourcedefinition.go

package info (click to toggle)
golang-github-linbit-golinstor 0.57.0-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid
  • size: 472 kB
  • sloc: makefile: 11
file content (413 lines) | stat: -rw-r--r-- 18,547 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
// A REST client to interact with LINSTOR's REST API
// Copyright (C) LINBIT HA-Solutions GmbH
// All Rights Reserved.
// Author: Roland Kammerer <roland.kammerer@linbit.com>
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package client

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"

	"github.com/google/go-querystring/query"

	"github.com/LINBIT/golinstor/clonestatus"
	"github.com/LINBIT/golinstor/devicelayerkind"
)

// ResourceDefinitionService is a struct for the client pointer
type ResourceDefinitionService struct {
	client *Client
}

// ResourceDefinition is a struct to store the information about a resource-definition
type ResourceDefinition struct {
	Name string `json:"name,omitempty"`
	// 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.
	ExternalName string `json:"external_name,omitempty"`
	// A string to string property map.
	Props     map[string]string         `json:"props,omitempty"`
	Flags     []string                  `json:"flags,omitempty"`
	LayerData []ResourceDefinitionLayer `json:"layer_data,omitempty"`
	// unique object id
	Uuid string `json:"uuid,omitempty"`
	// name of the linked resource group, if there is a link
	ResourceGroupName string `json:"resource_group_name,omitempty"`
}

// ResourceDefinitionCreate is a struct for holding the data needed to create a resource-defintion
type ResourceDefinitionCreate struct {
	// drbd port for resources
	DrbdPort int32 `json:"drbd_port,omitempty"`
	// drbd resource secret
	DrbdSecret         string             `json:"drbd_secret,omitempty"`
	DrbdTransportType  string             `json:"drbd_transport_type,omitempty"`
	ResourceDefinition ResourceDefinition `json:"resource_definition"`
}

// ResourceDefinitionLayer is a struct for the storing the layertype of a resource-defintion
type ResourceDefinitionLayer struct {
	Type devicelayerkind.DeviceLayerKind `json:"type,omitempty"`
	Data *DrbdResourceDefinitionLayer    `json:"data,omitempty"`
}

// DrbdResourceDefinitionLayer is a struct which contains the information about the layertype of a resource-definition on drbd level
type DrbdResourceDefinitionLayer struct {
	ResourceNameSuffix string `json:"resource_name_suffix,omitempty"`
	PeerSlots          int32  `json:"peer_slots,omitempty"`
	AlStripes          int64  `json:"al_stripes,omitempty"`
	// used drbd port for this resource
	Port          int32  `json:"port,omitempty"`
	TransportType string `json:"transport_type,omitempty"`
	// drbd resource secret
	Secret string `json:"secret,omitempty"`
	Down   bool   `json:"down,omitempty"`
}

// VolumeDefinitionCreate is a struct used for creating volume-definitions
type VolumeDefinitionCreate struct {
	VolumeDefinition VolumeDefinition `json:"volume_definition"`
	DrbdMinorNumber  int32            `json:"drbd_minor_number,omitempty"`
}

// VolumeDefinition is a struct which is used to store volume-definition properties
type VolumeDefinition struct {
	VolumeNumber *int32 `json:"volume_number,omitempty"`
	// Size of the volume in Kibi.
	SizeKib uint64 `json:"size_kib"`
	// A string to string property map.
	Props     map[string]string       `json:"props,omitempty"`
	Flags     []string                `json:"flags,omitempty"`
	LayerData []VolumeDefinitionLayer `json:"layer_data,omitempty"`
	// unique object id
	Uuid string `json:"uuid,omitempty"`
}

type VolumeDefinitionModify struct {
	SizeKib uint64 `json:"size_kib,omitempty"`
	GenericPropsModify
	// To add a flag just specify the flag name, to remove a flag prepend it with a '-'.  Flags:   * GROSS_SIZE
	Flags []string `json:"flags,omitempty"`
}

// VolumeDefinitionLayer is a struct for the layer-type of a volume-definition
type VolumeDefinitionLayer struct {
	Type devicelayerkind.DeviceLayerKind `json:"type"`
	Data OneOfDrbdVolumeDefinition       `json:"data,omitempty"`
}

// DrbdVolumeDefinition is a struct containing volume-definition on drbd level
type DrbdVolumeDefinition struct {
	ResourceNameSuffix string `json:"resource_name_suffix,omitempty"`
	VolumeNumber       int32  `json:"volume_number,omitempty"`
	MinorNumber        int32  `json:"minor_number,omitempty"`
}

type ResourceDefinitionCloneRequest struct {
	Name               string                            `json:"name,omitempty"`
	ExternalName       string                            `json:"external_name,omitempty"`
	UseZfsClone        bool                              `json:"use_zfs_clone,omitempty"`
	LayerList          []devicelayerkind.DeviceLayerKind `json:"layer_list,omitempty"`
	VolumePassphrases  []string                          `json:"volume_passphrases,omitempty"`
	ResourceGroup      string                            `json:"resource_group,omitempty"`
	GenericPropsModify `json:",inline"`
}

type ResourceDefinitionCloneStarted struct {
	// Path for clone status
	Location string `json:"location"`
	// name of the source resource
	SourceName string `json:"source_name"`
	// name of the clone resource
	CloneName string       `json:"clone_name"`
	Messages  *[]ApiCallRc `json:"messages,omitempty"`
}

type ResourceDefinitionCloneStatus struct {
	Status clonestatus.CloneStatus `json:"status"`
}

type ResourceDefinitionSyncStatus struct {
	SyncedOnAll bool `json:"synced_on_all"`
}

// custom code

type ResourceDefinitionWithVolumeDefinition struct {
	ResourceDefinition
	VolumeDefinitions []VolumeDefinition `json:"volume_definitions,omitempty"`
}

type RDGetAllRequest struct {
	// ResourceDefinitions filters the returned resource definitions by the given names
	ResourceDefinitions []string `url:"resource_definitions,omitempty"`
	// Props filters the returned resource definitions on their property values (uses key=value syntax)
	Props  []string `url:"props,omitempty"`
	Offset int      `url:"offset,omitempty"`
	Limit  int      `url:"offset,omitempty"`
	// WithVolumeDefinitions, if set to true, LINSTOR will also include volume definitions in the response.
	WithVolumeDefinitions bool `url:"with_volume_definitions,omitempty"`
}

// ResourceDefinitionProvider acts as an abstraction for a
// ResourceDefinitionService. It can be swapped out for another
// ResourceDefinitionService implementation, for example for testing.
type ResourceDefinitionProvider interface {
	// GetAll lists all resource-definitions
	GetAll(ctx context.Context, request RDGetAllRequest) ([]ResourceDefinitionWithVolumeDefinition, error)
	// Get return information about a resource-defintion
	Get(ctx context.Context, resDefName string, opts ...*ListOpts) (ResourceDefinition, error)
	// Create adds a new resource-definition
	Create(ctx context.Context, resDef ResourceDefinitionCreate) error
	// Modify allows to modify a resource-definition
	Modify(ctx context.Context, resDefName string, props GenericPropsModify) error
	// Delete completely deletes a resource-definition
	Delete(ctx context.Context, resDefName string) error
	// GetVolumeDefinitions returns all volume-definitions of a resource-definition
	GetVolumeDefinitions(ctx context.Context, resDefName string, opts ...*ListOpts) ([]VolumeDefinition, error)
	// GetVolumeDefinition shows the properties of a specific volume-definition
	GetVolumeDefinition(ctx context.Context, resDefName string, volNr int, opts ...*ListOpts) (VolumeDefinition, error)
	// CreateVolumeDefinition adds a volume-definition to a resource-definition. Only the size is required.
	CreateVolumeDefinition(ctx context.Context, resDefName string, volDef VolumeDefinitionCreate) error
	// ModifyVolumeDefinition give the abilty to modify a specific volume-definition
	ModifyVolumeDefinition(ctx context.Context, resDefName string, volNr int, props VolumeDefinitionModify) error
	// DeleteVolumeDefinition deletes a specific volume-definition
	DeleteVolumeDefinition(ctx context.Context, resDefName string, volNr int) error
	// GetPropsInfos gets meta information about the properties that can be
	// set on a resource definition.
	GetPropsInfos(ctx context.Context, opts ...*ListOpts) ([]PropsInfo, error)
	// GetDRBDProxyPropsInfos gets meta information about the properties
	// that can be set on a resource definition for drbd proxy.
	GetDRBDProxyPropsInfos(ctx context.Context, resDefName string, opts ...*ListOpts) ([]PropsInfo, error)
	// AttachExternalFile adds an external file to the resource definition. This
	// means that the file will be deployed to every node the resource is deployed on.
	AttachExternalFile(ctx context.Context, resDefName string, filePath string) error
	// DetachExternalFile removes a binding between an external file and a resource definition.
	// This means that the file will no longer be deployed on every node the resource
	// is deployed on.
	DetachExternalFile(ctx context.Context, resDefName string, filePath string) error
	// Clone starts cloning a resource definition and all resources using a method optimized for the storage driver.
	Clone(ctx context.Context, srcResDef string, request ResourceDefinitionCloneRequest) (ResourceDefinitionCloneStarted, error)
	// CloneStatus fetches the current status of a clone operation started by Clone.
	CloneStatus(ctx context.Context, srcResDef, targetResDef string) (ResourceDefinitionCloneStatus, error)
	// SyncStatus checks if a resource is currently in sync on all nodes
	SyncStatus(ctx context.Context, resDef string) (ResourceDefinitionSyncStatus, error)
}

var _ ResourceDefinitionProvider = &ResourceDefinitionService{}

// resourceDefinitionLayerIn is a struct for resource-definitions
type resourceDefinitionLayerIn struct {
	Type devicelayerkind.DeviceLayerKind `json:"type,omitempty"`
	Data json.RawMessage                 `json:"data,omitempty"`
}

// UnmarshalJSON is needed for the unmarshal interface for ResourceDefinitionLayer types
func (rd *ResourceDefinitionLayer) UnmarshalJSON(b []byte) error {
	var rdIn resourceDefinitionLayerIn
	if err := json.Unmarshal(b, &rdIn); err != nil {
		return err
	}

	rd.Type = rdIn.Type
	switch rd.Type {
	case devicelayerkind.Drbd:
		dst := new(DrbdResourceDefinitionLayer)
		if rdIn.Data != nil {
			if err := json.Unmarshal(rdIn.Data, &dst); err != nil {
				return err
			}
		}
		rd.Data = dst
	case devicelayerkind.Luks, devicelayerkind.Storage, devicelayerkind.Nvme, devicelayerkind.Writecache, devicelayerkind.Cache: // valid types, but do not set data
	default:
		return fmt.Errorf("'%+v' is not a valid type to Unmarshal", rd.Type)
	}

	return nil
}

// volumeDefinitionLayerIn is a struct for volume-defintion-layers
type volumeDefinitionLayerIn struct {
	Type devicelayerkind.DeviceLayerKind `json:"type,omitempty"`
	Data json.RawMessage                 `json:"data,omitempty"`
}

// UnmarshalJSON is needed for the unmarshal interface for VolumeDefinitionLayer types
func (vd *VolumeDefinitionLayer) UnmarshalJSON(b []byte) error {
	var vdIn volumeDefinitionLayerIn
	if err := json.Unmarshal(b, &vdIn); err != nil {
		return err
	}

	vd.Type = vdIn.Type
	switch vd.Type {
	case devicelayerkind.Drbd:
		dst := new(DrbdVolumeDefinition)
		if vdIn.Data != nil {
			if err := json.Unmarshal(vdIn.Data, &dst); err != nil {
				return err
			}
		}
		vd.Data = dst
	case devicelayerkind.Luks, devicelayerkind.Storage, devicelayerkind.Nvme, devicelayerkind.Writecache, devicelayerkind.Cache: // valid types, but do not set data
	default:
		return fmt.Errorf("'%+v' is not a valid type to Unmarshal", vd.Type)
	}

	return nil
}

// OneOfDrbdVolumeDefinition is used to prevent other layertypes than drbd-volume-defintion
type OneOfDrbdVolumeDefinition interface {
	isOneOfDrbdVolumeDefinition()
}

// Function used if volume-defintion-layertype is correct
func (d *DrbdVolumeDefinition) isOneOfDrbdVolumeDefinition() {}

// GetAll lists all resource-definitions
func (n *ResourceDefinitionService) GetAll(ctx context.Context, request RDGetAllRequest) ([]ResourceDefinitionWithVolumeDefinition, error) {
	val, err := query.Values(request)
	if err != nil {
		return nil, err
	}

	var resDefs []ResourceDefinitionWithVolumeDefinition
	_, err = n.client.doGET(ctx, "/v1/resource-definitions?"+val.Encode(), &resDefs)
	return resDefs, err
}

// Get return information about a resource-defintion
func (n *ResourceDefinitionService) Get(ctx context.Context, resDefName string, opts ...*ListOpts) (ResourceDefinition, error) {
	var resDef ResourceDefinition
	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDefName, &resDef, opts...)
	return resDef, err
}

// Create adds a new resource-definition
func (n *ResourceDefinitionService) Create(ctx context.Context, resDef ResourceDefinitionCreate) error {
	_, err := n.client.doPOST(ctx, "/v1/resource-definitions", resDef)
	return err
}

// Modify allows to modify a resource-definition
func (n *ResourceDefinitionService) Modify(ctx context.Context, resDefName string, props GenericPropsModify) error {
	_, err := n.client.doPUT(ctx, "/v1/resource-definitions/"+resDefName, props)
	return err
}

// Delete completely deletes a resource-definition
func (n *ResourceDefinitionService) Delete(ctx context.Context, resDefName string) error {
	_, err := n.client.doDELETE(ctx, "/v1/resource-definitions/"+resDefName, nil)
	return err
}

// GetVolumeDefinitions returns all volume-definitions of a resource-definition
func (n *ResourceDefinitionService) GetVolumeDefinitions(ctx context.Context, resDefName string, opts ...*ListOpts) ([]VolumeDefinition, error) {
	var volDefs []VolumeDefinition
	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions", &volDefs, opts...)
	return volDefs, err
}

// GetVolumeDefinition shows the properties of a specific volume-definition
func (n *ResourceDefinitionService) GetVolumeDefinition(ctx context.Context, resDefName string, volNr int, opts ...*ListOpts) (VolumeDefinition, error) {
	var volDef VolumeDefinition
	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions/"+strconv.Itoa(volNr), &volDef, opts...)
	return volDef, err
}

// CreateVolumeDefinition adds a volume-definition to a resource-definition. Only the size is required.
func (n *ResourceDefinitionService) CreateVolumeDefinition(ctx context.Context, resDefName string, volDef VolumeDefinitionCreate) error {
	_, err := n.client.doPOST(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions", volDef)
	return err
}

// ModifyVolumeDefinition give the abilty to modify a specific volume-definition
func (n *ResourceDefinitionService) ModifyVolumeDefinition(ctx context.Context, resDefName string, volNr int, props VolumeDefinitionModify) error {
	_, err := n.client.doPUT(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions/"+strconv.Itoa(volNr), props)
	return err
}

// DeleteVolumeDefinition deletes a specific volume-definition
func (n *ResourceDefinitionService) DeleteVolumeDefinition(ctx context.Context, resDefName string, volNr int) error {
	_, err := n.client.doDELETE(ctx, "/v1/resource-definitions/"+resDefName+"/volume-definitions/"+strconv.Itoa(volNr), nil)
	return err
}

// GetPropsInfos gets meta information about the properties that can be set on
// a resource definition.
func (n *ResourceDefinitionService) GetPropsInfos(ctx context.Context, opts ...*ListOpts) ([]PropsInfo, error) {
	var infos []PropsInfo
	_, err := n.client.doGET(ctx, "/v1/resource-definitions/properties/info", &infos, opts...)
	return infos, err
}

// GetDRBDProxyPropsInfos gets meta information about the properties that can
// be set on a resource definition for drbd proxy.
func (n *ResourceDefinitionService) GetDRBDProxyPropsInfos(ctx context.Context, resDefName string, opts ...*ListOpts) ([]PropsInfo, error) {
	var infos []PropsInfo
	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDefName+"/drbd-proxy/properties/info", &infos, opts...)
	return infos, err
}

// AttachExternalFile adds an external file to the resource definition. This
// means that the file will be deployed to every node the resource is deployed on.
func (n *ResourceDefinitionService) AttachExternalFile(ctx context.Context, resDefName string, filePath string) error {
	_, err := n.client.doPOST(ctx, "/v1/resource-definitions/"+resDefName+"/files/"+url.QueryEscape(filePath), nil)
	return err
}

// DetachExternalFile removes a binding between an external file and a resource definition.
// This means that the file will no longer be deployed on every node the resource
// is deployed on.
func (n *ResourceDefinitionService) DetachExternalFile(ctx context.Context, resDefName string, filePath string) error {
	_, err := n.client.doDELETE(ctx, "/v1/resource-definitions/"+resDefName+"/files/"+url.QueryEscape(filePath), nil)
	return err
}

// Clone starts cloning a resource definition and all resources using a method optimized for the storage driver.
func (n *ResourceDefinitionService) Clone(ctx context.Context, srcResDef string, request ResourceDefinitionCloneRequest) (ResourceDefinitionCloneStarted, error) {
	var resp ResourceDefinitionCloneStarted

	req, err := n.client.newRequest("POST", "/v1/resource-definitions/"+srcResDef+"/clone", request)
	if err != nil {
		return ResourceDefinitionCloneStarted{}, err
	}

	_, err = n.client.doJSON(ctx, req, &resp)
	if err != nil {
		return ResourceDefinitionCloneStarted{}, err
	}

	return resp, nil
}

// CloneStatus fetches the current status of a clone operation started by Clone.
func (n *ResourceDefinitionService) CloneStatus(ctx context.Context, srcResDef, targetResDef string) (ResourceDefinitionCloneStatus, error) {
	var status ResourceDefinitionCloneStatus
	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+srcResDef+"/clone/"+targetResDef, &status)
	return status, err
}

// SyncStatus checks if a resource is currently in sync on all nodes
func (n *ResourceDefinitionService) SyncStatus(ctx context.Context, resDef string) (ResourceDefinitionSyncStatus, error) {
	var status ResourceDefinitionSyncStatus
	_, err := n.client.doGET(ctx, "/v1/resource-definitions/"+resDef+"/sync-status", &status)
	return status, err
}