diff --git a/CHANGELOG.md b/CHANGELOG.md index 03ee6f3..c79b0c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,197 +1,32 @@ -## Version 1.8.0 +## Version 1.8.1 ### Feature -#### account: -- Add endpoints GrantAccessTemplates, ListAvailableTemplates, RevokeAccessTemplates in cloudbroker/account -- Add SortBy field for list groups and validation functionality -- Add ComputeFeatures field to RecordAccount and ItemAccount models in cloudapi/account -- Add ComputeFeatures field to InfoAccount and CreateRequest model in cloudbroker/account -- Add UpdateComputeFeatures endpoint in cloudbroker/account -- Add ExtnetId, FreeIPs fields to model ItemVINS in cloudapi/account and cloudbroker/account - -#### audit: -- Add field GUID in model ItemLinkedJobs cloudbroker/audit -- Delete field StatusCode in model ListRequest cloudbroker/audit -- Add fields MinStatusCode and MaxStatusCode in model ListRequest cloudbroker/audit - -#### bservice: -- Add validation of RAM to be divisible by 128 in cloudapi/bservice - -#### compute: -- Add field CdImageId in models ItemCompute and RecordCompute in cloudapi/compute and cloudbroker/compute -- Add fields NatableVINSID, NatableVINSIP, NatableVINSName, NatableVINSNetwork, NatableVINSNetworkName in models RecordCompute in cloudbroker/compute -- Set field LocalBasePort in model PFWAddRequest as optional in cloudapi/compute and cloudbroker/compute -- Add SortBy field for list groups and validation functionality -- Add HPBacked, CPUPin, NumaAffinity fields to RecordCompute and InfoCompute models in cloudbroker/compute -- Add HPBacked, CPUPin, NumaAffinity fields to RecordCompute and ItemCompute models in cloudapi/compute -- Add validation of RAM to be divisible by 128 in cloudapi/compute and cloudbroker/compute -- Add field NumaNodeId in models RecordCompute and ItemCompute in cloudapi/compute and cloudbroker/compute -- Add BootDiskSet endpoints in cloudapi/compute and cloudbroker/compute -- Add endpoints DiskSwitchToReplication, DiskMigrate in cloudapi/compute and cloudbroker/compute -- Add field Replication in model RecordCompute.Disks.ItemComputeDisk in cloudapi/compute and cloudbroker/compute -- Add endpoint getCustomFields in cloudbroker/compute -- Delete field AffinityLabel in model AffinityRelationsRequest in cloudbroker/compute -- Add fields ImageName and VirtualImageName, delete VINSConnected fields in RecordCompute model in cloudbroker/compute -- Add fields Enabled and NodeID in ItemInterface model in cloudbroker/compute. Add NodeID field in ItemVNFInterface model in cloudapi/compute - -#### disks: -- Add endpoints Replicate, ReplicationResume, ReplicationReverse, ReplicationStart, ReplicationStop, ReplicationStatus, ReplicationSuspend in cloudapi/disks and cloudbroker/disks -- Add field Replication in models RecordDisk and ItemDisk in cloudapi/disks and cloudbroker/disks -- Add CreateTemplateFromBlank and CreateTemplateFromBlankAsync endpoints in cloudapi/compute and cloudbroker/compute -- Add fields Page and Size in model SearchRequest in cloudapi/disks -- Add FromPlatformDisk and FromPlatformDiskAsync endpoints in cloudapi/disks and cloudbroker/disks - -#### flipgroup: -- Add fields ConnId, Status, AccountId to model ListRequest in cloudapi/flipgroup and cloudbroker/flipgroup -- Add SortBy field for list groups and validation functionality - -#### grid: -- Add endpoints addCustomBackupPath, removeCustomBackupPath, setPasswordPolicy in cloudbroker/grid - -#### image: -- Add field CdPresentedTo in model RecordImage in cloudapi/image cloudbroker/image -- Set field AccountId in CreateRequest in cloudapi/image as required -- Add endpoints GrantAccess, RevokeAccess in cloudbroker/image -- Add SortBy field for list groups and validation functionality -- Add field NetworkInterfaceNaming in model CreateRequest in cloudapi/image cloudbroker/image, in model EditRequest in cloudbroker/image, in models ItemImage and RecordImage in cloudapi/image and cloudbroker/image -- Add validation field NetworkInterfaceNaming in cloudapi/image cloudbroker/image -- Delete field GID in model CreateRequest in cloudapi/image -- Delete field Meta and Ckey in model and RecordImage in cloudbroker/image -- Add model ItemImage in cloudbroker/image - -#### k8s: -- Add field LbSysctlParams in model CreateRequest in cloudapi/k8s/create and cloudbroker/k8s/create -- Add validation of RAM to be divisible by 128 in cloudapi/k8s and cloudbroker/k8s -- Set uint64 as MasterRAM and WorkerRAM types in CreateRequest struct in cloudapi/k8s and cloudbroker/k8s - -#### kvmppc -- Add validation of RAM to be divisible by 128 in cloudapi/kvmppc and cloudbroker/kvmppc -- Add DataDisks field to CreateRequest and CreateBlankRequest in cloudapi/kvmppc and cloudbroker/kvmppc -- Add DataDisks field to MassCreateRequest in cloudbroker/kvmppc - -#### kvmx86 -- Add validation of RAM to be divisible by 128 in cloudapi/kvmx86 and cloudbroker/kvmx86 -- Add DataDisks field to CreateRequest and CreateBlankRequest in cloudapi/kvmx86 and cloudbroker/kvmx86 -- Add DataDisks field to MassCreateRequest in cloudbroker/kvmx86 - -#### lb: -- Add fields UserManaged, ManagerId, ManagerType, PartK8S in models RecordLB, ItemLBList in cloudapi/lb and cloudbroker/lb -- Add SortBy field for list groups and validation functionality -- Add field SysctlParams in model CreateRequest in cloudapi/lb/create and cloudbroker/lb/create -- Add endpoint update_sysctl_params in cloudapi/lb and cloudbroker/lb - -#### node: -- Add ApplyIpmiAction, Consumption, Decommission, Enable, EnableNodes, Get, GetRaw, List, ListRaw, Maintenance, Restrict, SetCoreIsolation, SetHugePages, SetSRIOVStatus, SetVFSNumber and Update endpoints in cloudbroker/node -- Add IDs method and Filters methods for ListNodes structure in cloudbroker/node -- Add Serialize method for ListNodes, ItemNode and RecordNode structures in cloudbroker/node - -#### pcidevice: -- Add List, ListRaw endpoints in cloudapi/pcidevice -- Add IDs and Serialize methods for ListPCIDevices structure in cloudapi/pcidevice - -#### rg: -- Add ComputeFeatures field to ItemResourceGroup and RecordResourceGroup models in cloudapi/rg -- Add ComputeFeatures field to ItemRG and CreateRequest models in cloudbroker/rg -- Add UpdateComputeFeatures endpoint in cloudbroker/rg -- Add ExtnetId, FreeIPs fields to model ItemVINS in cloudapi/rg and cloudbroker/rg - -#### sep: -- Add endpoints addPool, delPool in cloudbroker/sep -- Set field Config in CreateRequest as required in cloudbroker/sep -- Add SortBy field for list groups and validation functionality - -#### stack: -- Add endpoints getLogicalCoresCount, setCpuAllocationRatio, setMemAllocationRatio in cloudbroker/stack - -#### tasks: -- Add field TaskId, Status, Completed in model ListRequest in cloudapi/task and cloudbroker/task -- Add fields GUID, UpdatedBy in model ItemTask in cloudapi/task and cloudbroker/task -- Fix panic in List endpoints in cloudapi/task and cloudbroker/task -- Change type field Result from int to interface{} in models RecordAsyncTask, ItemAsyncTask in cloudapi/task and models ItemTask, RecordTask in cloudbroker/task -- Add methods ID(), Name(), ToString(), ToMaps() for processing the value of the Result field in model ItemTask in cloudapi/task and cloudbroker/task -- Add use case examples for the above methods in README.md -- Add SortBy field for list groups and validation functionality -- Add fields UpdateTimeAt and UpdateTimeTo in model ListRequest in cloudapi/tasks and cloudbroker/tasks - -#### user: -- Add fields Call, StatusCode, TimestampAt, TimestampTo in models GetAuditRequest in cloudbroker/user -- Fix return value in GetAudit endpoint in cloudbroker/user -- Add model ListAudits and change type field APIAccess in model ItemUser in cloudbroker/user -- Add SortBy field for list groups and validation functionality in cloudbroker/user -- Add APIList, Authenticate, Brief, GetAudit, GetResourceConsumption, Get, GetRaw, IsValidInviteUserToken, Search, SetData endpoints in cloudapi/user - -#### vfpool: -- Add Get, GetRaw, List, ListRaw endpoints in cloudapi/vfpool -- Add Create, Delete, Disable, Enable, Get, GetRaw, List, ListRaw, Update endpoints in cloudbroker/vfpool -- Add IDs method for ListVFPool, VFSInfoListand in cloudapi/vfpool and cloudbroker/vfpool -- Add Filters methods for ListVFPool structure in cloudapi/vfpool and cloudbroker/vfpool -- Add Serialize method for ListVFPool, ItemVFPool and RecordVFPool structures in cloudapi/vfpool and cloudbroker/vfpool - -#### vins: -- Set field IntPort in model NATRuleAddRequest as optional in cloudapi/vins and cloudbroker/vins -- Add SortBy field for list groups and validation functionality -- Add ExtnetId, FreeIPs fields to model ItemVINS in cloudapi/vins and cloudbroker/vins -- Add DNSList field to models CreateInAccountRequest and CreateInRGRequest in cloudapi/vins and cloudbroker/vins -- Add DNSApply endpoints in cloudapi/vins and cloudbroker/vins -- Add field NodeID in model RecordVINS.RecordVNFDev.Interfaces in cloudapi/vins and cloudbroker/vins +- Add NumaAffinity, CPUPin and HPBacked fields to model CreateRequest in cloudapi/kvmx86 and cloudbroker/kvmx86 +- Add NumaAffinity, CPUPin and HPBacked fields to model UpdateRequest in cloudapi/compute and cloudbroker/compute +- Add ReservedNodeCpus field to models ItemCompute, RecordCompute in cloudapi/compute and to models ItemCompute, InfoCompute in cloudbroker/compute +- Add VFNIC as possible NetType value in Interfaces field in model CreateRequest in cloudapi/kvmx86 and cloudbroker/kvmx86 +- Add VFNIC as possible NetType value in model NetAttachRequest in cloudapi/compute and cloudbroker/compute +- Add WithoutBootDisk field to models CreateRequest, CreateBlankRequest, MassCreateRequest in cloudapi/kvmx86 and cloudbroker/kvmx86 +- Set optional fields BootDisk, SEPID, Pool in model CreateBlankRequest in cloudapi/kvmx86 and cloudbroker/kvmx86 +- Set optional field ImageID in model CreateRequest in cloudapi/kvmx86 and cloudbroker/kvmx86 +- Add field VNFDevID in models ListRequest (cloudbrocker/vins/list) and ListDeletedRequest (cloudapi/vins/list_deleted) +- Add new endpoints /cloudbroker/disks/present and /cloudbroker/disks/depresent +- Add field AuditID in model ListRequest (cloudbrocker/tasks/list, cloudapi/tasks/list) +- Add field ClientIDs in model ListRequest (cloudbrocker/flipgroup/list, cloudapi/flipgroup/list) +- Add field Depresent in model StopRequest (cloudbrocker/compute/stop) +- Add new endpoint /cloudbroker/image/uploadImageFile ### Bugfix -#### account: -- Fix wrong json, url field AccountID in model SetCPUAllocationParameterRequest in cloudbroker/account -- Fix wrong json, url field AccountID in model SetCPUAllocationRatioRequest in cloudbroker/account -- Change Start and End fields type from uint64 to float64 in model GetConsumptionRequest in cloudapi/account - -#### apiaccess: -- Changed Job and Resmon field types in model CloudBrokerEndpoints in cloudbroker/apiaccess -- Add fields Page and Size in model UserListRequest in cloudbroker/apiaccess - -#### bservice: -- Fix url for Disable endpoint in cloudapi/bservice - -#### compute: -- Fix Entrycount tag in model ListComputes in cloudbroker/compute -- Add field Name in model ListPCIDeviceRequest in cloudbroker/compute -- Fix type of field Data in model ListPCIDevices in cloudapi/compute -- Add model ItemPCIDevice in cloudapi/compute -- Fix type of field Data in model ListVGPUs in cloudapi/compute and cloudbroker/compute -- Add model ItemVGPU in cloudapi/compute and cloudbroker/compute - -#### disks: -- Delete tag required from field Detailed in ListTypesRequest model in cloudapi/disks - -#### grid: -- Fix json, url tags in field Name in model RenameRequest in cloudbroker/grid - -#### image: -- Fix url for ComputeCIUnset endpoint in cloudbroker/image - -#### k8ci: -- Delete omitempty from json, url tags in field Permanently in model DeleteRequest in cloudbroker/k8ci -- Fix wrong json, url tags field ByID in model ListDeletedRequest in cloudbroker/k8ci -- Fix allowed network plugin value from "weawenet" to "weavenet" in validators for cloudbroker/k8ci - -#### k8s: -- Fix allowed network plugin value from "weawenet" to "weavenet" in validators for cloudapi/k8s, cloudbroker/k8s - -#### lb: -- Fix wrong json, url tags field AccountID in model ListDeletedRequest in cloudapi/lb -- Add field Safe in model RestartRequest in cloudbroker/lb -- Fix wrong json, url tags field AccountID in models ListRequest, ListDeletedRequest in cloudbroker/lb -- Fix url for Stop endpoint in cloudapi/lb - -#### rg: -- Fix wrong json, url field AccountID in model ListLBRequest in cloudapi/rg and cloudbroker/rg -- Fix wrong json, url field UniqPools in model CreateRequest in cloudbroker/rg - -#### sep: -- Fix wrong json, url tag in field GID in model ListRequest in cloudbroker/sep - -#### user: -- Fix wrong json, url field Password in model ItemUser in cloudbroker/user -- Change ByID field type from uint64 to string in model ListRequest in cloudbroker/user - -#### vins: -- Fix wrong json, url tags in models DefaultQOSUpdateRequest and NetQOSRequest in field IngressBirst in cloudbroker/vins -- Fix wrong name field ListNatRule to ListNATRule in cloudbroker/vins \ No newline at end of file +- Change the return value of the method ReplicationStatus from interface to string in cloudapi/disks and cloudbroker/disks +- Fix type field Replication in models ItemComputeDisk (cloudapi/compute), ItemDisk (cloudapi/disks, cloudbroker/disks), RecordDisk (cloudapi/disks), InfoDisk (cloudbroker/compute). Add type ItemReplication in models cloudapi/disks, cloudbroker/disks, cloudapi/compute, cloudbroker/compute +- Rename field Async to AsyncMode in cloudapi/compute, cloudbroker/compute, cloudbroker/backup +- Fix url tags in fields UpdateTimeAt and UpdateTimeTo in models ListRequest (cloudapi/tasks/list, cloudbroker/tasks/list) +- Fix type field Completed in models ListRequest (cloudapi/tasks/list, cloudbroker/tasks/list) +- Fix type field Shared in models ListRequest (cloudapi/disk/list, cloudbroker/disk/list) +- Fix type field Shared in models ListDeletedRequest (cloudapi/disk/list_deleted, cloudbroker/disk/list_deleted) +- Fix type field Public, HotResize, Bootable in models ListRequest (cloudapi/image/list, cloudbroker/image/list) +- Fix type field Active, ServiceAccount in model ListRequest (cloudbroker/user/list) +- Change required type and correct description in field ImageID in model MassCreateRequest (cloudbroker/kvmx86/mass_create) +- Delete field AccountID in model ListLBRequest (cloudbroker/rg/listLB, cloudapi/rg/listLB) \ No newline at end of file diff --git a/client.go b/client.go index b248244..91af120 100644 --- a/client.go +++ b/client.go @@ -18,6 +18,7 @@ import ( "github.com/google/go-querystring/query" "repository.basistech.ru/BASIS/decort-golang-sdk/config" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker" ) @@ -71,12 +72,22 @@ func (dc *DecortClient) CloudBroker() *cloudbroker.CloudBroker { // DecortApiCall method for sending requests to the platform func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { - values, err := query.Values(params) - if err != nil { - return nil, err - } - body := bytes.NewBufferString(values.Encode()) + var body *bytes.Buffer + var ctype string + + byteSlice, ok := params.([]byte) + if ok { + body = bytes.NewBuffer(byteSlice) + // ctype = "application/x-iso9660-image" + ctype = "application/octet-stream" + } else { + values, err := query.Values(params) + if err != nil { + return nil, err + } + body = bytes.NewBufferString(values.Encode()) + } req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+constants.RESTMACHINE+url, body) if err != nil { @@ -87,9 +98,8 @@ func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, p if err = dc.getToken(ctx); err != nil { return nil, err } - // perform request - respBytes, err := dc.do(req, "") + respBytes, err := dc.do(req, ctype) if err != nil { return nil, err } @@ -284,11 +294,22 @@ func multiPartReq(params interface{}) (*bytes.Buffer, string, error) { if values.Field(i).Type().Kind() == reflect.Slice { switch slice := values.Field(i).Interface().(type) { case []string: - for _, val := range slice { - err := writer.WriteField(trimString(types.Field(i)), val) + if validators.IsInSlice(trimString(types.Field(i)), constants.K8sValues) { + code, err := json.Marshal(slice) if err != nil { return &bytes.Buffer{}, "", err } + err = writer.WriteField(trimString(types.Field(i)), string(code)) + if err != nil { + return &bytes.Buffer{}, "", err + } + } else { + for _, val := range slice { + err := writer.WriteField(trimString(types.Field(i)), val) + if err != nil { + return &bytes.Buffer{}, "", err + } + } } case []uint: for _, val := range slice { diff --git a/client_bvs.go b/client_bvs.go index 8b02bb6..1b1f787 100644 --- a/client_bvs.go +++ b/client_bvs.go @@ -71,13 +71,22 @@ func (bdc *BVSDecortClient) CloudBroker() *cloudbroker.CloudBroker { // DecortApiCall method for sending requests to the platform func (bdc *BVSDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { - values, err := query.Values(params) - if err != nil { - return nil, err + var body *bytes.Buffer + var ctype string + + byteSlice, ok := params.([]byte) + if ok { + body = bytes.NewBuffer(byteSlice) + // ctype = "application/x-iso9660-image" + ctype = "application/octet-stream" + } else { + values, err := query.Values(params) + if err != nil { + return nil, err + } + body = bytes.NewBufferString(values.Encode()) } - body := bytes.NewBufferString(values.Encode()) - req, err := http.NewRequestWithContext(ctx, method, bdc.decortURL+constants.RESTMACHINE+url, body) if err != nil { return nil, err @@ -101,7 +110,7 @@ func (bdc *BVSDecortClient) DecortApiCall(ctx context.Context, method, url strin // perform request reqCopy := req.Clone(ctx) - respBytes, err := bdc.do(req, "") + respBytes, err := bdc.do(req, ctype) if err == nil { return respBytes, nil } diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 8385511..3ce48de 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -10,3 +10,5 @@ const ( var FileName = map[string]string{ "OidcCertificate": "ca.crt", } + +var K8sValues = []string{"labels", "taints", "annotations, additionalSANs"} diff --git a/internal/validators/custom.go b/internal/validators/custom.go index ba9352b..6f741cd 100644 --- a/internal/validators/custom.go +++ b/internal/validators/custom.go @@ -3,6 +3,7 @@ package validators import ( "errors" "fmt" + "reflect" "regexp" "strings" @@ -104,6 +105,13 @@ func computeNetTypeValidator(fe validator.FieldLevel) bool { return IsInSlice(fieldValue, computeNetTypeValues) } +// computex86NetTypeValidator is used to validate NetType field. +func computex86NetTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computex86NetTypeValues) +} + // computeOrderValidator is used to validate Order field. func computeOrderValidator(fe validator.FieldLevel) bool { fieldSlice, ok := fe.Field().Interface().([]string) @@ -312,6 +320,23 @@ func networkInterfaceNamingValidator(fe validator.FieldLevel) bool { return IsInSlice(fieldValue, networkInterfaceNamingValues) } +func numaAffinityValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, numaAffinityValues) +} + +// kvmx86NetTypeValidator is used to validate NetType field for x86 compute. +func kvmx86NetTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, kvmx86NetTypeValues) +} + +func isBoolTypeValidator(fe validator.FieldLevel) bool { + return fe.Field().CanConvert(reflect.TypeOf(true)) +} + // ValidateRAM checks if request contains RAM value that is positive integer divisible by divisibility passed. // It is recommended to pass constants.RAM_DIVISIBILITY as divisility arguement func ValidateRAM(r interfaces.RequestWithRAM, divisibility uint64) error { diff --git a/internal/validators/messages.go b/internal/validators/messages.go index 35f1c85..19d534c 100644 --- a/internal/validators/messages.go +++ b/internal/validators/messages.go @@ -95,6 +95,12 @@ func errorMessage(fe validator.FieldError) string { fe.Field(), joinValues(computeNetTypeValues)) + case "computex86NetType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computex86NetTypeValues)) + case "computeOrder": return fmt.Sprintf("%s %s can contain only the following values: %s", prefix, @@ -258,6 +264,18 @@ func errorMessage(fe validator.FieldError) string { prefix, fe.Field(), joinValues(networkInterfaceNamingValues)) + + case "numaAffinity": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(numaAffinityValues)) + + case "kvmx86NetType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(kvmx86NetTypeValues)) } return fe.Error() diff --git a/internal/validators/validator.go b/internal/validators/validator.go index 2a00437..cab98b6 100644 --- a/internal/validators/validator.go +++ b/internal/validators/validator.go @@ -106,6 +106,11 @@ func registerAllValidators(validate *validator.Validate) error { return err } + err = validate.RegisterValidation("computex86NetType", computex86NetTypeValidator) + if err != nil { + return err + } + err = validate.RegisterValidation("computeOrder", computeOrderValidator) if err != nil { return err @@ -211,5 +216,20 @@ func registerAllValidators(validate *validator.Validate) error { return err } + err = validate.RegisterValidation("numaAffinity", numaAffinityValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("kvmx86NetType", kvmx86NetTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("isBool", isBoolTypeValidator) + if err != nil { + return err + } + return nil } diff --git a/internal/validators/values.go b/internal/validators/values.go index 2bcd22a..0f42b99 100644 --- a/internal/validators/values.go +++ b/internal/validators/values.go @@ -10,20 +10,22 @@ var ( bserviceModeValues = []string{"ABSOLUTE", "RELATIVE"} - computeTopologyValues = []string{"compute", "node"} - computePolicyValues = []string{"RECOMMENDED", "REQUIRED"} - computeModeValues = []string{"EQ", "EN", "ANY"} - computeDiskTypeValues = []string{"D", "B"} - computeNetTypeValues = []string{"EXTNET", "VINS"} - computeOrderValues = []string{"cdrom", "network", "hd"} - computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"} - computeDriverValues = []string{"KVM_X86", "SVA_KVM_X86"} + computeTopologyValues = []string{"compute", "node"} + computePolicyValues = []string{"RECOMMENDED", "REQUIRED"} + computeModeValues = []string{"EQ", "EN", "ANY"} + computeDiskTypeValues = []string{"D", "B"} + computeNetTypeValues = []string{"EXTNET", "VINS"} + computex86NetTypeValues = []string{"EXTNET", "VINS", "VFNIC"} + computeOrderValues = []string{"cdrom", "network", "hd"} + computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"} + computeDriverValues = []string{"KVM_X86", "SVA_KVM_X86"} diskTypeValues = []string{"B", "T", "D"} flipgroupClientTypeValues = []string{"compute", "vins"} - kvmNetTypeValues = []string{"EXTNET", "VINS", "NONE"} + kvmNetTypeValues = []string{"EXTNET", "VINS", "NONE"} + kvmx86NetTypeValues = []string{"EXTNET", "VINS", "NONE", "VFNIC"} lbAlgorithmValues = []string{"roundrobin", "static-rr", "leastconn"} @@ -52,4 +54,6 @@ var ( computeFeaturesValues = []string{"hugepages", "numa", "cpupin", "vfnic"} networkInterfaceNamingValues = []string{"eth", "ens"} + + numaAffinityValues = []string{"none", "strict", "loose"} ) diff --git a/legacy-client.go b/legacy-client.go index 4d4a7a4..f43baf1 100644 --- a/legacy-client.go +++ b/legacy-client.go @@ -73,12 +73,20 @@ func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url st return nil, err } - values, err := query.Values(params) - if err != nil { - return nil, err - } + var body *bytes.Buffer + var ctype string - body := bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token)) + byteSlice, ok := params.([]byte) + if ok { + body = bytes.NewBuffer(byteSlice) + ctype = "application/octet-stream" + } else { + values, err := query.Values(params) + if err != nil { + return nil, err + } + body = bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token)) + } req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+constants.RESTMACHINE+url, body) if err != nil { @@ -86,7 +94,7 @@ func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url st } // perform request - respBytes, err := ldc.do(req, "") + respBytes, err := ldc.do(req, ctype) if err != nil { return nil, err } diff --git a/pkg/cloudapi/compute/create_template.go b/pkg/cloudapi/compute/create_template.go index 9d575d9..18dbc8c 100644 --- a/pkg/cloudapi/compute/create_template.go +++ b/pkg/cloudapi/compute/create_template.go @@ -23,7 +23,7 @@ type CreateTemplateRequest struct { type wrapperCreateTemplateRequest struct { CreateTemplateRequest - Async bool `url:"async"` + AsyncMode bool `url:"asyncMode"` } // CreateTemplate create template from compute instance @@ -35,7 +35,7 @@ func (c Compute) CreateTemplate(ctx context.Context, req CreateTemplateRequest) reqWrapped := wrapperCreateTemplateRequest{ CreateTemplateRequest: req, - Async: false, + AsyncMode: false, } url := "/cloudapi/compute/createTemplate" @@ -62,7 +62,7 @@ func (c Compute) CreateTemplateAsync(ctx context.Context, req CreateTemplateRequ reqWrapped := wrapperCreateTemplateRequest{ CreateTemplateRequest: req, - Async: true, + AsyncMode: true, } url := "/cloudapi/compute/createTemplate" diff --git a/pkg/cloudapi/compute/models.go b/pkg/cloudapi/compute/models.go index a2f46c9..5e9f99e 100644 --- a/pkg/cloudapi/compute/models.go +++ b/pkg/cloudapi/compute/models.go @@ -431,6 +431,9 @@ type RecordCompute struct { // Resource name ResName string `json:"resName"` + // Reserved Node Cpus + ReservedNodeCpus []uint64 `json:"reservedNodeCpus"` + // Resource group ID RGID uint64 `json:"rgId"` @@ -673,7 +676,7 @@ type ItemComputeDisk struct { RealityDeviceNumber uint64 `json:"realityDeviceNumber"` // Replication - Replication interface{} `json:"replication"` + Replication ItemReplication `json:"replication"` // Resource ID ResID string `json:"resId"` @@ -709,6 +712,26 @@ type ItemComputeDisk struct { VMID uint64 `json:"vmid"` } +type ItemReplication struct { + // DiskID + DiskID uint64 `json:"diskId"` + + // PoolID + PoolID string `json:"poolId"` + + // Role + Role string `json:"role"` + + // SelfVolumeID + SelfVolumeID string `json:"selfVolumeId"` + + // StorageID + StorageID string `json:"storageId"` + + // VolumeID + VolumeID string `json:"volumeId"` +} + // Main information about snapshot extend type SnapshotExtend struct { // GUID @@ -915,6 +938,9 @@ type ItemCompute struct { // Resource name ResName string `json:"resName"` + // Reserved Node Cpus + ReservedNodeCpus []uint64 `json:"reservedNodeCpus"` + // Resource group ID RGID uint64 `json:"rgId"` @@ -1085,6 +1111,7 @@ type ItemPCIDevice struct { type ListPCIDevices struct { // Data Data []ItemPCIDevice `json:"data"` + // Entry count EntryCount uint64 `json:"entryCount"` } diff --git a/pkg/cloudapi/compute/net_attach.go b/pkg/cloudapi/compute/net_attach.go index 46d3e44..22068f5 100644 --- a/pkg/cloudapi/compute/net_attach.go +++ b/pkg/cloudapi/compute/net_attach.go @@ -16,9 +16,10 @@ type NetAttachRequest struct { // Network type // 'EXTNET' for connect to external network directly - // and 'VINS' for connect to ViNS + // 'VINS' for connect to ViNS + // 'VFNIC' for connect to vfpool // Required: true - NetType string `url:"netType" json:"netType" validate:"computeNetType"` + NetType string `url:"netType" json:"netType" validate:"computex86NetType"` // Network ID for connect to // For EXTNET - external network ID diff --git a/pkg/cloudapi/compute/update.go b/pkg/cloudapi/compute/update.go index add4cdb..ef29bda 100644 --- a/pkg/cloudapi/compute/update.go +++ b/pkg/cloudapi/compute/update.go @@ -21,6 +21,24 @@ type UpdateRequest struct { // New description // Required: false Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Rule for VM placement with NUMA affinity. + // Possible values - none (placement without NUMA affinity), + // strict (strictly with NUMA affinity, if not possible - do not start VM), + // loose (use NUMA affinity if possible) + // Required: false + // Default: none + NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"` + + // Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node + // Required: false + // Default: false + CPUPin bool `url:"cpupin" json:"cpupin"` + + // Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node + // Required: false + // Default: false + HPBacked bool `url:"hpBacked" json:"hpBacked"` } // Update updates some properties of the compute diff --git a/pkg/cloudapi/disks/from_platform_disk.go b/pkg/cloudapi/disks/from_platform_disk.go index c7a54d6..0d030f1 100644 --- a/pkg/cloudapi/disks/from_platform_disk.go +++ b/pkg/cloudapi/disks/from_platform_disk.go @@ -57,7 +57,7 @@ type FromPlatformDiskRequest struct { // List of types of compute suitable for image // Example: [ "KVM_X86" ] // Required: false - Drivers []string `url:"drivers" json:"drivers" validate:"min=1,max=2,imageDrivers"` + Drivers []string `url:"drivers,omitempty" json:"drivers,omitempty"` // Does this machine supports hot resize // Required: false diff --git a/pkg/cloudapi/disks/list.go b/pkg/cloudapi/disks/list.go index 20f981f..54eb2a2 100644 --- a/pkg/cloudapi/disks/list.go +++ b/pkg/cloudapi/disks/list.go @@ -32,7 +32,7 @@ type ListRequest struct { // Find by shared, true or false // Required: false - Shared bool `url:"shared,omitempty" json:"shared,omitempty"` + Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"` // ID of the account the disks belong to // Required: false diff --git a/pkg/cloudapi/disks/list_deleted.go b/pkg/cloudapi/disks/list_deleted.go index 9252bd3..60e4db5 100644 --- a/pkg/cloudapi/disks/list_deleted.go +++ b/pkg/cloudapi/disks/list_deleted.go @@ -28,7 +28,7 @@ type ListDeletedRequest struct { // Find by shared, true or false // Required: false - Shared bool `url:"shared,omitempty" json:"shared,omitempty"` + Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"` // ID of the account the disks belong to // Required: false diff --git a/pkg/cloudapi/disks/models.go b/pkg/cloudapi/disks/models.go index 2aa7f9c..2838b65 100644 --- a/pkg/cloudapi/disks/models.go +++ b/pkg/cloudapi/disks/models.go @@ -75,7 +75,7 @@ type ItemDisk struct { PurgeTime uint64 `json:"purgeTime"` // Replication - Replication interface{} `json:"replication"` + Replication ItemReplication `json:"replication"` // Resource ID ResID string `json:"resId"` @@ -265,8 +265,10 @@ type ListDisks struct { // List of unattached disks type ListDisksUnattached struct { + // Data Data []ItemDiskUnattached `json:"data"` + // Entry count EntryCount uint64 `json:"entryCount"` } @@ -407,7 +409,7 @@ type RecordDisk struct { PurgeTime uint64 `json:"purgeTime"` // Replication - Replication interface{} `json:"replication"` + Replication ItemReplication `json:"replication"` // Resource ID ResID string `json:"resId"` @@ -449,6 +451,26 @@ type RecordDisk struct { VMID uint64 `json:"vmid"` } +type ItemReplication struct { + // DiskID + DiskID uint64 `json:"diskId"` + + // PoolID + PoolID string `json:"poolId"` + + // Role + Role string `json:"role"` + + // SelfVolumeID + SelfVolumeID string `json:"selfVolumeId"` + + // StorageID + StorageID string `json:"storageId"` + + // VolumeID + VolumeID string `json:"volumeId"` +} + type ListTypes struct { // Data Data []interface{} `json:"data"` diff --git a/pkg/cloudapi/disks/replication_status.go b/pkg/cloudapi/disks/replication_status.go index f5ee27f..6c56989 100644 --- a/pkg/cloudapi/disks/replication_status.go +++ b/pkg/cloudapi/disks/replication_status.go @@ -15,23 +15,18 @@ type ReplicationStatusRequest struct { } // ReplicationStatus get replication status -func (d Disks) ReplicationStatus(ctx context.Context, req ReplicationStatusRequest) (interface{}, error) { +func (d Disks) ReplicationStatus(ctx context.Context, req ReplicationStatusRequest) (string, error) { err := validators.ValidateRequest(req) if err != nil { - return nil, validators.ValidationErrors(validators.GetErrors(err)) + return "", validators.ValidationErrors(validators.GetErrors(err)) } url := "/cloudapi/disks/replicationStatus" res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) if err != nil { - return nil, err + return "", err } - // result, err := strconv.ParseBool(string(res)) - // if err != nil { - // return nil, err - // } - - return res, nil + return string(res), nil } diff --git a/pkg/cloudapi/flipgroup/list.go b/pkg/cloudapi/flipgroup/list.go index b21bb5a..f248a88 100644 --- a/pkg/cloudapi/flipgroup/list.go +++ b/pkg/cloudapi/flipgroup/list.go @@ -42,6 +42,10 @@ type ListRequest struct { // Required: false ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + // Find by list of clientIds + // Required: false + ClientIDs []uint64 `url:"clientIds,omitempty" json:"clientIds,omitempty"` + // Page number // Required: false Page uint64 `url:"page,omitempty" json:"page,omitempty"` diff --git a/pkg/cloudapi/image/list.go b/pkg/cloudapi/image/list.go index cf8c2f9..52c9efb 100644 --- a/pkg/cloudapi/image/list.go +++ b/pkg/cloudapi/image/list.go @@ -48,15 +48,15 @@ type ListRequest struct { // Find by public True or False // Required: false - Public bool `url:"public,omitempty" json:"public,omitempty"` + Public interface{} `url:"public,omitempty" json:"public,omitempty" validate:"omitempty,sortBy"` // Find by hot resize True or False // Required: false - HotResize bool `url:"hotResize,omitempty" json:"hotResize,omitempty"` + HotResize interface{} `url:"hotResize,omitempty" json:"hotResize,omitempty" validate:"omitempty,sortBy"` // Find by bootable True or False // Required: false - Bootable bool `url:"bootable,omitempty" json:"bootable,omitempty"` + Bootable interface{} `url:"bootable,omitempty" json:"bootable,omitempty" validate:"omitempty,sortBy"` // Sort by one of supported fields, format +|-(field) // Required: false diff --git a/pkg/cloudapi/kvmx86/create.go b/pkg/cloudapi/kvmx86/create.go index 245ea0c..57536cc 100644 --- a/pkg/cloudapi/kvmx86/create.go +++ b/pkg/cloudapi/kvmx86/create.go @@ -14,7 +14,8 @@ type Interface struct { // Should be one of: // - VINS // - EXTNET - NetType string `url:"netType" json:"netType" validate:"required,kvmNetType"` + // - VFNIC + NetType string `url:"netType" json:"netType" validate:"required,kvmx86NetType"` // Network ID for connect to, // for EXTNET - external network ID, @@ -74,10 +75,14 @@ type CreateRequest struct { // Required: true RAM uint64 `url:"ram" json:"ram" validate:"required"` + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + // ID of the OS image to base this VM on; // Could be boot disk image or CD-ROM image - // Required: true - ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` // Size of the boot disk in GB // Required: false @@ -131,6 +136,24 @@ type CreateRequest struct { // Type of compute Stateful (KVM_X86) or Stateless (SVA_KVM_X86) // Required: false Driver string `url:"driver,omitempty" json:"driver,omitempty" validate:"omitempty,computeDriver"` + + // Rule for VM placement with NUMA affinity. + // Possible values - none (placement without NUMA affinity), + // strict (strictly with NUMA affinity, if not possible - do not start VM), + // loose (use NUMA affinity if possible) + // Required: false + // Default: none + NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"` + + // Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node + // Required: false + // Default: false + CPUPin bool `url:"cpupin" json:"cpupin"` + + // Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node + // Required: false + // Default: false + HPBacked bool `url:"hpBacked" json:"hpBacked"` } // GetRAM returns RAM field values diff --git a/pkg/cloudapi/kvmx86/create_blank.go b/pkg/cloudapi/kvmx86/create_blank.go index 8b423fc..e771518 100644 --- a/pkg/cloudapi/kvmx86/create_blank.go +++ b/pkg/cloudapi/kvmx86/create_blank.go @@ -28,18 +28,22 @@ type CreateBlankRequest struct { // Required: true RAM uint64 `url:"ram" json:"ram" validate:"required"` + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + // Size of the boot disk in GB - // Required: true - BootDisk uint64 `url:"bootDisk" json:"bootDisk" validate:"required"` + // Required: false + BootDisk uint64 `url:"bootDisk,omitempty" json:"bootDisk,omitempty"` // ID of SEP to create boot disk on. // Uses images SEP ID if not set - // Required: true - SEPID uint64 `url:"sepId" json:"sepId" validate:"required"` + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` // Pool to use if sepId is set, can be also empty if needed to be chosen by system - // Required: true - Pool string `url:"pool" json:"pool" validate:"required"` + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` // Slice of structs with data disk description. Each disk has parameters: required - diskName, size; optional - sepId, pool, desc and imageId. // If not specified, compute will be created without disks. diff --git a/pkg/cloudapi/rg/list_lb.go b/pkg/cloudapi/rg/list_lb.go index ee59a45..8ae79c9 100644 --- a/pkg/cloudapi/rg/list_lb.go +++ b/pkg/cloudapi/rg/list_lb.go @@ -22,10 +22,6 @@ type ListLBRequest struct { // Required: false Name string `url:"name,omitempty" json:"name,omitempty"` - // Find by account ID - // Required: false - AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` - // Find by tech status // Required: false TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` diff --git a/pkg/cloudapi/tasks/list.go b/pkg/cloudapi/tasks/list.go index 15ace57..a2db764 100644 --- a/pkg/cloudapi/tasks/list.go +++ b/pkg/cloudapi/tasks/list.go @@ -14,14 +14,17 @@ type ListRequest struct { // Required: false TaskID string `url:"taskId,omitempty" json:"taskId,omitempty"` + // Find by auditId + // Required: false + AuditID string `url:"auditId,omitempty" json:"auditId,omitempty"` + // Find by status // Required: false Status string `url:"status,omitempty" json:"status,omitempty"` // Find by completed True or False - // Default: false // Required: false - Completed bool `url:"completed" json:"completed"` + Completed interface{} `url:"completed,omitempty" json:"completed,omitempty" validate:"omitempty,isBool"` // Sort by one of supported fields, format +|-(field) // Required: false @@ -29,19 +32,21 @@ type ListRequest struct { // Find all tasks after point in time (unixtime) // Required: false - UpdateTimeAt uint64 `url:"page,updateTimeAt" json:"updateTimeAt,omitempty"` + UpdateTimeAt uint64 `url:"updateTimeAt,omitempty" json:"updateTimeAt,omitempty"` // Find all tasks before point in time (unixtime) // Required: false - UpdateTimeTo uint64 `url:"page,updateTimeTo" json:"updateTimeTo,omitempty"` + UpdateTimeTo uint64 `url:"updateTimeTo,omitempty" json:"updateTimeTo,omitempty"` // Page number // Default: 0 + // Default: 0 // Required: false Page uint64 `url:"page,omitempty" json:"page,omitempty"` // Page size // Default: 0 + // Default: 0 // Required: false Size uint64 `url:"size,omitempty" json:"size,omitempty"` } @@ -71,6 +76,10 @@ func (t Tasks) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { return nil, validators.ValidationErrors(validators.GetErrors(err)) } + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + url := "/cloudapi/tasks/list" res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) diff --git a/pkg/cloudapi/vins/list_deleted.go b/pkg/cloudapi/vins/list_deleted.go index 0c7d552..99311cd 100644 --- a/pkg/cloudapi/vins/list_deleted.go +++ b/pkg/cloudapi/vins/list_deleted.go @@ -30,6 +30,10 @@ type ListDeletedRequest struct { // Required: false ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + // Find by VNF Device id + // Required: false + VNFDevID uint64 `url:"vnfdevId,omitempty" json:"vnfdevId,omitempty"` + // Sort by one of supported fields, format +|-(field) // Required: false SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` @@ -50,6 +54,10 @@ func (v VINS) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListVIN return nil, validators.ValidationErrors(validators.GetErrors(err)) } + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + url := "/cloudapi/vins/listDeleted" res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) diff --git a/pkg/cloudbroker/backup/create_disk_backup.go b/pkg/cloudbroker/backup/create_disk_backup.go index f85a493..14b5fc8 100644 --- a/pkg/cloudbroker/backup/create_disk_backup.go +++ b/pkg/cloudbroker/backup/create_disk_backup.go @@ -27,7 +27,7 @@ type CreateDiskBackupRequest struct { type wrapperCreateDiskBackupRequest struct { CreateDiskBackupRequest - Async bool `url:"async"` + AsyncMode bool `url:"asyncMode"` } // CreateDiskBackup creates disk backup @@ -39,7 +39,7 @@ func (b Backup) CreateDiskBackup(ctx context.Context, req CreateDiskBackupReques reqWrapped := wrapperCreateDiskBackupRequest{ CreateDiskBackupRequest: req, - Async: false, + AsyncMode: false, } url := "/cloudbroker/backup/createDiskBackup" @@ -68,7 +68,7 @@ func (b Backup) CreateDiskBackupAsync(ctx context.Context, req CreateDiskBackupR reqWrapped := wrapperCreateDiskBackupRequest{ CreateDiskBackupRequest: req, - Async: true, + AsyncMode: true, } url := "/cloudbroker/backup/createDiskBackup" diff --git a/pkg/cloudbroker/backup/create_disks_backup.go b/pkg/cloudbroker/backup/create_disks_backup.go index 85cae35..843bc37 100644 --- a/pkg/cloudbroker/backup/create_disks_backup.go +++ b/pkg/cloudbroker/backup/create_disks_backup.go @@ -29,7 +29,7 @@ type CreateDisksBackupRequest struct { type wrapperCreateDisksBackupRequest struct { CreateDisksBackupRequest - Async bool `url:"async"` + AsyncMode bool `url:"asyncMode"` } // CreateDisksBackup creates disks backup @@ -41,7 +41,7 @@ func (b Backup) CreateDisksBackup(ctx context.Context, req CreateDisksBackupRequ reqWrapped := wrapperCreateDisksBackupRequest{ CreateDisksBackupRequest: req, - Async: false, + AsyncMode: false, } url := "/cloudbroker/backup/createDisksBackup" @@ -70,7 +70,7 @@ func (b Backup) CreateDisksBackupAsync(ctx context.Context, req CreateDisksBacku reqWrapped := wrapperCreateDisksBackupRequest{ CreateDisksBackupRequest: req, - Async: true, + AsyncMode: true, } url := "/cloudbroker/backup/createDisksBackup" diff --git a/pkg/cloudbroker/backup/delete_disk_backup.go b/pkg/cloudbroker/backup/delete_disk_backup.go index 33f16f9..225e5e5 100644 --- a/pkg/cloudbroker/backup/delete_disk_backup.go +++ b/pkg/cloudbroker/backup/delete_disk_backup.go @@ -21,7 +21,7 @@ type DeleteDiskBackupRequest struct { type wrapperDeleteDiskBackupRequest struct { DeleteDiskBackupRequest - Async bool `url:"async"` + AsyncMode bool `url:"asyncMode"` } // DeleteDiskBackup deletes disk backup @@ -33,7 +33,7 @@ func (b Backup) DeleteDiskBackup(ctx context.Context, req DeleteDiskBackupReques reqWrapped := wrapperDeleteDiskBackupRequest{ DeleteDiskBackupRequest: req, - Async: false, + AsyncMode: false, } url := "/cloudbroker/backup/deleteDiskBackup" @@ -60,7 +60,7 @@ func (b Backup) DeleteDiskBackupAsync(ctx context.Context, req DeleteDiskBackupR reqWrapped := wrapperDeleteDiskBackupRequest{ DeleteDiskBackupRequest: req, - Async: true, + AsyncMode: true, } url := "/cloudbroker/backup/deleteDiskBackup" diff --git a/pkg/cloudbroker/backup/restore_disk_from_backup.go b/pkg/cloudbroker/backup/restore_disk_from_backup.go index 97c451c..de716b1 100644 --- a/pkg/cloudbroker/backup/restore_disk_from_backup.go +++ b/pkg/cloudbroker/backup/restore_disk_from_backup.go @@ -27,7 +27,7 @@ type RestoreDiskFromBackupRequest struct { type wrapperRestoreDiskFromBackupRequest struct { RestoreDiskFromBackupRequest - Async bool `url:"async"` + AsyncMode bool `url:"asyncMode"` } // RestoreDiskFromBackup restores disk from backup @@ -39,7 +39,7 @@ func (b Backup) RestoreDiskFromBackup(ctx context.Context, req RestoreDiskFromBa reqWrapped := wrapperRestoreDiskFromBackupRequest{ RestoreDiskFromBackupRequest: req, - Async: false, + AsyncMode: false, } url := "/cloudbroker/backup/restoreDiskFromBackup" @@ -68,7 +68,7 @@ func (b Backup) RestoreDiskFromBackupAsync(ctx context.Context, req RestoreDiskF reqWrapped := wrapperRestoreDiskFromBackupRequest{ RestoreDiskFromBackupRequest: req, - Async: true, + AsyncMode: true, } url := "/cloudbroker/backup/restoreDiskFromBackup" diff --git a/pkg/cloudbroker/backup/restore_disks_from_backup.go b/pkg/cloudbroker/backup/restore_disks_from_backup.go index 24799e6..b88add8 100644 --- a/pkg/cloudbroker/backup/restore_disks_from_backup.go +++ b/pkg/cloudbroker/backup/restore_disks_from_backup.go @@ -32,7 +32,7 @@ type RestoreDisksFromBackupRequest struct { type wrapperRestoreDisksFromBackupRequest struct { RestoreDisksFromBackupRequest - Async bool `url:"async"` + AsyncMode bool `url:"asyncMode"` } // RestoreDisksFromBackup restores disks from backup @@ -44,7 +44,7 @@ func (b Backup) RestoreDisksFromBackup(ctx context.Context, req RestoreDisksFrom reqWrapped := wrapperRestoreDisksFromBackupRequest{ RestoreDisksFromBackupRequest: req, - Async: false, + AsyncMode: false, } url := "/cloudbroker/backup/restoreDisksFromBackup" @@ -73,7 +73,7 @@ func (b Backup) RestoreDisksFromBackupAsync(ctx context.Context, req RestoreDisk reqWrapped := wrapperRestoreDisksFromBackupRequest{ RestoreDisksFromBackupRequest: req, - Async: true, + AsyncMode: true, } url := "/cloudbroker/backup/restoreDisksFromBackup" diff --git a/pkg/cloudbroker/compute/create_template.go b/pkg/cloudbroker/compute/create_template.go index e649fca..17c448a 100644 --- a/pkg/cloudbroker/compute/create_template.go +++ b/pkg/cloudbroker/compute/create_template.go @@ -27,7 +27,7 @@ type CreateTemplateRequest struct { type wrapperCreateTemplateRequest struct { CreateTemplateRequest - Async bool `url:"async"` + AsyncMode bool `url:"asyncMode"` } // CreateTemplateAsync create template from compute instance @@ -39,7 +39,7 @@ func (c Compute) CreateTemplateAsync(ctx context.Context, req CreateTemplateRequ reqWrapped := wrapperCreateTemplateRequest{ CreateTemplateRequest: req, - Async: true, + AsyncMode: true, } url := "/cloudbroker/compute/createTemplate" @@ -63,7 +63,7 @@ func (c Compute) CreateTemplate(ctx context.Context, req CreateTemplateRequest) reqWrapped := wrapperCreateTemplateRequest{ CreateTemplateRequest: req, - Async: false, + AsyncMode: false, } url := "/cloudbroker/compute/createTemplate" diff --git a/pkg/cloudbroker/compute/models.go b/pkg/cloudbroker/compute/models.go index 8cfbcb4..233cc18 100644 --- a/pkg/cloudbroker/compute/models.go +++ b/pkg/cloudbroker/compute/models.go @@ -440,7 +440,7 @@ type ItemDisk struct { RealityDeviceNumber uint64 `json:"realityDeviceNumber"` // Replication - Replication interface{} `json:"replication"` + Replication ItemReplication `json:"replication"` // Reference ID ReferenceID string `json:"referenceId"` @@ -485,6 +485,26 @@ type ItemDisk struct { VMID uint64 `json:"vmid"` } +type ItemReplication struct { + // DiskID + DiskID uint64 `json:"diskId"` + + // PoolID + PoolID string `json:"poolId"` + + // Role + Role string `json:"role"` + + // SelfVolumeID + SelfVolumeID string `json:"selfVolumeId"` + + // StorageID + StorageID string `json:"storageId"` + + // VolumeID + VolumeID string `json:"volumeId"` +} + // List disks type ListDisks []ItemDisk @@ -706,6 +726,9 @@ type InfoCompute struct { // Resource name ResName string `json:"resName"` + // Reserved Node Cpus + ReservedNodeCpus []uint64 `json:"reservedNodeCpus"` + // Resource group ID RGID uint64 `json:"rgId"` @@ -919,6 +942,9 @@ type RecordCompute struct { // Resource name ResName string `json:"resName"` + // Reserved Node Cpus + ReservedNodeCpus []uint64 `json:"reservedNodeCpus"` + // Resource group ID RGID uint64 `json:"rgId"` @@ -1010,6 +1036,7 @@ type ListComputes struct { EntryCount uint64 `json:"entryCount"` } +// Short information about audit // Short information about audit type ItemAudit struct { // Epoch diff --git a/pkg/cloudbroker/compute/net_attach.go b/pkg/cloudbroker/compute/net_attach.go index dc49488..939dcac 100644 --- a/pkg/cloudbroker/compute/net_attach.go +++ b/pkg/cloudbroker/compute/net_attach.go @@ -16,9 +16,10 @@ type NetAttachRequest struct { // Network type // 'EXTNET' for connect to external network directly - // and 'VINS' for connect to ViNS + // 'VINS' for connect to ViNS + // 'VFNIC' for connect to vfpool // Required: true - NetType string `url:"netType" json:"netType" validate:"computeNetType"` + NetType string `url:"netType" json:"netType" validate:"computex86NetType"` // Network ID for connect to // For EXTNET - external network ID diff --git a/pkg/cloudbroker/compute/stop.go b/pkg/cloudbroker/compute/stop.go index a5b1e34..52062ef 100644 --- a/pkg/cloudbroker/compute/stop.go +++ b/pkg/cloudbroker/compute/stop.go @@ -18,6 +18,11 @@ type StopRequest struct { // Required: false Force bool `url:"force,omitempty" json:"force,omitempty"` + // whether to depresent compute disks from node or not + // Default: true + // Required: false + Depresent bool `url:"depresent" json:"depresent"` + // Reason for action // Required: false Reason string `url:"reason,omitempty" json:"reason,omitempty"` diff --git a/pkg/cloudbroker/compute/update.go b/pkg/cloudbroker/compute/update.go index b8ff1a5..f08265f 100644 --- a/pkg/cloudbroker/compute/update.go +++ b/pkg/cloudbroker/compute/update.go @@ -22,6 +22,24 @@ type UpdateRequest struct { // Required: false Description string `url:"desc,omitempty" json:"desc,omitempty"` + // Rule for VM placement with NUMA affinity. + // Possible values - none (placement without NUMA affinity), + // strict (strictly with NUMA affinity, if not possible - do not start VM), + // loose (use NUMA affinity if possible) + // Required: false + // Default: none + NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"` + + // Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node + // Required: false + // Default: false + CPUPin bool `url:"cpupin" json:"cpupin"` + + // Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node + // Required: false + // Default: false + HPBacked bool `url:"hpBacked" json:"hpBacked"` + // Reason for action // Required: false Reason string `url:"reason,omitempty" json:"reason,omitempty"` diff --git a/pkg/cloudbroker/disks/depresent.go b/pkg/cloudbroker/disks/depresent.go new file mode 100644 index 0000000..f8db779 --- /dev/null +++ b/pkg/cloudbroker/disks/depresent.go @@ -0,0 +1,41 @@ +package disks + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// DepresentRequest struct to depresent disk from node +type DepresentRequest struct { + // ID of the disk to depresent + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID of the node to depresent disk from + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// Depresent depresents disk from node +func (d Disks) Depresent(ctx context.Context, req DepresentRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/depresent" + + res, err := d.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/disks/from_platform_disk.go b/pkg/cloudbroker/disks/from_platform_disk.go index d0450b4..e121100 100644 --- a/pkg/cloudbroker/disks/from_platform_disk.go +++ b/pkg/cloudbroker/disks/from_platform_disk.go @@ -57,7 +57,7 @@ type FromPlatformDiskRequest struct { // List of types of compute suitable for image // Example: [ "KVM_X86" ] // Required: false - Drivers []string `url:"drivers" json:"drivers" validate:"min=1,max=2,imageDrivers"` + Drivers []string `url:"drivers,omitempty" json:"drivers,omitempty"` // Does this machine supports hot resize // Required: false diff --git a/pkg/cloudbroker/disks/list.go b/pkg/cloudbroker/disks/list.go index d5b40fd..46b657a 100644 --- a/pkg/cloudbroker/disks/list.go +++ b/pkg/cloudbroker/disks/list.go @@ -32,7 +32,7 @@ type ListRequest struct { // Find by shared, true or false // Required: false - Shared bool `url:"shared,omitempty" json:"shared,omitempty"` + Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"` // ID of the account the disks belong to // Required: false diff --git a/pkg/cloudbroker/disks/list_deleted.go b/pkg/cloudbroker/disks/list_deleted.go index c68b436..f4a6c39 100644 --- a/pkg/cloudbroker/disks/list_deleted.go +++ b/pkg/cloudbroker/disks/list_deleted.go @@ -28,7 +28,7 @@ type ListDeletedRequest struct { // Find by shared, true or false // Required: false - Shared bool `url:"shared,omitempty" json:"shared,omitempty"` + Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"` // ID of the account the disks belong to // Required: false diff --git a/pkg/cloudbroker/disks/models.go b/pkg/cloudbroker/disks/models.go index fbd35b8..92203f2 100644 --- a/pkg/cloudbroker/disks/models.go +++ b/pkg/cloudbroker/disks/models.go @@ -138,7 +138,7 @@ type InfoDisk struct { ReferenceID string `json:"referenceId"` // Replication - Replication interface{} `json:"replication"` + Replication ItemReplication `json:"replication"` // Resource ID ResID string `json:"resId"` @@ -177,6 +177,26 @@ type InfoDisk struct { VMID uint64 `json:"vmid"` } +type ItemReplication struct { + // DiskID + DiskID uint64 `json:"diskId"` + + // PoolID + PoolID string `json:"poolId"` + + // Role + Role string `json:"role"` + + // SelfVolumeID + SelfVolumeID string `json:"selfVolumeId"` + + // StorageID + StorageID string `json:"storageId"` + + // VolumeID + VolumeID string `json:"volumeId"` +} + // Detailed indormation about disk type RecordDisk struct { // Device name diff --git a/pkg/cloudbroker/disks/present.go b/pkg/cloudbroker/disks/present.go new file mode 100644 index 0000000..f0de67b --- /dev/null +++ b/pkg/cloudbroker/disks/present.go @@ -0,0 +1,41 @@ +package disks + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" + "strconv" +) + +// PresentRequest struct to present disk to node +type PresentRequest struct { + // ID of the disk to present + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID of the node to present disk to + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// Present presents disk to node +func (d Disks) Present(ctx context.Context, req PresentRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/present" + + res, err := d.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/disks/replication_status.go b/pkg/cloudbroker/disks/replication_status.go index 4072d7f..8ec2ce4 100644 --- a/pkg/cloudbroker/disks/replication_status.go +++ b/pkg/cloudbroker/disks/replication_status.go @@ -15,23 +15,18 @@ type ReplicationStatusRequest struct { } // ReplicationStatus get replication status -func (d Disks) ReplicationStatus(ctx context.Context, req ReplicationStatusRequest) (interface{}, error) { +func (d Disks) ReplicationStatus(ctx context.Context, req ReplicationStatusRequest) (string, error) { err := validators.ValidateRequest(req) if err != nil { - return nil, validators.ValidationErrors(validators.GetErrors(err)) + return "", validators.ValidationErrors(validators.GetErrors(err)) } url := "/cloudbroker/disks/replicationStatus" res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) if err != nil { - return nil, err + return "", err } - // result, err := strconv.ParseBool(string(res)) - // if err != nil { - // return nil, err - // } - - return res, nil + return string(res), nil } diff --git a/pkg/cloudbroker/flipgroup/list.go b/pkg/cloudbroker/flipgroup/list.go index 5505c39..dcae554 100644 --- a/pkg/cloudbroker/flipgroup/list.go +++ b/pkg/cloudbroker/flipgroup/list.go @@ -42,6 +42,10 @@ type ListRequest struct { // Required: false ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + // Find by list of clientIds + // Required: false + ClientIDs []uint64 `url:"clientIds,omitempty" json:"clientIds,omitempty"` + // Sort by one of supported fields, format +|-(field) // Required: false SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` @@ -88,6 +92,10 @@ func (f FLIPGroup) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) return nil, validators.ValidationErrors(validators.GetErrors(err)) } + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + url := "/cloudbroker/flipgroup/list" res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) diff --git a/pkg/cloudbroker/group/list.go b/pkg/cloudbroker/group/list.go index c436678..9196bf7 100644 --- a/pkg/cloudbroker/group/list.go +++ b/pkg/cloudbroker/group/list.go @@ -31,6 +31,7 @@ type ListRequest struct { Size uint64 `url:"size,omitempty" json:"size,omitempty"` // Find by active True or False. + // Default: true // Required: false Active bool `url:"active" json:"active"` } diff --git a/pkg/cloudbroker/image/list.go b/pkg/cloudbroker/image/list.go index 0e83852..d31b95f 100644 --- a/pkg/cloudbroker/image/list.go +++ b/pkg/cloudbroker/image/list.go @@ -48,15 +48,15 @@ type ListRequest struct { // Find by public True or False // Required: false - Public bool `url:"public,omitempty" json:"public,omitempty"` + Public interface{} `url:"public,omitempty" json:"public,omitempty" validate:"omitempty,isBool"` // Find by hot resize True or False // Required: false - HotResize bool `url:"hotResize,omitempty" json:"hotResize,omitempty"` + HotResize interface{} `url:"hotResize,omitempty" json:"hotResize,omitempty" validate:"omitempty,isBool"` // Find by bootable True or False // Required: false - Bootable bool `url:"bootable,omitempty" json:"bootable,omitempty"` + Bootable interface{} `url:"bootable,omitempty" json:"bootable,omitempty" validate:"omitempty,isBool"` // Sort by one of supported fields, format +|-(field) // Required: false diff --git a/pkg/cloudbroker/image/upload_image_file.go b/pkg/cloudbroker/image/upload_image_file.go new file mode 100644 index 0000000..1268eae --- /dev/null +++ b/pkg/cloudbroker/image/upload_image_file.go @@ -0,0 +1,39 @@ +package image + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "os" +) + +// UploadImageFileResponse struct to enable image +type UploadImageFileResponse struct { + // ImageFileUri + ImageFileUri string `json:"image_file_uri"` +} + +// UploadImageFile uploads file image to platform +func (i Image) UploadImageFile(ctx context.Context, filePath string) (string, error) { + file, err := os.ReadFile(filePath) + if err != nil { + return "", fmt.Errorf("can not read file %v", err) + } + + url := "/cloudbroker/image/uploadImageFile" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, file) + if err != nil { + return "", err + } + + result := UploadImageFileResponse{} + + err = json.Unmarshal(res, &result) + if err != nil { + return "", err + } + + return result.ImageFileUri, nil +} diff --git a/pkg/cloudbroker/kvmx86/create.go b/pkg/cloudbroker/kvmx86/create.go index f97d4b9..e5729a5 100644 --- a/pkg/cloudbroker/kvmx86/create.go +++ b/pkg/cloudbroker/kvmx86/create.go @@ -14,7 +14,8 @@ type Interface struct { // Should be one of: // - VINS // - EXTNET - NetType string `url:"netType" json:"netType" validate:"required,kvmNetType"` + // - VFNIC + NetType string `url:"netType" json:"netType" validate:"required,kvmx86NetType"` // Network ID for connect to, // for EXTNET - external network ID, @@ -74,10 +75,14 @@ type CreateRequest struct { // Required: true RAM uint64 `url:"ram" json:"ram" validate:"required"` + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + // ID of the OS image to base this VM on; // Could be boot disk image or CD-ROM image - // Required: true - ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` // Size of the boot disk in GB // Required: false @@ -136,6 +141,24 @@ type CreateRequest struct { // Required: false Driver string `url:"driver,omitempty" json:"driver,omitempty"` + // Rule for VM placement with NUMA affinity. + // Possible values - none (placement without NUMA affinity), + // strict (strictly with NUMA affinity, if not possible - do not start VM), + // loose (use NUMA affinity if possible) + // Required: false + // Default: none + NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"` + + // Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node + // Required: false + // Default: false + CPUPin bool `url:"cpupin" json:"cpupin"` + + // Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node + // Required: false + // Default: false + HPBacked bool `url:"hpBacked" json:"hpBacked"` + // Reason for action // Required: false Reason string `url:"reason,omitempty" json:"reason,omitempty"` diff --git a/pkg/cloudbroker/kvmx86/create_blank.go b/pkg/cloudbroker/kvmx86/create_blank.go index 05fc414..8660560 100644 --- a/pkg/cloudbroker/kvmx86/create_blank.go +++ b/pkg/cloudbroker/kvmx86/create_blank.go @@ -28,18 +28,22 @@ type CreateBlankRequest struct { // Required: true RAM uint64 `url:"ram" json:"ram" validate:"required"` + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + // Size of the boot disk in GB - // Required: true - BootDisk uint64 `url:"bootDisk" json:"bootDisk" validate:"required"` + // Required: false + BootDisk uint64 `url:"bootDisk,omitempty" json:"bootDisk,omitempty"` // ID of SEP to create boot disk on. // Uses images SEP ID if not set - // Required: true - SEPID uint64 `url:"sepId" json:"sepId" validate:"required"` + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` // Pool to use if sepId is set, can be also empty if needed to be chosen by system - // Required: true - Pool string `url:"pool" json:"pool" validate:"required"` + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` // Slice of structs with data disk description. Each disk has parameters: required - diskName, size; optional - sepId, pool, desc and imageId. // If not specified, compute will be created without disks. diff --git a/pkg/cloudbroker/kvmx86/mass_create.go b/pkg/cloudbroker/kvmx86/mass_create.go index 24986a2..3b43cf7 100644 --- a/pkg/cloudbroker/kvmx86/mass_create.go +++ b/pkg/cloudbroker/kvmx86/mass_create.go @@ -31,9 +31,13 @@ type MassCreateRequest struct { // Required: true RAM uint64 `url:"ram" json:"ram" validate:"required"` - // Image ID - // Required: true - ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + + // ID of the OS image to base this VM on; Could be boot disk image or CD-ROM image + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` // Size of the boot disk in GB // Required: false diff --git a/pkg/cloudbroker/node/models.go b/pkg/cloudbroker/node/models.go index 9bf0f20..6c16450 100644 --- a/pkg/cloudbroker/node/models.go +++ b/pkg/cloudbroker/node/models.go @@ -102,7 +102,7 @@ type ConsumedResourcesInfo struct { // Information about node CPU type CpuInfo struct { // Clock Speed - ClockSpeed uint64 `json:"clockSpeed"` + ClockSpeed float64 `json:"clockSpeed"` // CoreCount CoreCount uint64 `json:"coreCount"` diff --git a/pkg/cloudbroker/rg/list_lb.go b/pkg/cloudbroker/rg/list_lb.go index ea87acd..6832144 100644 --- a/pkg/cloudbroker/rg/list_lb.go +++ b/pkg/cloudbroker/rg/list_lb.go @@ -22,10 +22,6 @@ type ListLBRequest struct { // Required: false Name string `url:"name,omitempty" json:"name,omitempty"` - // Find by account ID - // Required: false - AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` - // Find by tech status // Required: false TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` diff --git a/pkg/cloudbroker/tasks/list.go b/pkg/cloudbroker/tasks/list.go index 781c079..d6b549a 100644 --- a/pkg/cloudbroker/tasks/list.go +++ b/pkg/cloudbroker/tasks/list.go @@ -14,14 +14,17 @@ type ListRequest struct { // Required: false TaskID string `url:"taskId,omitempty" json:"taskId,omitempty"` + // Find by auditId + // Required: false + AuditID string `url:"auditId,omitempty" json:"auditId,omitempty"` + // Find by status // Required: false Status string `url:"status,omitempty" json:"status,omitempty"` // Find by completed True or False - // Default: false // Required: false - Completed bool `url:"completed" json:"completed"` + Completed interface{} `url:"completed,omitempty" json:"completed,omitempty" validate:"omitempty,isBool"` // Sort by one of supported fields, format +|-(field) // Required: false @@ -29,19 +32,21 @@ type ListRequest struct { // Find all tasks after point in time (unixtime) // Required: false - UpdateTimeAt uint64 `url:"page,updateTimeAt" json:"updateTimeAt,omitempty"` + UpdateTimeAt uint64 `url:"updateTimeAt,omitempty" json:"updateTimeAt,omitempty"` // Find all tasks before point in time (unixtime) // Required: false - UpdateTimeTo uint64 `url:"page,updateTimeTo" json:"updateTimeTo,omitempty"` + UpdateTimeTo uint64 `url:"updateTimeTo,omitempty" json:"updateTimeTo,omitempty"` // Page number // Default: 0 + // Default: 0 // Required: false Page uint64 `url:"page,omitempty" json:"page,omitempty"` // Page size // Default: 0 + // Default: 0 // Required: false Size uint64 `url:"size,omitempty" json:"size,omitempty"` } diff --git a/pkg/cloudbroker/user/list.go b/pkg/cloudbroker/user/list.go index 30ff0e2..493cf1f 100644 --- a/pkg/cloudbroker/user/list.go +++ b/pkg/cloudbroker/user/list.go @@ -16,11 +16,11 @@ type ListRequest struct { // Find by active. True or False. // Required: false - Active bool `url:"active,omitempty" json:"active,omitempty"` + Active interface{} `url:"active,omitempty" json:"active,omitempty" validate:"omitempty,isBool"` // Find by serviceaccount. True or False. // Required: false - ServiceAccount bool `url:"serviceaccount,omitempty" json:"serviceaccount,omitempty"` + ServiceAccount interface{} `url:"serviceaccount,omitempty" json:"serviceaccount,omitempty" validate:"omitempty,isBool"` // Sort by one of supported fields, format +|-(field) // Required: false diff --git a/pkg/cloudbroker/vins/dns_apply.go b/pkg/cloudbroker/vins/dns_apply.go index 33dbf50..26e77e6 100644 --- a/pkg/cloudbroker/vins/dns_apply.go +++ b/pkg/cloudbroker/vins/dns_apply.go @@ -15,8 +15,8 @@ type DNSApplyRequest struct { VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` // List of DNS ip address - // Required: true - DNSList []string `url:"dnsList" json:"dnsList" validate:"required"` + // Required: false + DNSList []string `url:"dnsList" json:"dnsList"` } // DNSApply applies new DNS list in VINS diff --git a/pkg/cloudbroker/vins/list.go b/pkg/cloudbroker/vins/list.go index 80c1dee..b8d6f51 100644 --- a/pkg/cloudbroker/vins/list.go +++ b/pkg/cloudbroker/vins/list.go @@ -30,6 +30,10 @@ type ListRequest struct { // Required: false ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + // Find by VNF Device id + // Required: false + VNFDevID uint64 `url:"vnfdevId,omitempty" json:"vnfdevId,omitempty"` + // Include deleted // Required: false IncludeDeleted bool `url:"includeDeleted,omitempty" json:"includeDeleted,omitempty"` diff --git a/tests/platform_upgrade/README.md b/tests/platform_upgrade/README.md index 7f8b4a9..bce82df 100644 --- a/tests/platform_upgrade/README.md +++ b/tests/platform_upgrade/README.md @@ -17,7 +17,11 @@ 1. Тесты находятся по директории `decort-sdk/tests/platform_upgrade` 2. Внутри директории нужно создать и заполнить файл `.env` по аналогии с `.env.template` для доступа к платформе -3. Внутри директории нужно создать и заполнить файл `input.json`, содержащий json с раздела [POST /system/docgenerator/prepareCatalog](https://delta.qa.loc/system/ActorApi?group=system#!/system__docgenerator/post_system_docgenerator_prepareCatalog) (для получения json нажать кнопку Try it Out!) - требуется только для тестов запросов +3. Внутри директории нужно создать и заполнить файл `input.json`, содержащий json с раздела [POST /system/docgenerator/prepareCatalog](https://delta.qa.loc/system/ActorApi?group=system#!/system__docgenerator/post_system_docgenerator_prepareCatalog) (для получения json нажать кнопку Try it Out!) - требуется только для тестов запросов и тестов API методов + +Примечание: тесты можно запускать напрямую методами среды разработки либо из командной строки из нужной директории, например командой `go test -v -run `, где `` - название запускаемого теста. + +Примечание 2: все тесты, кроме `TestGetAllPaths` при первом запуске генерируют фал `.log`. При каждом последующем запуске результаты вывода сравниваются с файлом и в случае, если они одинаковы, то в консоль будет выведено `All lines match the log file.`. В противном случае будут выведены различающиеся строки. ## Тесты Raw методов (Get, List) @@ -90,7 +94,7 @@ FAIL ## Тесты API методов Запустить тест `TestGetAllPaths` в `decort-sdk/tests/platform_upgrade/cloud_test.go` -При наличии подсвеченных ошибок, проверить, что указанные методы API не являются устаревшими (deprecated). В противном случе добавить устаревшие методы в переменную `DEPRECATED_GROUPS` по адресу `decort-sdk/tests/platform_upgrade/utils_url.go`. +При наличии подсвеченных ошибок, проверить, что указанные методы API не являются устаревшими (deprecated). В противном случае добавить устаревшие методы в переменную `DEPRECATED_GROUPS` по адресу `decort-sdk/tests/platform_upgrade/utils_url.go`. Пример вывода: ```go diff --git a/tests/platform_upgrade/cloud_test.go b/tests/platform_upgrade/cloud_test.go index 8da9932..1fa6608 100644 --- a/tests/platform_upgrade/cloud_test.go +++ b/tests/platform_upgrade/cloud_test.go @@ -50,13 +50,16 @@ func TestGetListCloudAPI(t *testing.T) { t.Fatalf("Cannot get client: %v", err) } + const logFileName = "test_get_list_cloudAPI.log" + var testLogs []string // Account // List bytes, err = client.CloudAPI().Account().ListRaw(context.Background(), account.ListRequest{}) if err != nil { t.Error(err) } - getResult("Account list", bytes, account.ListAccounts{}, t) + + testLogs = append(testLogs, getResult("Account list", bytes, account.ListAccounts{}, t)) // Get listAcc, _ := client.CloudAPI().Account().List(context.Background(), account.ListRequest{}) if len(listAcc.Data) > 0 { @@ -65,7 +68,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Account get", bytes, account.RecordAccount{}, t) + testLogs = append(testLogs, getResult("Account get", bytes, account.RecordAccount{}, t)) } else { t.Errorf("Can not test Account get because account list is empty") } @@ -76,7 +79,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Bservice list", bytes, bservice.ListBasicServices{}, t) + testLogs = append(testLogs, getResult("Bservice list", bytes, bservice.ListBasicServices{}, t)) // Get listBServ, _ := client.CloudAPI().BService().List(context.Background(), bservice.ListRequest{}) if len(listBServ.Data) > 0 { @@ -85,7 +88,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Bservice get", bytes, bservice.RecordBasicService{}, t) + testLogs = append(testLogs, getResult("Bservice get", bytes, bservice.RecordBasicService{}, t)) } else { t.Errorf("Can not test Bservice get because bservice list is empty") } @@ -96,7 +99,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Compute list", bytes, compute.ListComputes{}, t) + testLogs = append(testLogs, getResult("Compute list", bytes, compute.ListComputes{}, t)) // Get listComp, _ := client.CloudAPI().Compute().List(context.Background(), compute.ListRequest{}) if len(listComp.Data) > 0 { @@ -105,7 +108,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Compute get", bytes, compute.RecordCompute{}, t) + testLogs = append(testLogs, getResult("Compute get", bytes, compute.RecordCompute{}, t)) } else { t.Errorf("Can not test Compute get because compute list is empty") } @@ -116,7 +119,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Disk list", bytes, disks.ListDisks{}, t) + testLogs = append(testLogs, getResult("Disk list", bytes, disks.ListDisks{}, t)) // Get listDisk, _ := client.CloudAPI().Disks().List(context.Background(), disks.ListRequest{}) if len(listDisk.Data) > 0 { @@ -125,7 +128,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Disk get", bytes, disks.RecordDisk{}, t) + testLogs = append(testLogs, getResult("Disk get", bytes, disks.RecordDisk{}, t)) } else { t.Errorf("Can not test Disk get because disk list is empty") } @@ -136,7 +139,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("ExtNet list", bytes, extnet.ListExtNets{}, t) + testLogs = append(testLogs, getResult("ExtNet list", bytes, extnet.ListExtNets{}, t)) // Get listExtNet, _ := client.CloudAPI().ExtNet().List(context.Background(), extnet.ListRequest{}) if len(listExtNet.Data) > 0 { @@ -145,7 +148,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("ExtNet get", bytes, extnet.RecordExtNet{}, t) + testLogs = append(testLogs, getResult("ExtNet get", bytes, extnet.RecordExtNet{}, t)) } else { t.Errorf("Can not test ExtNet get because listExtNet list is empty") } @@ -156,7 +159,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("FLIPGroup list", bytes, flipgroup.ListFLIPGroups{}, t) + testLogs = append(testLogs, getResult("FLIPGroup list", bytes, flipgroup.ListFLIPGroups{}, t)) // Get listFG, _ := client.CloudAPI().FLIPGroup().List(context.Background(), flipgroup.ListRequest{}) if len(listFG.Data) > 0 { @@ -165,7 +168,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("FLIPGroup get", bytes, flipgroup.RecordFLIPGroup{}, t) + testLogs = append(testLogs, getResult("FLIPGroup get", bytes, flipgroup.RecordFLIPGroup{}, t)) } else { t.Errorf("Can not test FLIPGroup get because flipgroup list is empty") } @@ -176,7 +179,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Image list", bytes, image.ListImages{}, t) + testLogs = append(testLogs, getResult("Image list", bytes, image.ListImages{}, t)) // Get listImg, _ := client.CloudAPI().Image().List(context.Background(), image.ListRequest{}) if len(listImg.Data) > 0 { @@ -185,7 +188,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Image get", bytes, image.RecordImage{}, t) + testLogs = append(testLogs, getResult("Image get", bytes, image.RecordImage{}, t)) } else { t.Errorf("Can not test Image get because Image list is empty") } @@ -196,7 +199,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("K8CI list", bytes, k8ci.ListK8CI{}, t) + testLogs = append(testLogs, getResult("K8CI list", bytes, k8ci.ListK8CI{}, t)) // Get listk8ci, _ := client.CloudAPI().K8CI().List(context.Background(), k8ci.ListRequest{}) if len(listk8ci.Data) > 0 { @@ -205,7 +208,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("K8CI get", bytes, k8ci.RecordK8CI{}, t) + testLogs = append(testLogs, getResult("K8CI get", bytes, k8ci.RecordK8CI{}, t)) } else { t.Errorf("Can not test K8CI get because K8CI list is empty") } @@ -216,7 +219,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("K8S list", bytes, k8s.ListK8SClusters{}, t) + testLogs = append(testLogs, getResult("K8S list", bytes, k8s.ListK8SClusters{}, t)) // Get listk8s, _ := client.CloudAPI().K8S().List(context.Background(), k8s.ListRequest{}) if len(listk8s.Data) > 0 { @@ -225,7 +228,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("K8S get", bytes, k8s.RecordK8S{}, t) + testLogs = append(testLogs, getResult("K8S get", bytes, k8s.RecordK8S{}, t)) } else { t.Errorf("Can not test K8S get because K8S list is empty") } @@ -236,7 +239,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("LB list", bytes, lb.ListLB{}, t) + testLogs = append(testLogs, getResult("LB list", bytes, lb.ListLB{}, t)) // Get listLB, _ := client.CloudAPI().LB().List(context.Background(), lb.ListRequest{}) if len(listLB.Data) > 0 { @@ -245,7 +248,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("LB get", bytes, lb.RecordLB{}, t) + testLogs = append(testLogs, getResult("LB get", bytes, lb.RecordLB{}, t)) } else { t.Errorf("Can not test LB get because LB list is empty") } @@ -256,7 +259,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Locations list", bytes, locations.ListLocations{}, t) + testLogs = append(testLogs, getResult("Locations list", bytes, locations.ListLocations{}, t)) // RG // List @@ -264,7 +267,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("RG list", bytes, rg.ListResourceGroups{}, t) + testLogs = append(testLogs, getResult("RG list", bytes, rg.ListResourceGroups{}, t)) // Get listRG, _ := client.CloudAPI().RG().List(context.Background(), rg.ListRequest{}) if len(listRG.Data) > 0 { @@ -273,7 +276,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("RG get", bytes, rg.RecordResourceGroup{}, t) + testLogs = append(testLogs, getResult("RG get", bytes, rg.RecordResourceGroup{}, t)) } else { t.Errorf("Can not test RG get because RG list is empty") } @@ -284,7 +287,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Sizes list", bytes, sizes.ListSizes{}, t) + testLogs = append(testLogs, getResult("Sizes list", bytes, sizes.ListSizes{}, t)) // Stack // List @@ -292,7 +295,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Stack list", bytes, stack.ListStacks{}, t) + testLogs = append(testLogs, getResult("Stack list", bytes, stack.ListStacks{}, t)) // Tasks // List @@ -300,7 +303,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Tasks list", bytes, tasks.ListTasks{}, t) + testLogs = append(testLogs, getResult("Tasks list", bytes, tasks.ListTasks{}, t)) // Get listTasks, _ := client.CloudAPI().Tasks().List(context.Background(), tasks.ListRequest{}) if len(listTasks.Data) > 0 { @@ -309,7 +312,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("Tasks get", bytes, tasks.RecordAsyncTask{}, t) + testLogs = append(testLogs, getResult("Tasks get", bytes, tasks.RecordAsyncTask{}, t)) } else { t.Errorf("Can not test Tasks get because Tasks list is empty") } @@ -320,7 +323,7 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("VINS list", bytes, vins.ListVINS{}, t) + testLogs = append(testLogs, getResult("VINS list", bytes, vins.ListVINS{}, t)) // Get listVINS, _ := client.CloudAPI().VINS().List(context.Background(), vins.ListRequest{}) if len(listVINS.Data) > 0 { @@ -329,10 +332,12 @@ func TestGetListCloudAPI(t *testing.T) { if err != nil { t.Error(err) } - getResult("VINS get", bytes, vins.RecordVINS{}, t) + testLogs = append(testLogs, getResult("VINS get", bytes, vins.RecordVINS{}, t)) } else { t.Errorf("Can not test VINS get because VINS list is empty") } + + compareLogs(logFileName, testLogs, t, "get") } // WARNING: not working correctly due to inclusions of tagless structures in cloudbroker @@ -345,13 +350,15 @@ func TestGetListCloudbroker(t *testing.T) { t.Fatalf("Cannot get client: %v", err) } + const logFileName = "test_get_list_cloudbroker.log" + var testLogs = make([]string, 0) // Account // List bytes, err = client.CloudBroker().Account().ListRaw(context.Background(), account_cb.ListRequest{}) if err != nil { t.Error(err) } - getResult("Account list", bytes, account_cb.ListAccounts{}, t) + testLogs = append(testLogs, getResult("Account list", bytes, account_cb.ListAccounts{}, t)) // Get listAcc, _ := client.CloudBroker().Account().List(context.Background(), account_cb.ListRequest{}) if len(listAcc.Data) > 0 { @@ -360,7 +367,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Account get", bytes, account_cb.RecordAccount{}, t) + testLogs = append(testLogs, getResult("Account get", bytes, account_cb.RecordAccount{}, t)) } else { t.Errorf("Can not test Account get because account list is empty") } @@ -371,7 +378,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Audit list", bytes, audit_cb.ListAudits{}, t) + testLogs = append(testLogs, getResult("Audit list", bytes, audit_cb.ListAudits{}, t)) // Get listAudits, _ := client.CloudBroker().Audit().List(context.Background(), audit_cb.ListRequest{}) if len(listAudits.Data) > 0 { @@ -380,7 +387,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Audit get", bytes, audit_cb.RecordAudit{}, t) + testLogs = append(testLogs, getResult("Audit get", bytes, audit_cb.RecordAudit{}, t)) } else { t.Errorf("Can not test Audit get because Audit list is empty") } @@ -391,7 +398,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Compute list", bytes, compute_cb.ListComputes{}, t) + testLogs = append(testLogs, getResult("Compute list", bytes, compute_cb.ListComputes{}, t)) // Get listComp, _ := client.CloudBroker().Compute().List(context.Background(), compute_cb.ListRequest{}) if len(listComp.Data) > 0 { @@ -400,7 +407,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Compute get", bytes, compute_cb.RecordCompute{}, t) + testLogs = append(testLogs, getResult("Compute get", bytes, compute_cb.RecordCompute{}, t)) } else { t.Errorf("Can not test Compute get because compute list is empty") } @@ -411,7 +418,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Disk list", bytes, disks_cb.ListDisks{}, t) + testLogs = append(testLogs, getResult("Disk list", bytes, disks_cb.ListDisks{}, t)) // Get listDisk, _ := client.CloudBroker().Disks().List(context.Background(), disks_cb.ListRequest{}) if len(listDisk.Data) > 0 { @@ -420,7 +427,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Disk get", bytes, disks_cb.RecordDisk{}, t) + testLogs = append(testLogs, getResult("Disk get", bytes, disks_cb.RecordDisk{}, t)) } else { t.Errorf("Can not test Disk get because disk list is empty") } @@ -431,7 +438,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("ExtNet list", bytes, extnet_cb.ListExtNet{}, t) + testLogs = append(testLogs, getResult("ExtNet list", bytes, extnet_cb.ListExtNet{}, t)) // Get listExtNet, _ := client.CloudBroker().ExtNet().List(context.Background(), extnet_cb.ListRequest{}) if len(listExtNet.Data) > 0 { @@ -440,7 +447,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("ExtNet get", bytes, extnet_cb.RecordExtNet{}, t) + testLogs = append(testLogs, getResult("ExtNet get", bytes, extnet_cb.RecordExtNet{}, t)) } else { t.Errorf("Can not test ExtNet get because listExtNet list is empty") } @@ -451,7 +458,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("FLIPGroup list", bytes, flipgroup_cb.ListFLIPGroups{}, t) + testLogs = append(testLogs, getResult("FLIPGroup list", bytes, flipgroup_cb.ListFLIPGroups{}, t)) // Get listFG, _ := client.CloudBroker().FLIPGroup().List(context.Background(), flipgroup_cb.ListRequest{}) if len(listFG.Data) > 0 { @@ -460,7 +467,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("FLIPGroup get", bytes, flipgroup_cb.RecordFLIPGroup{}, t) + testLogs = append(testLogs, getResult("FLIPGroup get", bytes, flipgroup_cb.RecordFLIPGroup{}, t)) } else { t.Errorf("Can not test FLIPGroup get because flipgroup list is empty") } @@ -471,7 +478,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Grid list", bytes, grid_cb.ListGrids{}, t) + testLogs = append(testLogs, getResult("Grid list", bytes, grid_cb.ListGrids{}, t)) // Get listGrid, _ := client.CloudBroker().Grid().List(context.Background(), grid_cb.ListRequest{}) if len(listGrid.Data) > 0 { @@ -480,7 +487,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Grid get", bytes, grid_cb.RecordGrid{}, t) + testLogs = append(testLogs, getResult("Grid get", bytes, grid_cb.RecordGrid{}, t)) } else { t.Errorf("Can not test Grid get because Grid list is empty") } @@ -491,7 +498,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Image list", bytes, image_cb.ListImages{}, t) + testLogs = append(testLogs, getResult("Image list", bytes, image_cb.ListImages{}, t)) // Get listImg, _ := client.CloudBroker().Image().List(context.Background(), image_cb.ListRequest{}) if len(listImg.Data) > 0 { @@ -500,7 +507,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Image get", bytes, image_cb.RecordImage{}, t) + testLogs = append(testLogs, getResult("Image get", bytes, image_cb.RecordImage{}, t)) } else { t.Errorf("Can not test Image get because Image list is empty") } @@ -511,7 +518,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("K8CI list", bytes, k8ci_cb.ListK8CI{}, t) + testLogs = append(testLogs, getResult("K8CI list", bytes, k8ci_cb.ListK8CI{}, t)) // Get listk8ci, _ := client.CloudBroker().K8CI().List(context.Background(), k8ci_cb.ListRequest{}) if len(listk8ci.Data) > 0 { @@ -520,7 +527,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("K8CI get", bytes, k8ci_cb.RecordK8CI{}, t) + testLogs = append(testLogs, getResult("K8CI get", bytes, k8ci_cb.RecordK8CI{}, t)) } else { t.Errorf("Can not test K8CI get because K8CI list is empty") } @@ -531,7 +538,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("K8S list", bytes, k8s_cb.ListK8S{}, t) + testLogs = append(testLogs, getResult("K8S list", bytes, k8s_cb.ListK8S{}, t)) // Get listk8s, _ := client.CloudBroker().K8S().List(context.Background(), k8s_cb.ListRequest{}) if len(listk8s.Data) > 0 { @@ -540,7 +547,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("K8S get", bytes, k8s_cb.RecordK8S{}, t) + testLogs = append(testLogs, getResult("K8S get", bytes, k8s_cb.RecordK8S{}, t)) } else { t.Errorf("Can not test K8S get because K8S list is empty") } @@ -551,7 +558,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("LB list", bytes, lb_cb.ListLB{}, t) + testLogs = append(testLogs, getResult("LB list", bytes, lb_cb.ListLB{}, t)) // Get listLB, _ := client.CloudBroker().LB().List(context.Background(), lb_cb.ListRequest{}) if len(listLB.Data) > 0 { @@ -560,7 +567,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("LB get", bytes, lb_cb.RecordLB{}, t) + testLogs = append(testLogs, getResult("LB get", bytes, lb_cb.RecordLB{}, t)) } else { t.Errorf("Can not test LB get because LB list is empty") } @@ -571,7 +578,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Pcidevice list", bytes, pcidevice_cb.ListPCIDevices{}, t) + testLogs = append(testLogs, getResult("Pcidevice list", bytes, pcidevice_cb.ListPCIDevices{}, t)) // RG // List @@ -579,7 +586,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("RG list", bytes, rg_cb.ListRG{}, t) + testLogs = append(testLogs, getResult("RG list", bytes, rg_cb.ListRG{}, t)) // Get listRG, _ := client.CloudBroker().RG().List(context.Background(), rg_cb.ListRequest{}) if len(listRG.Data) > 0 { @@ -588,7 +595,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("RG get", bytes, rg_cb.RecordRG{}, t) + testLogs = append(testLogs, getResult("RG get", bytes, rg_cb.RecordRG{}, t)) } else { t.Errorf("Can not test RG get because RG list is empty") } @@ -599,7 +606,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("SEP list", bytes, sep_cb.ListSEP{}, t) + testLogs = append(testLogs, getResult("SEP list", bytes, sep_cb.ListSEP{}, t)) // Get listSEP, _ := client.CloudBroker().SEP().List(context.Background(), sep_cb.ListRequest{}) if len(listSEP.Data) > 0 { @@ -608,7 +615,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("SEP get", bytes, sep_cb.RecordSEP{}, t) + testLogs = append(testLogs, getResult("SEP get", bytes, sep_cb.RecordSEP{}, t)) } else { t.Errorf("Can not test SEP get because SEP list is empty") } @@ -619,7 +626,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Stack list", bytes, stack_cb.ListStacks{}, t) + testLogs = append(testLogs, getResult("Stack list", bytes, stack_cb.ListStacks{}, t)) // Get listStack, _ := client.CloudBroker().Stack().List(context.Background(), stack_cb.ListRequest{}) if len(listStack.Data) > 0 { @@ -628,7 +635,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Stack get", bytes, stack_cb.InfoStack{}, t) + testLogs = append(testLogs, getResult("Stack get", bytes, stack_cb.InfoStack{}, t)) } else { t.Errorf("Can not test Stack get because Stack list is empty") } @@ -639,7 +646,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("Tasks list", bytes, tasks_cb.ListTasks{}, t) + testLogs = append(testLogs, getResult("Tasks list", bytes, tasks_cb.ListTasks{}, t)) // VINS // List @@ -647,7 +654,7 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("VINS list", bytes, vins_cb.ListVINS{}, t) + testLogs = append(testLogs, getResult("VINS list", bytes, vins_cb.ListVINS{}, t)) // Get listVINS, _ := client.CloudBroker().VINS().List(context.Background(), vins_cb.ListRequest{}) if len(listVINS.Data) > 0 { @@ -656,10 +663,11 @@ func TestGetListCloudbroker(t *testing.T) { if err != nil { t.Error(err) } - getResult("VINS get", bytes, vins_cb.RecordVINS{}, t) + testLogs = append(testLogs, getResult("VINS get", bytes, vins_cb.RecordVINS{}, t)) } else { t.Errorf("Can not test VINS get because VINS list is empty") } + compareLogs(logFileName, testLogs, t, "get") } // TestRequestsCloudAPI tests platform requests vs. golang request structures in sdk for cloudapi requests diff --git a/tests/platform_upgrade/request_map.go b/tests/platform_upgrade/request_map.go index 5a2817d..ef38117 100644 --- a/tests/platform_upgrade/request_map.go +++ b/tests/platform_upgrade/request_map.go @@ -557,6 +557,7 @@ func getRequestsMapCloudbroker() map[string]interface{} { "/cloudbroker/disks/create": disks_cb.CreateRequest{}, "/cloudbroker/disks/delete": disks_cb.DeleteRequest{}, "/cloudbroker/disks/deleteDisks": disks_cb.DeleteDisksRequest{}, + "/cloudbroker/disks/depresent": disks_cb.DepresentRequest{}, "/cloudbroker/disks/fromPlatformDisk": disks_cb.FromPlatformDiskRequest{}, "/cloudbroker/disks/get": disks_cb.GetRequest{}, "/cloudbroker/disks/limitIO": disks_cb.LimitIORequest{}, @@ -565,6 +566,7 @@ func getRequestsMapCloudbroker() map[string]interface{} { "/cloudbroker/disks/listTypes": disks_cb.ListTypesRequest{}, "/cloudbroker/disks/listUnattached": disks_cb.ListUnattachedRequest{}, "/cloudbroker/disks/rename": disks_cb.RenameRequest{}, + "/cloudbroker/disks/present": disks_cb.PresentRequest{}, "/cloudbroker/disks/replicate": disks_cb.ReplicateRequest{}, "/cloudbroker/disks/replicationResume": disks_cb.ReplicationResumeRequest{}, "/cloudbroker/disks/replicationReverse": disks_cb.ReplicationReverseRequest{}, diff --git a/tests/platform_upgrade/test_get_list_cloudAPI.log b/tests/platform_upgrade/test_get_list_cloudAPI.log new file mode 100644 index 0000000..d69e59b --- /dev/null +++ b/tests/platform_upgrade/test_get_list_cloudAPI.log @@ -0,0 +1,31 @@ +[ + "Account list: OK", + "Account get: OK", + "", + "Bservice get: OK", + "Compute list: \nPlatform has these fields that golang struct doesn't: [lb mgmt_target mgmt_mac mgmt_slot]\n", + "Compute get: \nPlatform has these fields that golang struct doesn't: [mgmt_target mgmt_mac mgmt_slot lb]\n", + "Disk list: OK", + "Disk get: OK", + "", + "ExtNet get: OK", + "FLIPGroup list: OK", + "", + "", + "Image get: \nPlatform has these fields that golang struct doesn't: [12]\n", + "K8CI list: OK", + "", + "K8S list: OK", + "K8S get: OK", + "LB list: \nPlatform has these fields that golang struct doesn't: [fs.inotify.max_queued_events kernel.kptr_restrict net.ipv4.tcp_congestion_control]\n", + "LB get: \nPlatform has these fields that golang struct doesn't: [kernel.kptr_restrict net.ipv4.tcp_congestion_control fs.inotify.max_queued_events]\n", + "Locations list: OK", + "RG list: OK", + "", + "Sizes list: OK", + "", + "Tasks list: \nPlatform has these fields that golang struct doesn't: [completed guid stage updateTime updatedTime auditId error log status]\n", + "Tasks get: \nPlatform has these fields that golang struct doesn't: [updatedTime completed error updateTime auditId stage status log]\n", + "", + "VINS get: OK" +] \ No newline at end of file diff --git a/tests/platform_upgrade/test_get_list_cloudbroker.log b/tests/platform_upgrade/test_get_list_cloudbroker.log new file mode 100644 index 0000000..d9e7152 --- /dev/null +++ b/tests/platform_upgrade/test_get_list_cloudbroker.log @@ -0,0 +1,34 @@ +[ + "Account list: OK", + "Account get: OK", + "", + "Audit get: OK", + "Compute list: \nPlatform has these fields that golang struct doesn't: [disks]\n", + "Compute get: OK", + "Disk list: \nPlatform has these fields that golang struct doesn't: [machineId machineName sepType devicename]\n", + "Disk get: \nPlatform has these fields that golang struct doesn't: [devicename sepType updatedBy]\n", + "ExtNet list: OK", + "ExtNet get: OK", + "FLIPGroup list: OK", + "", + "Grid list: \nPlatform has these fields that golang struct doesn't: [1 90 380 1 90 380]\n", + "Grid get: OK", + "Image list: OK", + "Image get: OK", + "K8CI list: \nPlatform has these fields that golang struct doesn't: [createdTime]\n", + "K8CI get: OK", + "K8S list: OK", + "K8S get: OK", + "LB list: OK", + "LB get: OK", + "Pcidevice list: OK", + "RG list: OK", + "RG get: OK", + "SEP list: \nPlatform has these fields that golang struct doesn't: [protocol disk_max_size format name name_prefix pools]\n", + "SEP get: \nPlatform has these fields that golang struct doesn't: [format name name_prefix pools protocol disk_max_size]\n", + "Stack list: OK", + "Stack get: OK", + "Tasks list: \nPlatform has these fields that golang struct doesn't: [stage status error log auditId completed updateTime guid]\n", + "", + "VINS get: \nPlatform has these fields that golang struct doesn't: [computes config config config]\n" +] \ No newline at end of file diff --git a/tests/platform_upgrade/test_requests_cloudAPI.log b/tests/platform_upgrade/test_requests_cloudAPI.log new file mode 100644 index 0000000..9324d11 --- /dev/null +++ b/tests/platform_upgrade/test_requests_cloudAPI.log @@ -0,0 +1,14 @@ +[ + "Path /cloudapi/compute/antiAffinityRuleAdd has following errors: [Field value has different required parameters on the platform and in golang structure]", + "Path /cloudapi/user/setData has following errors: [Field data has different required parameters on the platform and in golang structure]", + "Path /cloudapi/compute/affinityRuleRemove has following errors: [Field value has different required parameters on the platform and in golang structure]", + "Path /cloudapi/compute/createTemplate has following errors: [Platform (3) and golang structure (2) have different amount of fields. Platform has field asyncMode that golang structure doesn't]", + "Path /cloudapi/k8s/create has following errors: [Field oidcCertificate has different type parameters on the platform and in golang structure]", + "Path /cloudapi/disks/fromPlatformDisk has following errors: [Platform (14) and golang structure (13) have different amount of fields. Field drivers has different type parameters on the platform and in golang structure Platform has field asyncMode that golang structure doesn't]", + "Path /cloudapi/compute/affinityRuleAdd has following errors: [Field value has different required parameters on the platform and in golang structure]", + "Path /cloudapi/compute/antiAffinityRuleRemove has following errors: [Field value has different required parameters on the platform and in golang structure]", + "Path /cloudapi/compute/createTemplateFromBlank has following errors: [Platform (11) and golang structure (10) have different amount of fields. Platform has field asyncMode that golang structure doesn't]", + "Path /cloudapi/lb/create has following errors: [Field extnetId has different required parameters on the platform and in golang structure Field vinsId has different required parameters on the platform and in golang structure]", + "Path /cloudapi/image/list has following errors: [Field size has different type parameters on the platform and in golang structure]", + "Path /cloudapi/compute/snapshotDelete has following errors: [Platform (3) and golang structure (2) have different amount of fields. Platform has field asyncMode that golang structure doesn't]" +] \ No newline at end of file diff --git a/tests/platform_upgrade/test_requests_cloudbroker.log b/tests/platform_upgrade/test_requests_cloudbroker.log new file mode 100644 index 0000000..2c619b8 --- /dev/null +++ b/tests/platform_upgrade/test_requests_cloudbroker.log @@ -0,0 +1 @@ +["Path /cloudbroker/compute/createTemplate has following errors: [Platform (4) and golang structure (3) have different amount of fields. Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/backup/createDisksBackup has following errors: [Platform (3) and golang structure (2) have different amount of fields. Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/compute/affinityRuleAdd has following errors: [Field value has different required parameters on the platform and in golang structure]","Path /cloudbroker/apiaccess/update has following errors: [Platform has field apis that golang structure doesn't]","Path /cloudbroker/compute/antiAffinityRuleRemove has following errors: [Field value has different required parameters on the platform and in golang structure]","Path /cloudbroker/compute/snapshotDelete has following errors: [Platform (3) and golang structure (2) have different amount of fields. Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/backup/restoreDiskFromBackup has following errors: [Platform (5) and golang structure (4) have different amount of fields. Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/backup/createDiskBackup has following errors: [Platform (4) and golang structure (3) have different amount of fields. Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/compute/createTemplateFromBlank has following errors: [Platform (11) and golang structure (10) have different amount of fields. Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/account/setCpuAllocationRatio has following errors: [Field ratio has different required parameters on the platform and in golang structure]","Path /cloudbroker/disks/fromPlatformDisk has following errors: [Platform (14) and golang structure (13) have different amount of fields. Field drivers has different type parameters on the platform and in golang structure Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/lb/create has following errors: [Field extnetId has different required parameters on the platform and in golang structure Field vinsId has different required parameters on the platform and in golang structure]","Path /cloudbroker/stack/setCpuAllocationRatio has following errors: [Field ratio has different required parameters on the platform and in golang structure]","Path /cloudbroker/backup/deleteDiskBackup has following errors: [Platform (3) and golang structure (2) have different amount of fields. Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/image/updateNodes has following errors: [Field enabledStacks has different type parameters on the platform and in golang structure]","Path /cloudbroker/stack/setMemAllocationRatio has following errors: [Field ratio has different required parameters on the platform and in golang structure]","Path /cloudbroker/compute/antiAffinityRuleAdd has following errors: [Field value has different required parameters on the platform and in golang structure]","Path /cloudbroker/k8s/create has following errors: [Field oidcCertificate has different type parameters on the platform and in golang structure]","Path /cloudbroker/backup/restoreDisksFromBackup has following errors: [Platform (3) and golang structure (2) have different amount of fields. Platform has field disks that golang structure doesn't Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/node/enable has following errors: [Platform (4) and golang structure (3) have different amount of fields. Platform has field asyncMode that golang structure doesn't]","Path /cloudbroker/compute/affinityRuleRemove has following errors: [Field value has different required parameters on the platform and in golang structure]","Path /cloudbroker/image/list has following errors: [Field size has different type parameters on the platform and in golang structure]"] \ No newline at end of file diff --git a/tests/platform_upgrade/utils_get_list.go b/tests/platform_upgrade/utils_get_list.go index 01ca31b..12f5ddd 100644 --- a/tests/platform_upgrade/utils_get_list.go +++ b/tests/platform_upgrade/utils_get_list.go @@ -56,7 +56,7 @@ func getClient() (*decort.DecortClient, error) { } // getResult checks how json-structure of bytes is different from golang structure and return t.Error if there are any differences. -func getResult(testName string, bytes []byte, structure any, t *testing.T) { +func getResult(testName string, bytes []byte, structure any, t *testing.T) string { // convert bytes to map[string]interface{} bytesMap, err := GetMapFromBytes(bytes) if err != nil { @@ -68,8 +68,11 @@ func getResult(testName string, bytes []byte, structure any, t *testing.T) { // assert if they are equal if !reflect.DeepEqual(bytesMap, structMap) { - t.Errorf(getDifference(testName, bytesMap, structMap)) + var result = getDifference(testName, bytesMap, structMap) + t.Errorf(result) + return result } + return "" } // getDifference tells which fields are present in bytesMap and not present in structMap and vice versa. diff --git a/tests/platform_upgrade/utils_log.go b/tests/platform_upgrade/utils_log.go new file mode 100644 index 0000000..39eeb84 --- /dev/null +++ b/tests/platform_upgrade/utils_log.go @@ -0,0 +1,119 @@ +package test + +import ( + "bufio" + "encoding/json" + "io" + "os" + "regexp" + "sort" + "strings" + "testing" +) + +func compareLogs(logFileName string, logsData []string, t *testing.T, testType string) { + if _, err := os.Stat(logFileName); os.IsNotExist(err) { + file, err := os.Create(logFileName) + if err != nil { + t.Errorf("Failed to create log file: %v", err) + } + defer file.Close() + + writer := bufio.NewWriter(file) + + jsonData, err := json.MarshalIndent(logsData, "", " ") + if err != nil { + t.Errorf("Failed to marshal logsData to JSON: %v", err) + } + + _, err = writer.WriteString(string(jsonData)) + if err != nil { + t.Errorf("Failed to write JSON to log file: %v", err) + } + + writer.Flush() + } else { + file, err := os.Open(logFileName) + if err != nil { + t.Errorf("Failed to open log file: %v", err) + } + defer file.Close() + + reader := bufio.NewReader(file) + var sb strings.Builder + for { + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + sb.WriteString(strings.TrimSpace(line)) + break + } + t.Errorf("Error reading log file: %v", err) + return + } + sb.WriteString(strings.TrimSpace(line)) + } + + var fileContent = sb.String() + + var fileLogsData []string + err = json.Unmarshal([]byte(fileContent), &fileLogsData) + if err != nil { + t.Errorf("Failed to unmarshal JSON from log file: %v", err) + } + + if len(fileLogsData) != len(logsData) { + t.Errorf("Log data length does not match. Got: %d, Expected: %d", len(fileLogsData), len(logsData)) + } + + var allLinesMatch = true + + switch testType { + default: + for i := range logsData { + if sortBracketsContent(fileLogsData[i]) != sortBracketsContent(logsData[i]) { + allLinesMatch = false + t.Errorf("Line %d does not match. Got: %s, Expected: %s", i+1, sortBracketsContent(fileLogsData[i]), sortBracketsContent(logsData[i])) + } + } + if allLinesMatch { + t.Log("\nAll lines match the log file.") + } + case "request": + var tmp = make(map[string]struct{}) + for _, v := range fileLogsData { + if _, ok := tmp[v]; ok { + delete(tmp, v) + } else { + tmp[v] = struct{}{} + } + + } + for _, v := range logsData { + if _, ok := tmp[v]; ok { + delete(tmp, v) + } else { + tmp[v] = struct{}{} + } + + } + if len(tmp) == 0 { + t.Log("\nAll lines match the log file.") + return + } + for i := range tmp { + t.Errorf("Line %s does not match.", i) + } + } + } +} + +func sortBracketsContent(log string) string { + re := regexp.MustCompile(`\[([^\[\]]*)\]`) + return re.ReplaceAllStringFunc(log, func(match string) string { + content := match[1 : len(match)-1] + parts := strings.Split(content, " ") + sort.Strings(parts) + return "[" + strings.Join(parts, " ") + "]" + }) +} diff --git a/tests/platform_upgrade/utils_requests.go b/tests/platform_upgrade/utils_requests.go index c25d795..233e72d 100644 --- a/tests/platform_upgrade/utils_requests.go +++ b/tests/platform_upgrade/utils_requests.go @@ -51,16 +51,21 @@ func getBytesFromJSON(fileName string, t *testing.T) []byte { func getErrorsFromJSON(bytes []byte, t *testing.T, cloud string) { var requests map[string]interface{} + var logFileName string switch cloud { case "cloudapi": requests = getRequestsMapCloudAPI() + logFileName = "test_requests_cloudAPI.log" case "cloudbroker": requests = getRequestsMapCloudbroker() + logFileName = "test_requests_cloudbroker.log" default: t.Fatalf("Wrong cloud provided, expected `cloudapi` or `cloudbroker`, got %s", cloud) } + var dataLogs []string + paths, err := getMapFromFile(bytes) if err != nil { t.Error(err) @@ -131,14 +136,19 @@ func getErrorsFromJSON(bytes []byte, t *testing.T, cloud string) { } } if len(errs) > 0 { - t.Errorf("Path %s has following errors: %v", k, errs) + msg := fmt.Sprintf("Path %s has following errors: %v", k, errs) + t.Error(msg) + dataLogs = append(dataLogs, msg) } } if len(requests) != i { - t.Errorf("Amount of structure checked (%d) is not the same as amount of platform requests available (%d), please check getRequestsMapCloudAPI func in code.", + msg := fmt.Sprintf("Amount of structure checked (%d) is not the same as amount of platform requests available (%d), please check getRequestsMapCloudAPI func in code.", i, len(requests)) + t.Error(msg) + dataLogs = append(dataLogs, msg) } + compareLogs(logFileName, dataLogs, t, "request") } // checkName checks if name field from platform has the same value as json tag in golang structure (maybe including omitempty) @@ -211,8 +221,13 @@ func checkKind(platformType, items string, typ reflect.Type) bool { return true } return false + + default: // for cases like dataDisks etc. + return true + } - } - return false + default: // for cases like drivers etc + return true + } }