From 7d6cda7119fd9c345025ed6f1e1a77e6d58a10ae Mon Sep 17 00:00:00 2001 From: stSolo Date: Thu, 20 Apr 2023 11:17:35 +0300 Subject: [PATCH] v1.3.1 --- .gitignore | 3 +- CHANGELOG.md | 54 +++---- internal/validators/custom.go | 14 +- internal/validators/messages.go | 4 + internal/validators/validator.go | 5 + pkg/cloudapi/account/models.go | 2 +- pkg/cloudapi/account/update.go | 12 +- pkg/cloudapi/compute/filter_test.go | 114 +++++++------- pkg/cloudapi/compute/models.go | 24 ++- pkg/cloudapi/disks/filter.go | 59 ++++++++ pkg/cloudapi/disks/filter_test.go | 199 ++++++++++++++++++++++++- pkg/cloudapi/disks/list_unattached.go | 4 +- pkg/cloudapi/disks/models.go | 137 +++++++++++++++++ pkg/cloudapi/disks/serialize.go | 36 +++++ pkg/cloudapi/disks/sorting.go | 57 +++++++ pkg/cloudapi/extnet/models.go | 19 ++- pkg/cloudapi/k8s/workers_group_add.go | 10 +- pkg/cloudapi/locations/filter.go | 9 ++ pkg/cloudapi/rg/models.go | 8 +- pkg/cloudapi/rg/update.go | 10 +- pkg/cloudbroker/account/update.go | 18 +-- pkg/cloudbroker/pcidevice.go | 8 + pkg/cloudbroker/pcidevice/create.go | 56 +++++++ pkg/cloudbroker/pcidevice/delete.go | 43 ++++++ pkg/cloudbroker/pcidevice/disable.go | 43 ++++++ pkg/cloudbroker/pcidevice/enable.go | 39 +++++ pkg/cloudbroker/pcidevice/list.go | 26 ++++ pkg/cloudbroker/pcidevice/models.go | 43 ++++++ pkg/cloudbroker/pcidevice/pcidevice.go | 15 ++ pkg/cloudbroker/pcidevice/serialize.go | 42 ++++++ pkg/cloudbroker/rg/update.go | 10 +- pkg/cloudbroker/vgpu.go | 8 + pkg/cloudbroker/vgpu/allocate.go | 39 +++++ pkg/cloudbroker/vgpu/create.go | 51 +++++++ pkg/cloudbroker/vgpu/deallocate.go | 43 ++++++ pkg/cloudbroker/vgpu/destroy.go | 43 ++++++ pkg/cloudbroker/vgpu/list.go | 37 +++++ pkg/cloudbroker/vgpu/models.go | 66 ++++++++ pkg/cloudbroker/vgpu/serialize.go | 42 ++++++ pkg/cloudbroker/vgpu/vgpu.go | 15 ++ 40 files changed, 1339 insertions(+), 128 deletions(-) create mode 100644 pkg/cloudbroker/pcidevice.go create mode 100644 pkg/cloudbroker/pcidevice/create.go create mode 100644 pkg/cloudbroker/pcidevice/delete.go create mode 100644 pkg/cloudbroker/pcidevice/disable.go create mode 100644 pkg/cloudbroker/pcidevice/enable.go create mode 100644 pkg/cloudbroker/pcidevice/list.go create mode 100644 pkg/cloudbroker/pcidevice/models.go create mode 100644 pkg/cloudbroker/pcidevice/pcidevice.go create mode 100644 pkg/cloudbroker/pcidevice/serialize.go create mode 100644 pkg/cloudbroker/vgpu.go create mode 100644 pkg/cloudbroker/vgpu/allocate.go create mode 100644 pkg/cloudbroker/vgpu/create.go create mode 100644 pkg/cloudbroker/vgpu/deallocate.go create mode 100644 pkg/cloudbroker/vgpu/destroy.go create mode 100644 pkg/cloudbroker/vgpu/list.go create mode 100644 pkg/cloudbroker/vgpu/models.go create mode 100644 pkg/cloudbroker/vgpu/serialize.go create mode 100644 pkg/cloudbroker/vgpu/vgpu.go diff --git a/.gitignore b/.gitignore index 51aff64..c4c9112 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -cmd/ \ No newline at end of file +cmd/ +.idea/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e4215..cf8bcf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,39 +1,33 @@ -## Version 1.3.0 +## Version 1.3.1 ### Features -- Created CloudAPI/CloudBroker filtering, sorting and serialization functions for List requests. - - Every handler with present List request has available FilterBy functions. Filtering by ID, Name is common for each handler. - - In case user needs to filter response by uncommon field FilterFunc with user-specified predicate is also available. - - CloudAPI/CloudBroker computes, disks and lb also have specific Filter methods predefined, to name a few: - - computes: - - FilterByK8SID, used to filter computes used by specified k8s cluster; - - FilterByK8SMasters, FilterByK8SWorkers, used to filter master/workers nodes. Best used after FilterByK8SID call; - - FilterByLBID, used to filter computes used by specified load balancer; - - - disks: - - FilterByK8SID, used to filter disks attached to computes inside specified k8s cluster; - - FilterByLBID, used to filter disks attached to computes inside specified load balancer; - - - lb: - - FilterByK8SID, used to filter load balancers used by specified k8s cluster; - -- Reinvented request validation using go-validator. Made easier to manipulate and add on to. - - Request/Config validation now uses tags instead of hard-coded validation functions; - -- Added ability to parse client configuration from JSON or YAML formatted files. +- Added FilterByGID for cloudapi/locations/list handler response, used to filter locations by specified GID. +- Added /cloudbroker/pcidevices endpoints support + - /cloudbroker/pcidevices/create + - /cloudbroker/pcidevices/delete + - /cloudbroker/pcidevices/disable + - /cloudbroker/pcidevices/enable + - /cloudbroker/pcidevices/list +- Added /cloudbroker/vgpu endpoints support + - /cloudbroker/vgpu/allocate + - /cloudbroker/vgpu/create + - /cloudbroker/vgpu/deallocate + - /cloudbroker/vgpu/destroy + - /cloudbroker/vgpu/list ### Bug Fixes -- Fixed SSO_URL trailing slash possibly breaking authentication process. -- Fixed cloudbroker/vins/nat_rule_add request model types. -- Fixed cloudbroker/grid DiskSize field type -- Fixed TasksResult, InfoResult in cloudbroker/cloudapi/tasks/models JSON unmarshalling. +- Fixed cloudbroker/cloudapi/account/update request model types. +- Fixed cloudbroker/cloudapi/rg/update request model types. +- Fixed cloudapi/account DeactivationTime field type. +- Fixed cloudapi/k8s/workersGroupAdd return value type. +- Fixed cloudapi/disks/listUnattached return value type. +- Added ListDisksUnattached model as a cloudapi/disks/listUnattached handler response with filters. +- Fixed cloudapi/extnet Excluded field type. +- Fixed cloudapi/rg RecordResourceUsage model. +- Fixed cloudapi/compute ItemACL model. ### Tests -- Covered CloudAPI/CloudBroker filters with unit tests. - -### Other - -- Updated module to new repository +- Covered cloudapi/disks ListDisksUnattached filters with unit tests. diff --git a/internal/validators/custom.go b/internal/validators/custom.go index 57cd547..6705c43 100644 --- a/internal/validators/custom.go +++ b/internal/validators/custom.go @@ -1,6 +1,9 @@ package validators -import "github.com/go-playground/validator/v10" +import ( + "github.com/go-playground/validator/v10" + "regexp" +) // protoValidator is used to validate Proto fields. func protoValidator(fe validator.FieldLevel) bool { @@ -203,3 +206,12 @@ func sepFieldTypeValidator(fe validator.FieldLevel) bool { return StringInSlice(fieldValue, sepFieldTypeValues) } + +// hwPathValidator is used to validate HWPath field. +func hwPathValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + ok, _ := regexp.MatchString(`^\b[0-9a-f]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.\d{1}$`, fieldValue) + + return ok +} diff --git a/internal/validators/messages.go b/internal/validators/messages.go index 1716ac4..5c59414 100644 --- a/internal/validators/messages.go +++ b/internal/validators/messages.go @@ -187,6 +187,10 @@ func errorMessage(fe validator.FieldError) string { fe.Field(), joinValues(sepFieldTypeValues)) + case "hwPath": + return fmt.Sprintf("%s %s must be in format 0000:1f:2b.0", + prefix, + fe.Field()) } return fe.Error() diff --git a/internal/validators/validator.go b/internal/validators/validator.go index 2cffe16..8a27090 100644 --- a/internal/validators/validator.go +++ b/internal/validators/validator.go @@ -159,5 +159,10 @@ func registerAllValidators(validate *validator.Validate) error { return err } + err = validate.RegisterValidation("hwPath", hwPathValidator) + if err != nil { + return err + } + return nil } diff --git a/pkg/cloudapi/account/models.go b/pkg/cloudapi/account/models.go index c4fa599..5302c63 100644 --- a/pkg/cloudapi/account/models.go +++ b/pkg/cloudapi/account/models.go @@ -165,7 +165,7 @@ type RecordAccount struct { CreatedTime uint64 `json:"createdTime"` // Deactivation time - DeactivationTime uint64 `json:"deactivationTime"` + DeactivationTime float64 `json:"deactivationTime"` // Deleted by DeletedBy string `json:"deletedBy"` diff --git a/pkg/cloudapi/account/update.go b/pkg/cloudapi/account/update.go index 29d00cd..5973f55 100644 --- a/pkg/cloudapi/account/update.go +++ b/pkg/cloudapi/account/update.go @@ -20,23 +20,23 @@ type UpdateRequest struct { // Max size of memory in MB // Required: false - MaxMemoryCapacity uint64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` // Max size of aggregated vdisks in GB // Required: false - MaxVDiskCapacity uint64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` // Max number of CPU cores // Required: false - MaxCPUCapacity uint64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` // Max sent/received network transfer peering // Required: false - MaxNetworkPeerTransfer uint64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` // Max number of assigned public IPs // Required: false - MaxNumPublicIP uint64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` // If true send emails when a user is granted access to resources // Required: false @@ -44,7 +44,7 @@ type UpdateRequest struct { // Limit (positive) or disable (0) GPU resources // Required: false - GPUUnits uint64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` + GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` } // Update updates an account name and resource types and limits diff --git a/pkg/cloudapi/compute/filter_test.go b/pkg/cloudapi/compute/filter_test.go index 3d27871..fb6a9d0 100644 --- a/pkg/cloudapi/compute/filter_test.go +++ b/pkg/cloudapi/compute/filter_test.go @@ -4,7 +4,7 @@ import "testing" var computes = ListComputes{ ItemCompute{ - ACL: []interface{}{}, + ACL: ListACL{}, AccountID: 132847, AccountName: "std_2", AffinityLabel: "", @@ -85,7 +85,7 @@ var computes = ListComputes{ VirtualImageID: 0, }, ItemCompute{ - ACL: []interface{}{}, + ACL: ListACL{}, AccountID: 132848, AccountName: "std_broker", AffinityLabel: "", @@ -150,92 +150,92 @@ var computes = ListComputes{ } func TestFilterByID(t *testing.T) { - actual := computes.FilterByID(48500).FindOne() + actual := computes.FilterByID(48500).FindOne() - if actual.ID != 48500 { - t.Fatal("expected ID 48500, found: ", actual.ID) - } + if actual.ID != 48500 { + t.Fatal("expected ID 48500, found: ", actual.ID) + } - actualEmpty := computes.FilterByID(0) + actualEmpty := computes.FilterByID(0) - if len(actualEmpty) != 0 { - t.Fatal("expected empty, actual: ", len(actualEmpty)) - } + if len(actualEmpty) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty)) + } } func TestFilterByName(t *testing.T) { - actual := computes.FilterByName("test").FindOne() + actual := computes.FilterByName("test").FindOne() - if actual.Name != "test" { - t.Fatal("expected compute with name 'test', found: ", actual.Name) - } + if actual.Name != "test" { + t.Fatal("expected compute with name 'test', found: ", actual.Name) + } } func TestFilterByStatus(t *testing.T) { - actual := computes.FilterByStatus("ENABLED") + actual := computes.FilterByStatus("ENABLED") - for _, item := range actual { - if item.Status != "ENABLED" { - t.Fatal("expected ENABLED status, found: ", item.Status) - } - } + for _, item := range actual { + if item.Status != "ENABLED" { + t.Fatal("expected ENABLED status, found: ", item.Status) + } + } } func TestFilterByTechStatus(t *testing.T) { - actual := computes.FilterByTechStatus("STARTED").FindOne() + actual := computes.FilterByTechStatus("STARTED").FindOne() - if actual.ID != 48556 { - t.Fatal("expected 48556 with STARTED techStatus, found: ", actual.ID) - } + if actual.ID != 48556 { + t.Fatal("expected 48556 with STARTED techStatus, found: ", actual.ID) + } } func TestFilterByDiskID(t *testing.T) { - actual := computes.FilterByDiskID(65248).FindOne() + actual := computes.FilterByDiskID(65248).FindOne() - if actual.ID != 48556 { - t.Fatal("expected 48556 with DiskID 65248, found: ", actual.ID) - } + if actual.ID != 48556 { + t.Fatal("expected 48556 with DiskID 65248, found: ", actual.ID) + } } func TestFilterFunc(t *testing.T) { - actual := computes.FilterFunc(func(ic ItemCompute) bool { - return ic.Registered == true - }) - - if len(actual) != 2 { - t.Fatal("expected 2 elements found, actual: ", len(actual)) - } - - for _, item := range actual { - if item.Registered != true { - t.Fatal("expected Registered to be true, actual: ", item.Registered) - } - } + actual := computes.FilterFunc(func(ic ItemCompute) bool { + return ic.Registered == true + }) + + if len(actual) != 2 { + t.Fatal("expected 2 elements found, actual: ", len(actual)) + } + + for _, item := range actual { + if item.Registered != true { + t.Fatal("expected Registered to be true, actual: ", item.Registered) + } + } } func TestSortingByCreatedTime(t *testing.T) { - actual := computes.SortByCreatedTime(false) + actual := computes.SortByCreatedTime(false) - if actual[0].Name != "test" { - t.Fatal("expected 'test', found: ", actual[0].Name) - } + if actual[0].Name != "test" { + t.Fatal("expected 'test', found: ", actual[0].Name) + } - actual = computes.SortByCreatedTime(true) - if actual[0].Name != "compute_2" { - t.Fatal("expected 'compute_2', found: ", actual[0].Name) - } + actual = computes.SortByCreatedTime(true) + if actual[0].Name != "compute_2" { + t.Fatal("expected 'compute_2', found: ", actual[0].Name) + } } func TestSortingByCPU(t *testing.T) { - actual := computes.SortByCPU(false) + actual := computes.SortByCPU(false) - if actual[0].CPU != 4{ - t.Fatal("expected 4 CPU cores, found: ", actual[0].CPU) - } + if actual[0].CPU != 4 { + t.Fatal("expected 4 CPU cores, found: ", actual[0].CPU) + } - actual = computes.SortByCPU(true) + actual = computes.SortByCPU(true) - if actual[0].CPU != 6 { - t.Fatal("expected 6 CPU cores, found: ", actual[0].CPU) - } + if actual[0].CPU != 6 { + t.Fatal("expected 6 CPU cores, found: ", actual[0].CPU) + } } diff --git a/pkg/cloudapi/compute/models.go b/pkg/cloudapi/compute/models.go index 82192eb..8d31cfc 100644 --- a/pkg/cloudapi/compute/models.go +++ b/pkg/cloudapi/compute/models.go @@ -1,5 +1,7 @@ package compute +import "strconv" + // Access Control List type RecordACL struct { // Account ACL list @@ -12,10 +14,27 @@ type RecordACL struct { RGACL ListACL `json:"rgAcl"` } +type Explicit bool + +func (e *Explicit) UnmarshalJSON(b []byte) error { + if b[0] == '"' { + b = b[1 : len(b)-1] + } + + res, err := strconv.ParseBool(string(b)) + if err != nil { + return err + } + + *e = Explicit(res) + + return nil +} + // ACL information type ItemACL struct { // Explicit - Explicit bool `json:"explicit"` + Explicit Explicit `json:"explicit"` // GUID GUID string `json:"guid"` @@ -709,8 +728,7 @@ type IOTune struct { // Main information about compute type ItemCompute struct { // Access Control List - ACL []interface{} `json:"acl"` - + ACL ListACL `json:"acl"` // Account ID AccountID uint64 `json:"accountId"` diff --git a/pkg/cloudapi/disks/filter.go b/pkg/cloudapi/disks/filter.go index 3a69c4b..8e568f8 100644 --- a/pkg/cloudapi/disks/filter.go +++ b/pkg/cloudapi/disks/filter.go @@ -130,3 +130,62 @@ func (ld ListDisks) FindOne() ItemDisk { return ld[0] } + +// FilterByID returns ListDisksUnattached with specified ID. +func (lu ListDisksUnattached) FilterByID(id uint64) ListDisksUnattached { + predicate := func(idisk ItemDiskUnattached) bool { + return idisk.ID == id + } + + return lu.FilterFunc(predicate) +} + +// FilterByName returns ListDisksUnattached with specified Name. +func (lu ListDisksUnattached) FilterByName(name string) ListDisksUnattached { + predicate := func(idisk ItemDiskUnattached) bool { + return idisk.Name == name + } + + return lu.FilterFunc(predicate) +} + +// FilterByStatus returns ListDisksUnattached with specified Status. +func (lu ListDisksUnattached) FilterByStatus(status string) ListDisksUnattached { + predicate := func(idisk ItemDiskUnattached) bool { + return idisk.Status == status + } + + return lu.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListDisksUnattached with specified TechStatus. +func (lu ListDisksUnattached) FilterByTechStatus(techStatus string) ListDisksUnattached { + predicate := func(idisk ItemDiskUnattached) bool { + return idisk.TechStatus == techStatus + } + + return lu.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListDisksUnattached based on a user-specified predicate. +func (lu ListDisksUnattached) FilterFunc(predicate func(ItemDiskUnattached) bool) ListDisksUnattached { + var result ListDisksUnattached + + for _, item := range lu { + if predicate(item) { + result = append(result, item) + } + } + + return result +} + +// FindOne returns first found ItemDiskUnattached +// If none was found, returns an empty struct. +func (lu ListDisksUnattached) FindOne() ItemDiskUnattached { + if len(lu) == 0 { + return ItemDiskUnattached{} + } + + return lu[0] +} diff --git a/pkg/cloudapi/disks/filter_test.go b/pkg/cloudapi/disks/filter_test.go index aea6141..3bd6462 100644 --- a/pkg/cloudapi/disks/filter_test.go +++ b/pkg/cloudapi/disks/filter_test.go @@ -1,6 +1,8 @@ package disks -import "testing" +import ( + "testing" +) var disks = ListDisks{ ItemDisk{ @@ -175,3 +177,198 @@ func TestSortByCreatedTime(t *testing.T) { t.Fatal("expected ID 65193, found: ", actual[0].ID) } } + +var unattachedDisks = ListDisksUnattached{ + { + CKey: "", + Meta: []interface{}{ + "cloudbroker", + "disk", + 1, + }, + AccountID: 149, + AccountName: "test_account1", + ACL: map[string]interface{}{}, + BootPartition: 0, + CreatedTime: 1681477547, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + DiskPath: "", + GID: 2002, + GUID: 22636, + ID: 22636, + ImageID: 0, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + IQN: "", + Login: "", + Milestones: 43834, + Name: "test_disk", + Order: 0, + Params: "", + ParentID: 0, + Password: "", + PCISlot: -1, + Pool: "data05", + PresentTo: []uint64{}, + PurgeAttempts: 0, + PurgeTime: 0, + RealityDeviceNumber: 0, + ReferenceID: "", + ResID: "79bd3bd8-3424-48d3-963f-1870d506f169", + ResName: "volumes/volume_22636", + Role: "", + SEPID: 1, + Shareable: false, + SizeMax: 0, + SizeUsed: 0, + Snapshots: nil, + Status: "CREATED", + TechStatus: "ALLOCATED", + Type: "D", + VMID: 0, + }, + { + CKey: "", + Meta: []interface{}{ + "cloudbroker", + "disk", + 1, + }, + AccountID: 150, + AccountName: "test_account", + ACL: map[string]interface{}{}, + BootPartition: 0, + CreatedTime: 1681477558, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + DiskPath: "", + GID: 2002, + GUID: 22637, + ID: 22637, + ImageID: 0, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + IQN: "", + Login: "", + Milestones: 43834, + Name: "test_disk", + Order: 0, + Params: "", + ParentID: 0, + Password: "", + PCISlot: -1, + Pool: "data05", + PresentTo: []uint64{ + 27, + 27, + }, + PurgeAttempts: 0, + PurgeTime: 0, + RealityDeviceNumber: 0, + ReferenceID: "", + ResID: "79bd3bd8-3424-48d3-963f-1870d506f169", + ResName: "volumes/volume_22637", + Role: "", + SEPID: 1, + Shareable: false, + SizeMax: 0, + SizeUsed: 0, + Snapshots: nil, + Status: "CREATED", + TechStatus: "ALLOCATED", + Type: "B", + VMID: 0, + }, +} + +func TestListDisksUnattached_FilterByID(t *testing.T) { + actual := unattachedDisks.FilterByID(22636) + + if len(actual) == 0 { + t.Fatal("No elements were found") + } + + actualItem := actual.FindOne() + + if actualItem.ID != 22636 { + t.Fatal("expected ID 22636, found: ", actualItem.ID) + } +} + +func TestListDisksUnattached_FilterByName(t *testing.T) { + actual := unattachedDisks.FilterByName("test_disk") + + if len(actual) != 2 { + t.Fatal("expected 2 elements, found: ", len(actual)) + } + + for _, item := range actual { + if item.Name != "test_disk" { + t.Fatal("expected 'test_disk' name, found: ", item.Name) + } + } +} + +func TestListDisksUnattached_FilterByStatus(t *testing.T) { + actual := unattachedDisks.FilterByStatus("CREATED") + + if len(actual) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual { + if item.Status != "CREATED" { + t.Fatal("expected 'CREATED' status, found: ", item.Status) + } + } +} + +func TestListDisksUnattached_FilterByTechStatus(t *testing.T) { + actual := unattachedDisks.FilterByTechStatus("ALLOCATED") + + if len(actual) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual { + if item.TechStatus != "ALLOCATED" { + t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus) + } + } +} + +func TestListDisksUnattached_FilterFunc(t *testing.T) { + actual := unattachedDisks.FilterFunc(func(id ItemDiskUnattached) bool { + return len(id.PresentTo) == 2 + }) + + if len(actual) == 0 { + t.Fatal("No elements were found") + } + + if len(actual[0].PresentTo) != 2 { + t.Fatal("expected 2 elements in PresentTo, found: ", len(actual[0].PresentTo)) + } +} + +func TestListDisksUnattached_SortByCreatedTime(t *testing.T) { + actual := unattachedDisks.SortByCreatedTime(false) + + if actual[0].ID != 22636 { + t.Fatal("expected ID 22636, found: ", actual[0].ID) + } + + actual = unattachedDisks.SortByCreatedTime(true) + + if actual[0].ID != 22637 { + t.Fatal("expected ID 22637, found: ", actual[0].ID) + } + +} diff --git a/pkg/cloudapi/disks/list_unattached.go b/pkg/cloudapi/disks/list_unattached.go index accb6be..47ca667 100644 --- a/pkg/cloudapi/disks/list_unattached.go +++ b/pkg/cloudapi/disks/list_unattached.go @@ -14,7 +14,7 @@ type ListUnattachedRequest struct { } // ListUnattached gets list of unattached disks -func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (ListDisks, error) { +func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (ListDisksUnattached, error) { url := "/cloudapi/disks/listUnattached" res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) @@ -22,7 +22,7 @@ func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (L return nil, err } - list := ListDisks{} + list := ListDisksUnattached{} err = json.Unmarshal(res, &list) if err != nil { diff --git a/pkg/cloudapi/disks/models.go b/pkg/cloudapi/disks/models.go index b13515e..1ef3894 100644 --- a/pkg/cloudapi/disks/models.go +++ b/pkg/cloudapi/disks/models.go @@ -114,9 +114,146 @@ type ItemDisk struct { VMID uint64 `json:"vmid"` } +type ItemDiskUnattached struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control List + ACL map[string]interface{} `json:"acl"` + + // Boot Partition + BootPartition uint64 `json:"bootPartition"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Destruction time + DestructionTime uint64 `json:"destructionTime"` + + // Disk path + DiskPath string `json:"diskPath"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Images + Images []uint64 `json:"images"` + + // IOTune + IOTune IOTune `json:"iotune"` + + // IQN + IQN string `json:"iqn"` + + // Login + Login string `json:"login"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Order + Order uint64 `json:"order"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // Password + Password string `json:"passwd"` + + //PCISlot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Purge attempts + PurgeAttempts uint64 `json:"purgeAttempts"` + + // Purge time + PurgeTime uint64 `json:"purgeTime"` + + // Reality device number + RealityDeviceNumber uint64 `json:"realityDeviceNumber"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Resource ID + ResID string `json:"resId"` + + // Resource name + ResName string `json:"resName"` + + // Role + Role string `json:"role"` + + // ID SEP + SEPID uint64 `json:"sepId"` + + // Shareable + Shareable bool `json:"shareable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List of snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` +} + // List of disks type ListDisks []ItemDisk +// List of unattached disks +type ListDisksUnattached []ItemDiskUnattached + // Main information about snapshot type ItemSnapshot struct { // GUID diff --git a/pkg/cloudapi/disks/serialize.go b/pkg/cloudapi/disks/serialize.go index 2de9476..cf4ae08 100644 --- a/pkg/cloudapi/disks/serialize.go +++ b/pkg/cloudapi/disks/serialize.go @@ -41,3 +41,39 @@ func (idisk ItemDisk) Serialize(params ...string) (serialization.Serialized, err return json.Marshal(idisk) } + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lu ListDisksUnattached) Serialize(params ...string) (serialization.Serialized, error) { + if len(lu) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lu, prefix, indent) + } + + return json.Marshal(lu) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (idisk ItemDiskUnattached) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(idisk, prefix, indent) + } + + return json.Marshal(idisk) +} diff --git a/pkg/cloudapi/disks/sorting.go b/pkg/cloudapi/disks/sorting.go index 56891f9..8b34498 100644 --- a/pkg/cloudapi/disks/sorting.go +++ b/pkg/cloudapi/disks/sorting.go @@ -58,3 +58,60 @@ func (ld ListDisks) SortByDeletedTime(inverse bool) ListDisks { return ld } + +// SortByCreatedTime sorts ListDisksUnattached by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lu ListDisksUnattached) SortByCreatedTime(inverse bool) ListDisksUnattached { + if len(lu) < 2 { + return lu + } + + sort.Slice(lu, func(i, j int) bool { + if inverse { + return lu[i].CreatedTime > lu[j].CreatedTime + } + + return lu[i].CreatedTime < lu[j].CreatedTime + }) + + return lu +} + +// SortByDestructionTime sorts ListDisksUnattached by the DestructionTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lu ListDisksUnattached) SortByDestructionTime(inverse bool) ListDisksUnattached { + if len(lu) < 2 { + return lu + } + + sort.Slice(lu, func(i, j int) bool { + if inverse { + return lu[i].DestructionTime > lu[j].DestructionTime + } + + return lu[i].DestructionTime < lu[j].DestructionTime + }) + + return lu +} + +// SortByDeletedTime sorts ListDisksUnattached by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lu ListDisksUnattached) SortByDeletedTime(inverse bool) ListDisksUnattached { + if len(lu) < 2 { + return lu + } + + sort.Slice(lu, func(i, j int) bool { + if inverse { + return lu[i].DeletedTime > lu[j].DeletedTime + } + + return lu[i].DeletedTime < lu[j].DeletedTime + }) + + return lu +} diff --git a/pkg/cloudapi/extnet/models.go b/pkg/cloudapi/extnet/models.go index d6ef6f4..29505b7 100644 --- a/pkg/cloudapi/extnet/models.go +++ b/pkg/cloudapi/extnet/models.go @@ -107,6 +107,23 @@ type VNFs struct { DHCP uint64 `json:"dhcp"` } +type Excluded struct { + // ClientType + ClientType string `json:"clientType"` + + // IP + IP string `json:"ip"` + + // MAC + MAC string `json:"mac"` + + // Type + Type string `json:"type"` + + // VMID + VMID uint64 `json:"vmId"` +} + // Detailed information about external network type RecordExtNet struct { // CKey @@ -134,7 +151,7 @@ type RecordExtNet struct { DNS []string `json:"dns"` // Excluded - Excluded []string `json:"excluded"` + Excluded []Excluded `json:"excluded"` // Free IPs FreeIPs uint64 `json:"free_ips"` diff --git a/pkg/cloudapi/k8s/workers_group_add.go b/pkg/cloudapi/k8s/workers_group_add.go index 9c561ac..acb421b 100644 --- a/pkg/cloudapi/k8s/workers_group_add.go +++ b/pkg/cloudapi/k8s/workers_group_add.go @@ -59,11 +59,11 @@ type WorkersGroupAddRequest struct { } // WorkersGroupAdd adds workers group to Kubernetes cluster -func (k8s K8S) WorkersGroupAdd(ctx context.Context, req WorkersGroupAddRequest) (bool, error) { +func (k8s K8S) WorkersGroupAdd(ctx context.Context, req WorkersGroupAddRequest) (uint64, error) { err := validators.ValidateRequest(req) if err != nil { for _, validationError := range validators.GetErrors(err) { - return false, validators.ValidationError(validationError) + return 0, validators.ValidationError(validationError) } } @@ -71,12 +71,12 @@ func (k8s K8S) WorkersGroupAdd(ctx context.Context, req WorkersGroupAddRequest) res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) if err != nil { - return false, err + return 0, err } - result, err := strconv.ParseBool(string(res)) + result, err := strconv.ParseUint(string(res), 10, 64) if err != nil { - return false, err + return 0, err } return result, nil diff --git a/pkg/cloudapi/locations/filter.go b/pkg/cloudapi/locations/filter.go index 97f132d..886496a 100644 --- a/pkg/cloudapi/locations/filter.go +++ b/pkg/cloudapi/locations/filter.go @@ -18,6 +18,15 @@ func (ll ListLocations) FilterByName(name string) ListLocations { return ll.FilterFunc(predicate) } +// FilterByGID returns ListLocations with specified GID. +func (ll ListLocations) FilterByGID(gid uint64) ListLocations { + predicate := func(il ItemLocation) bool { + return il.GID == gid + } + + return ll.FilterFunc(predicate) +} + // FilterFunc allows filtering ListLocations based on a user-specified predicate. func (ll ListLocations) FilterFunc(predicate func(ItemLocation) bool) ListLocations { var result ListLocations diff --git a/pkg/cloudapi/rg/models.go b/pkg/cloudapi/rg/models.go index 57c9290..677e1df 100644 --- a/pkg/cloudapi/rg/models.go +++ b/pkg/cloudapi/rg/models.go @@ -735,7 +735,10 @@ type RecordResourceUsage struct { CPU uint64 `json:"cpu"` // Disk size - DiskSize uint64 `json:"disksize"` + DiskSize float64 `json:"disksize"` + + // Max disk size + DiskSizeMax uint64 `json:"disksizemax"` // Number of external IPs ExtIPs uint64 `json:"extips"` @@ -748,4 +751,7 @@ type RecordResourceUsage struct { // Number of RAM RAM uint64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` } diff --git a/pkg/cloudapi/rg/update.go b/pkg/cloudapi/rg/update.go index afe8933..7f7e390 100644 --- a/pkg/cloudapi/rg/update.go +++ b/pkg/cloudapi/rg/update.go @@ -24,23 +24,23 @@ type UpdateRequest struct { // Max size of memory in MB // Required: false - MaxMemoryCapacity uint64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` // Max size of aggregated virtual disks in GB // Required: false - MaxVDiskCapacity uint64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` // Max number of CPU cores // Required: false - MaxCPUCapacity uint64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` // Max sent/received network transfer peering // Required: false - MaxNetworkPeerTransfer uint64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` // Max number of assigned public IPs // Required: false - MaxNumPublicIP uint64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` // Register computes in registration system // Required: false diff --git a/pkg/cloudbroker/account/update.go b/pkg/cloudbroker/account/update.go index 19df96a..75853f4 100644 --- a/pkg/cloudbroker/account/update.go +++ b/pkg/cloudbroker/account/update.go @@ -15,8 +15,8 @@ type UpdateRequest struct { AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` // Display name - // Required: true - Name string `url:"name" json:"name" validate:"required"` + // Required: false + Name string `url:"name" json:"name"` // Name of the account // Required: true @@ -24,27 +24,27 @@ type UpdateRequest struct { // Email // Required: false - EmailAddress string `url:"emailaddress,omitempty" json:"emailaddress,omitempty" validate:"omitempty,email"` + EmailAddress string `url:"emailaddress,omitempty" json:"emailaddress,omitempty" validate:"omitempty,email"` // Max size of memory in MB // Required: false - MaxMemoryCapacity uint64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` // Max size of aggregated vdisks in GB // Required: false - MaxVDiskCapacity uint64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` // Max number of CPU cores // Required: false - MaxCPUCapacity uint64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` // Max sent/received network transfer peering // Required: false - MaxNetworkPeerTransfer uint64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` // Max number of assigned public IPs // Required: false - MaxNumPublicIP uint64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` // If true send emails when a user is granted access to resources // Required: false @@ -52,7 +52,7 @@ type UpdateRequest struct { // Limit (positive) or disable (0) GPU resources // Required: false - GPUUnits uint64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` + GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` // List of strings with pools // i.e.: ["sep1_poolName1", "sep2_poolName2", etc] diff --git a/pkg/cloudbroker/pcidevice.go b/pkg/cloudbroker/pcidevice.go new file mode 100644 index 0000000..0716016 --- /dev/null +++ b/pkg/cloudbroker/pcidevice.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/pcidevice" + +// Accessing the PCI Device method group +func (cb *CloudBroker) PCIDevice() *pcidevice.PCIDevice { + return pcidevice.New(cb.client) +} diff --git a/pkg/cloudbroker/pcidevice/create.go b/pkg/cloudbroker/pcidevice/create.go new file mode 100644 index 0000000..ab00e77 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/create.go @@ -0,0 +1,56 @@ +package pcidevice + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// Request struct for creating PCI device +type CreateRequest struct { + // StackID + // Required: true + StackID uint64 `url:"stackId" json:"stackId" validate:"required"` + + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of device + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // PCI address of the device + // Must be in format 0000:1f:2b.0 + // Required: true + HWPath string `url:"hwPath" json:"hwPath" validate:"required,hwPath"` + + // Description, just for information + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` +} + +// Create creates PCI Device +func (p PCIDevice) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + for _, validationError := range validators.GetErrors(err) { + return 0, validators.ValidationError(validationError) + } + } + + url := "/cloudbroker/pcidevice/create" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/pcidevice/delete.go b/pkg/cloudbroker/pcidevice/delete.go new file mode 100644 index 0000000..730acba --- /dev/null +++ b/pkg/cloudbroker/pcidevice/delete.go @@ -0,0 +1,43 @@ +package pcidevice + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// Request struct for deleting PCI device +type DeleteRequest struct { + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` + + // Force delete + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Delete PCI device +func (p PCIDevice) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + for _, validationError := range validators.GetErrors(err) { + return false, validators.ValidationError(validationError) + } + } + + url := "/cloudbroker/pcidevice/delete" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/pcidevice/disable.go b/pkg/cloudbroker/pcidevice/disable.go new file mode 100644 index 0000000..dd35ff1 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/disable.go @@ -0,0 +1,43 @@ +package pcidevice + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// Request struct for disabling PCI device +type DisableRequest struct { + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` + + // Force delete + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Disable PCI device +func (p PCIDevice) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + for _, validationError := range validators.GetErrors(err) { + return false, validators.ValidationError(validationError) + } + } + + url := "/cloudbroker/pcidevice/disable" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/pcidevice/enable.go b/pkg/cloudbroker/pcidevice/enable.go new file mode 100644 index 0000000..e2c36f8 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/enable.go @@ -0,0 +1,39 @@ +package pcidevice + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// Request struct for enabling PCI device +type EnableRequest struct { + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` +} + +// Enable PCI device +func (p PCIDevice) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + for _, validationError := range validators.GetErrors(err) { + return false, validators.ValidationError(validationError) + } + } + + url := "/cloudbroker/pcidevice/enable" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/pcidevice/list.go b/pkg/cloudbroker/pcidevice/list.go new file mode 100644 index 0000000..a799878 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/list.go @@ -0,0 +1,26 @@ +package pcidevice + +import ( + "context" + "encoding/json" + "net/http" +) + +// List gets list all pci devices +func (p PCIDevice) List(ctx context.Context) (ListPCIDevices, error) { + url := "/cloudbroker/pcidevice/list" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + list := ListPCIDevices{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/pcidevice/models.go b/pkg/cloudbroker/pcidevice/models.go new file mode 100644 index 0000000..300feb6 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/models.go @@ -0,0 +1,43 @@ +package pcidevice + +// Main information about PCI device +type ItemPCIDevice struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Description + Description string `json:"description"` + + // GUID + GUID uint64 `json:"guid"` + + // HwPath + HwPath string `json:"hwPath"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // Status + Status string `json:"status"` + + // System name + SystemName string `json:"systemName"` +} + +// List PCI devices +type ListPCIDevices []ItemPCIDevice diff --git a/pkg/cloudbroker/pcidevice/pcidevice.go b/pkg/cloudbroker/pcidevice/pcidevice.go new file mode 100644 index 0000000..27054c0 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/pcidevice.go @@ -0,0 +1,15 @@ +package pcidevice + +import "repository.basistech.ru/BASIS/decort-golang-sdk/interfaces" + +// Structure for creating request to PCI device +type PCIDevice struct { + client interfaces.Caller +} + +// Builder for PCI device endpoints +func New(client interfaces.Caller) *PCIDevice { + return &PCIDevice{ + client: client, + } +} diff --git a/pkg/cloudbroker/pcidevice/serialize.go b/pkg/cloudbroker/pcidevice/serialize.go new file mode 100644 index 0000000..6a2bc35 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/serialize.go @@ -0,0 +1,42 @@ +package pcidevice + +import ( + "encoding/json" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (l ListPCIDevices) Serialize(params ...string) (serialization.Serialized, error) { + if len(l) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(l, prefix, indent) + } + + return json.Marshal(l) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (i ItemPCIDevice) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(i, prefix, indent) + } + + return json.Marshal(i) +} diff --git a/pkg/cloudbroker/rg/update.go b/pkg/cloudbroker/rg/update.go index 2208a97..a7772be 100644 --- a/pkg/cloudbroker/rg/update.go +++ b/pkg/cloudbroker/rg/update.go @@ -24,23 +24,23 @@ type UpdateRequest struct { // Max size of memory in MB // Required: false - MaxMemoryCapacity uint64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` // Max size of aggregated virtual disks in GB // Required: false - MaxVDiskCapacity uint64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` // Max number of CPU cores // Required: false - MaxCPUCapacity uint64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` // Max sent/received network transfer peering // Required: false - MaxNetworkPeerTransfer uint64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` // Max number of assigned public IPs // Required: false - MaxNumPublicIP uint64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` // Register computes in registration system // Required: false diff --git a/pkg/cloudbroker/vgpu.go b/pkg/cloudbroker/vgpu.go new file mode 100644 index 0000000..c651e3e --- /dev/null +++ b/pkg/cloudbroker/vgpu.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/vgpu" + +// Accessing the VGPU method group +func (cb *CloudBroker) VGPU() *vgpu.VGPU { + return vgpu.New(cb.client) +} diff --git a/pkg/cloudbroker/vgpu/allocate.go b/pkg/cloudbroker/vgpu/allocate.go new file mode 100644 index 0000000..30091a2 --- /dev/null +++ b/pkg/cloudbroker/vgpu/allocate.go @@ -0,0 +1,39 @@ +package vgpu + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// Request for allocating VGPU +type AllocateRequest struct { + // Virtual GPU ID + // Required: true + VGPUID uint64 `url:"vgpuId" json:"vgpuId" validate:"required"` +} + +// Allocate allocates GPU +func (v VGPU) Allocate(ctx context.Context, req AllocateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + for _, validationError := range validators.GetErrors(err) { + return false, validators.ValidationError(validationError) + } + } + + url := "/cloudbroker/vgpu/allocate" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vgpu/create.go b/pkg/cloudbroker/vgpu/create.go new file mode 100644 index 0000000..c0c0a3e --- /dev/null +++ b/pkg/cloudbroker/vgpu/create.go @@ -0,0 +1,51 @@ +package vgpu + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// Request struct for creating VGPU +type CreateRequest struct { + // ID of pGPU + // Required: true + PGPUID uint64 `url:"pgpuId" json:"pgpuId" validate:"required"` + + // ID of the target resource group. + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Virtual profile id + // Required: false + ProfileID uint64 `url:"profileId,omitempty" json:"profileId,omitempty"` + + // Allocate vgpu after creation + // Required: false + Allocate bool `url:"allocate,omitempty" json:"allocate,omitempty"` +} + +// Create creates VGPU +func (v VGPU) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + for _, validationError := range validators.GetErrors(err) { + return 0, validators.ValidationError(validationError) + } + } + + url := "/cloudbroker/vgpu/create" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vgpu/deallocate.go b/pkg/cloudbroker/vgpu/deallocate.go new file mode 100644 index 0000000..0fc627e --- /dev/null +++ b/pkg/cloudbroker/vgpu/deallocate.go @@ -0,0 +1,43 @@ +package vgpu + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// Request for deallocating VGPU +type DeallocateRequest struct { + // Virtual GPU ID + // Required: true + VGPUID uint64 `url:"vgpuId" json:"vgpuId" validate:"required"` + + // Force delete (detach from compute) + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Deallocate releases GPU resources +func (v VGPU) Deallocate(ctx context.Context, req DeallocateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + for _, validationError := range validators.GetErrors(err) { + return false, validators.ValidationError(validationError) + } + } + + url := "/cloudbroker/vgpu/deallocate" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vgpu/destroy.go b/pkg/cloudbroker/vgpu/destroy.go new file mode 100644 index 0000000..5db1ab6 --- /dev/null +++ b/pkg/cloudbroker/vgpu/destroy.go @@ -0,0 +1,43 @@ +package vgpu + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// Request for destroying VGPU +type DestroyRequest struct { + // Virtual GPU ID + // Required: true + VGPUID uint64 `url:"vgpuId" json:"vgpuId" validate:"required"` + + // Force delete (deallocate and detach from compute) + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Destroy destroys VGPU +func (v VGPU) Destroy(ctx context.Context, req DestroyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + for _, validationError := range validators.GetErrors(err) { + return false, validators.ValidationError(validationError) + } + } + + url := "/cloudbroker/vgpu/destroy" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vgpu/list.go b/pkg/cloudbroker/vgpu/list.go new file mode 100644 index 0000000..63d91f9 --- /dev/null +++ b/pkg/cloudbroker/vgpu/list.go @@ -0,0 +1,37 @@ +package vgpu + +import ( + "context" + "encoding/json" + "net/http" +) + +// Request struct for getting list of VGPU +type ListRequest struct { + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list all VGPU +func (v VGPU) List(ctx context.Context, req ListRequest) (ListVGPU, error) { + url := "/cloudbroker/vgpu/list" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVGPU{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/vgpu/models.go b/pkg/cloudbroker/vgpu/models.go new file mode 100644 index 0000000..2e1c89e --- /dev/null +++ b/pkg/cloudbroker/vgpu/models.go @@ -0,0 +1,66 @@ +package vgpu + +type ItemVGPU struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + //Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // VGPU ID + ID uint64 `json:"id"` + + // Last claimed by + LastClaimedBy uint64 `json:"lastClaimedBy"` + + // Last update time + LastUpdateTime uint64 `json:"lastUpdateTime"` + + // Mode + Mode string `json:"mode"` + + // PCI Slot + PCISlot interface{} `json:"pciSlot"` + + // PGPUID + PGPUID uint64 `json:"pgpuid"` + + // Profile ID + ProfileID interface{} `json:"profileId"` + + // RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID interface{} `json:"referenceId"` + + // RGID + RGID uint64 `json:"rgId"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // VMID + VMID uint64 `json:"vmid"` +} + +// List of VGPU +type ListVGPU []ItemVGPU diff --git a/pkg/cloudbroker/vgpu/serialize.go b/pkg/cloudbroker/vgpu/serialize.go new file mode 100644 index 0000000..64e3475 --- /dev/null +++ b/pkg/cloudbroker/vgpu/serialize.go @@ -0,0 +1,42 @@ +package vgpu + +import ( + "encoding/json" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (l ListVGPU) Serialize(params ...string) (serialization.Serialized, error) { + if len(l) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(l, prefix, indent) + } + + return json.Marshal(l) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (i ItemVGPU) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(i, prefix, indent) + } + + return json.Marshal(i) +} diff --git a/pkg/cloudbroker/vgpu/vgpu.go b/pkg/cloudbroker/vgpu/vgpu.go new file mode 100644 index 0000000..cb54896 --- /dev/null +++ b/pkg/cloudbroker/vgpu/vgpu.go @@ -0,0 +1,15 @@ +package vgpu + +import "repository.basistech.ru/BASIS/decort-golang-sdk/interfaces" + +// Structure for creating request to VGPU +type VGPU struct { + client interfaces.Caller +} + +// Builder for VGPU endpoints +func New(client interfaces.Caller) *VGPU { + return &VGPU{ + client: client, + } +}