This commit is contained in:
2026-09-25 16:32:09 +03:00
parent bf932e5ef9
commit 69f056616f
33 changed files with 2074 additions and 594 deletions

View File

@@ -1,4 +1,4 @@
## Version 1.16.4 ## Version 1.16.5
### Deprecated ### Deprecated
@@ -18,98 +18,62 @@
| `rg/AccessRevoke` | :heavy_check_mark: | :heavy_check_mark: | | `rg/AccessRevoke` | :heavy_check_mark: | :heavy_check_mark: |
| `sep/ConfigFieldEdit` | :x: | :heavy_check_mark: | | `sep/ConfigFieldEdit` | :x: | :heavy_check_mark: |
### Добавлено ### Добавлено
#### apiaccess #### audit
| Идентификатор задачи | Описание | | Идентификатор задачи | Описание |
| --- | --- | | --- | --- |
| BGOS-1055 | Структура `SDNEndpoints` и опциональное поле `SDN` в структуру `APIsEndpoints` в cloudbroker/apiaccess | | BGOS-1072 | Вычисляемые поля `ResgroupID`, `AccountID`, `ComputeID`, `VINSID`, `ServiceID`, `K8SID`, `FLIPGroupID`, `LBID`, `SEPID`, `NodeIDs`, `DiskIDs` и `APITask` в структуру ответа `ItemAudit` в cloudapi/audit |
| BGOS-1055 | Опциональные поля `Audit`, `DPDKNet`, `SecurityGroup`, `SEP`, `StoragePolicy`, `Trunk`, `VFPool`, `VMFolder`, `Zone` в структуру `CloudAPIEndpoints` в cloudbroker/apiaccess | | BGOS-1072 | Вычисляемые поля `ResgroupID`, `AccountID`, `ComputeID`, `VINSID`, `ServiceID`, `K8SID`, `FLIPGroupID`, `LBID`, `SEPID`, `NodeIDs`, `DiskIDs` и `APITask` в структуру ответа `ItemAudit` в cloudbroker/audit |
| BGOS-1055 | Опциональные поля `Backup`, `DPDKNet`, `SecurityGroup`, `StoragePolicy`, `Trunk`, `VFPool`, `VMFolder`, `Zone` в структуру `CloudBrokerEndpoints` в cloudbroker/apiaccess |
| BGOS-1055 | Опциональные поля `Cache`, `InfoWellknown` в структуру `SystemEndpoints` в cloudbroker/apiaccess |
#### compute #### compute
| Идентификатор задачи | Описание | | Идентификатор задачи | Описание |
| --- | --- | | --- | --- |
| BGOS-1043 | Опциональное поле `AsyncMode` в структуру запроса `SnapshotEvictDiskRequest` в cloudbroker/compute | | BGOS-1069 | Вычисляемое поле `FolderID` в структуры ответа `RecordCompute` и `ItemCompute` в cloudapi/compute |
| BGOS-1044 | Методы `AsyncAffinityRuleAdd` и `AsyncAntiAffinityRuleAdd` в cloudapi/compute и cloudbroker/compute | | BGOS-1069 | Вычисляемое поле `FolderID` в структуры ответа `RecordCompute` и `InfoCompute` в cloudapi/compute |
| BGOS-1054 | Опциональные поля `FolderID` и `IncludeDescendants` в структуру запроса `ListRequest` в cloudapi/compute | | BGOS-1068 | Структуры ответа `MigrationReport`, `MigrationReportItem`, `AffinityRuleAddResult` и `AntiAffinityRuleAddResult` в cloudapi/compute |
| BGOS-1054 | Опциональные поля `FolderID` и `IncludeDescendants` в структуру запроса `ListRequest` в cloudbroker/compute | | BGOS-1068 | Структуры ответа `MigrationReport`, `MigrationReportItem`, `AffinityRuleAddResult` и `AntiAffinityRuleAddResult` в cloudbroker/compute |
| BGOS-1046 | Опциональное поле `Migrate` в структуры запросов `AffinityRuleAddRequest` и `AntiAffinityRuleAddRequest` в cloudapi/compute | | BGOS-1063 | Структуры запроса `MigrateV2Request`, `MigrateV2AbortRequest`, `MigrateV2CleanupRequest`, `MigrateV2GetRequest`, `MigrateV2ListRequest` и `MigrateV2ResolveRequest` и методы `MigrateV2`, `MigrateV2Abort`, `MigrateV2Cleanup`, `MigrateV2Get`, `MigrateV2List` и `MigrateV2ResolveRequest` в cloudbroker/compute |
| BGOS-1046 | Опциональное поле `Migrate` в структуры запросов `AffinityRuleAddRequest` и `AntiAffinityRuleAddRequest` в cloudbroker/compute | | BGOS-1080 | Опциональное поле `AccountID` из структуры запроса `CreateTemplateFromBlankRequest` в cloudapi/compute |
#### session
| Идентификатор задачи | Описание |
| --- | --- |
| BGOS-1048 | Опциональное поле `IncludeArchive` в структуру запроса `ListRequest` в cloudbroker/session |
#### stpolicy
| Идентификатор задачи | Описание |
| --- | --- |
| BGOS-1047 | Вычисляемое поле `ReplicationProfileID` в структуры ответа `InfoStoragePolicy` и `ItemStoragePolicy` в cloudapi/stpolicy и в cloudbroker/stpolicy |
| BGOS-1047 | Вычисляемое поле `ReplicationProfileID` в структуру ответа `InfoStoragePolicyWithID` в cloudbroker/stpolicy |
| BGOS-1047 | Методы `GetReplicationProfile`, `ListReplicationProfile`, структуры запроса `GetReplicationProfileRequest`, `ListReplicationProfileRequest` и структуры ответа `InfoReplicationProfile`, `ListReplicationProfiles`, `ItemReplicationProfile`, `ListSchedules`, `Schedule`, `Calendar` в cloudapi/stpolicy и в cloudbroker/stpolicy |
| BGOS-1047 | Метод `CreateReplicationProfile`, структуры запроса `CreateReplicationProfileRequest`, `SchedulesRequest`, `CalendarRequest` в cloudbroker/stpolicy |
| BGOS-1047 | Метод `UpdateReplicationProfile`, структуры запроса `UpdateReplicationProfileRequest`, `ConfigRequest` в cloudbroker/stpolicy |
| BGOS-1047 | Метод `DeleteReplicationProfile` и структура запроса `DeleteReplicationProfileRequest` в cloudbroker/stpolicy |
| BGOS-1047 | Методы `AddAccessReplicationProfile`, `RevokeAccessReplicationProfile` и структуры запроса `AddAccessReplicationProfileRequest`, `RevokeAccessReplicationProfileRequest` в cloudbroker/stpolicy |
#### vmfolder
| Идентификатор задачи | Описание |
| --- | --- |
| BGOS-1049 | Группа апи методов vmfolder в cloudapi и cloudbroker |
### Изменено
#### backup
| Идентификатор задачи | Описание |
| --- | --- |
| BGOS-1045 | JSON-тэг опционального поля `AsyncMode` с `asyncMode` на `async_mode` в структурах запроса `CreateDiskBackupRequest`, `CreateDisksBackupRequest`, `DeleteDiskBackupRequest`, `RestoreDiskFromBackupRequest` и `RestoreDisksFromBackupRequest` в cloudbroker/backup |
#### compute
| Идентификатор задачи | Описание |
| --- | --- |
| BGOS-1043 | JSON-тэг опционального поля `AsyncMode` с `asyncMode` на `async_mode` в структурах запроса `AbortSharedSnapshotMergeRequest`, `CDEjectRequest`, `CDInsertRequest`, `ChangeIPRequest`, `ChangeLinkStateRequest`, `ChangeMTURequest`, `ChangeQoSPoliciesRequest`, `ChangeSecGroupsRequest`, `CloneRequest`, `CreateTemplateRequest`, `CreateTemplateFromBlankRequest`, `DeleteRequest`, `DisableRequest`, `DiskAddRequest`, `DiskAttachRequest`, `DiskDelRequest`, `DiskDetachRequest`, `DiskQOSRequest`, `DiskResizeRequest`, `DiskSwitchToReplicationRequest`, `GuestAgentDisableRequest`, `GuestAgentEnableRequest`, `GuestAgentExecuteRequest`, `GuestAgentFeatureUpdateRequest`, `MigrateToZoneRequest`, `MoveToRGRequest`, `NetAttachRequest`, `NetDetachRequest`, `PauseRequest`, `PFWAddRequest`, `PFWDelRequest`, `PinToNodeRequest`, `PowerCycleRequest`, `RebootRequest`, `RedeployRequest`, `ResetRequest`, `ResizeRequest`, `RestoreRequest`, `ResumeRequest`, `SnapshotCreateRequest`, `SnapshotDeleteRequest`, `SnapshotRollbackRequest`, `StartRequest` и `StopRequest` в cloudapi/compute |
| BGOS-1043 | JSON-тэг опционального поля `AsyncMode` с `asyncMode` на `async_mode` в структурах запроса `AbortSharedSnapshotMergeRequest`, `CDEjectRequest`, `CDInsertRequest`, `ChangeIPRequest`, `ChangeLinkStateRequest`, `ChangeMTURequest`, `ChangeQoSPoliciesRequest`, `ChangeSecGroupsRequest`, `CloneRequest`, `CreateTemplateRequest`, `CreateTemplateFromBlankRequest`, `DeleteRequest`, `DisableRequest`, `DiskAddRequest`, `DiskAttachRequest`, `DiskDelRequest`, `DiskDetachRequest`, `DiskQOSRequest`, `DiskResizeRequest`, `DiskSwitchToReplicationRequest`, `GuestAgentDisableRequest`, `GuestAgentEnableRequest`, `GuestAgentExecuteRequest`, `GuestAgentFeatureUpdateRequest`, `MassDeleteRequest`, `MassRebootRequest`, `MassStartRequest`, `MassStopRequest`, `MigrateAbortRequest`, `MigrateStorageAbortRequest`, `MigrateStorageCleanUpRequest`, `MigrateToZoneRequest`, `MoveToRGRequest`, `NetAttachRequest`, `NetDetachRequest`, `NetQOSRequest`, `PauseRequest`, `PFWAddRequest`, `PFWDelRequest`, `PinToNodeRequest`, `PowerCycleRequest`, `RaiseDownRequest`, `RebootRequest`, `RedeployRequest`, `ResetRequest`, `ResizeRequest`, `RestoreRequest`, `ResumeRequest`, `SnapshotCreateRequest`, `SnapshotDeleteRequest`, `SnapshotRollbackRequest`, `StartRequest` и `StopRequest` в cloudbroker/compute |
| BGOS-1043 | Опциональное поле `SyncMode` на `AsyncMode` в структуре запроса `MigrateRequest` в cloudbroker/compute |
| BGOS-1043 | Опциональное поле `Sync` на `Async` в структуре запроса `MigrateStorageRequest` в cloudbroker/compute |
#### disks #### disks
| Идентификатор задачи | Описание | | Идентификатор задачи | Описание |
| --- | --- | | --- | --- |
| BGOS-1045 | JSON-тэг опционального поля `AsyncMode` с `asyncMode` на `async_mode` в структуре запроса `FromPlatformDiskRequest` в cloudapi/disks и в cloudbroker/disks | | BGOS-1080 | Опциональное поле `AccountID` из структуры запроса `FromPlatformDiskRequest` в cloudapi/disks |
#### image #### extnet
| Идентификатор задачи | Описание | | Идентификатор задачи | Описание |
| --- | --- | | --- | --- |
| BGOS-1045 | JSON-тэг опционального поля `AsyncMode` с `asyncMode` на `async_mode` в структуре запроса `CreateRequest` в cloudapi/image и в cloudbroker/image | | BGOS-1073 | Вычисляемое поле `AutoAssignable` в структуру ответа `ItemReservation` в cloudapi/extnet |
| BGOS-1045 | JSON-тэг опционального поля `AsyncMode` с `asyncMode` на `async_mode` в структуре запроса `CreateCDROMImageRequest` в cloudbroker/image | | BGOS-1073 | Вычисляемые поля `AccountID` и `AutoAssignable` в структуру ответа `Excluded` в cloudapi/extnet |
| BGOS-1073 | Вычисляемое поле `AutoAssignable` в структуру ответа `ItemReservation` в cloudbroker/extnet |
#### grid
| Идентификатор задачи | Описание |
| --- | --- |
| BGOS-1074 | Вычисляемое поле `VMFoldersEnabled` в структуры ответа `RecordGrid` и `ItemGridList` в cloudbroker/grid |
#### kvmx86 #### kvmx86
| Идентификатор задачи | Описание | | Идентификатор задачи | Описание |
| --- | --- | | --- | --- |
| BGOS-1045 | JSON-тэг опционального поля `AsyncMode` с `asyncMode` на `async_mode` в структуре запроса `MassCreateRequest` в cloudbroker/kvmx86 | | BGOS-1067 | Опциональное поле `PCIdevices` в структуры запросов `CreateRequest` и `CreateBlankRequest` в cloudapi/kvmx86 |
| BGOS-1067 | Опциональное поле `PCIdevices` в структуры запросов `CreateRequest` и `CreateBlankRequest` в cloudbroker/kvmx86 |
| BGOS-1081 | Опциональное поле `VGPUs` в структуры запросов `CreateRequest` и `CreateBlankRequest` в cloudapi/kvmx86 |
| BGOS-1081 | Опциональное поле `VGPUs` в структуры запросов `CreateRequest` и `CreateBlankRequest` в cloudbroker/kvmx86 |
#### node ### Изменено
| Идентификатор задачи | Описание |
| --- | --- |
| BGOS-1045 | JSON-тэг опционального поля `AsyncMode` с `asyncMode` на `async_mode` в структуре запроса `EnableRequest` в cloudbroker/node |
### Исправлено
#### apiaccess #### apiaccess
| Идентификатор задачи | Описание | | Идентификатор задачи | Описание |
| --- | --- | | --- | --- |
| BGOS-1055 | Ошибка при вызове метода `Update` в cloudbroker/apiaccess | | BGOS-1082 | Тип поля `APIs` с опционального на обязательный в структуре запроса `UpdateRequest` в cloudbroker/apiaccess |
| BGOS-1055 | Исправлен тип ответа метода `DescUpdate` в cloudbroker/apiaccess |
#### compute
#### Удалено
#### apiaccess
| Идентификатор задачи | Описание | | Идентификатор задачи | Описание |
| --- | --- | | --- | --- |
| BGOS-1055 | Структура `LibCloudEndpoints` и опциональное поле `LibCloud` из структуры `APIsEndpoints` в cloudbroker/apiaccess | | BGOS-1068 | Тип ответа метода `AffinityRuleAdd` с `bool` на структуру `AffinityRuleAddResult` в cloudapi/compute |
| BGOS-1055 | Опциональные поля `CloudSpace`, `ComputeCI`, `DPDK`, `Machine`, `Openshift`, `OpenshiftCI`, `PortForwarding`, `Sizes` из структуры `CloudAPIEndpoints` в cloudbroker/apiaccess | | BGOS-1068 | Тип ответа метода `AffinityRuleAdd` с `bool` на структуру `AffinityRuleAddResult` в cloudbroker/compute |
| BGOS-1055 | Опциональные поля `CloudSpace`, `ComputeCI`, `Desnode`, `Diagnostics`, `DPDK`, `IaaS`, `Machine`, `Openshift`, `OpenshiftCI`, `Ovsnode`, `QOS` из структуры `CloudBrokerEndpoints` в cloudbroker/apiaccess | | BGOS-1068 | Тип ответа метода `AntiAffinityRuleAdd` с `bool` на структуру `AntiAffinityRuleAddResult` в cloudapi/compute |
| BGOS-1055 | Опциональные поля `AgentController`, `Alerts`, `ErrorConditionHandler`, `Job`, `Log`, `Logo` из структуры `SystemEndpoints` в cloudbroker/apiaccess | | BGOS-1068 | Тип ответа метода `AntiAffinityRuleAdd` с `bool` на структуру `AntiAffinityRuleAddResult` в cloudbroker/compute |

View File

@@ -500,3 +500,8 @@ func memoryBalloonModeValidator(fe validator.FieldLevel) bool {
return IsInSlice(fieldValue, memoryBalloonModeValues) return IsInSlice(fieldValue, memoryBalloonModeValues)
} }
func migrateV2ModeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, migrateV2ModeValues)
}

View File

@@ -336,5 +336,7 @@ func registerAllValidators(validate *validator.Validate) error {
return err return err
} }
err = validate.RegisterValidation("migrateV2Mode", migrateV2ModeValidator)
return nil return nil
} }

View File

@@ -84,6 +84,8 @@ var (
watchdogActionValues = []string{"reset", "shutdown", "poweroff", "pause", "none"} watchdogActionValues = []string{"reset", "shutdown", "poweroff", "pause", "none"}
memoryBalloonModeValues = []string{"auto", "legacy", "none"} memoryBalloonModeValues = []string{"auto", "legacy", "none"}
migrateV2ModeValues = []string{"compute", "disks", "compute_and_disks"}
) )
const ( const (

View File

@@ -88,6 +88,42 @@ type ItemAudit struct {
// TTL // TTL
TTL string `json:"_ttl"` TTL string `json:"_ttl"`
// Resource group ID
ResgroupID uint64 `json:"resgroup_id"`
// Account ID
AccountID uint64 `json:"account_id"`
// Compute ID
ComputeID uint64 `json:"compute_id"`
// VINS ID
VINSID uint64 `json:"vins_id"`
// Service ID
ServiceID uint64 `json:"service_id"`
// K8S ID
K8SID uint64 `json:"k8s_id"`
// FLIPGroup ID
FLIPGroupID uint64 `json:"flipgroup_id"`
// Load balancer ID
LBID uint64 `json:"lb_id"`
// SEP ID
SEPID uint64 `json:"sep_id"`
// Node IDs
NodeIDs []uint64 `json:"node_ids"`
// Persistent disk IDs associated with the operation
DiskIDs []uint64 `json:"disk_ids"`
// API task ID for asynchronous calls
APITask string `json:"apitask"`
} }
// List of audits // List of audits

View File

@@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/internal/validators"
) )
@@ -51,14 +52,14 @@ type AffinityRuleAddRequest struct {
type wrapperAffinityRuleAddRequest struct { type wrapperAffinityRuleAddRequest struct {
AffinityRuleAddRequest AffinityRuleAddRequest
AsyncMode bool `url:"async_mode"` AsyncMode bool `url:"async_mode" json:"async_mode"`
} }
// AffinityRuleAdd add affinity rule // AffinityRuleAdd add affinity rule
func (c Compute) AffinityRuleAdd(ctx context.Context, req AffinityRuleAddRequest) (bool, error) { func (c Compute) AffinityRuleAdd(ctx context.Context, req AffinityRuleAddRequest) (AffinityRuleAddResult, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err)) return AffinityRuleAddResult{}, validators.ValidationErrors(validators.GetErrors(err))
} }
url := "/cloudapi/compute/affinityRuleAdd" url := "/cloudapi/compute/affinityRuleAdd"
@@ -68,17 +69,24 @@ func (c Compute) AffinityRuleAdd(ctx context.Context, req AffinityRuleAddRequest
AsyncMode: false, AsyncMode: false,
} }
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, aReq) res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, aReq)
if err != nil { if err != nil {
return false, err return AffinityRuleAddResult{}, err
} }
result, err := strconv.ParseBool(string(res)) if !req.Migrate {
if err != nil { result, err := strconv.ParseBool(string(res))
return false, err return AffinityRuleAddResult{Ok: result}, err
} }
return result, nil var report MigrationReport
err = json.Unmarshal(res, &report)
if err != nil {
return AffinityRuleAddResult{}, err
}
return AffinityRuleAddResult{Ok: report.Applied, MigrationReport: &report}, nil
} }
// AsyncAffinityRuleAdd add affinity rule in async mode // AsyncAffinityRuleAdd add affinity rule in async mode
@@ -95,7 +103,7 @@ func (c Compute) AsyncAffinityRuleAdd(ctx context.Context, req AffinityRuleAddRe
AsyncMode: true, AsyncMode: true,
} }
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, aReq) res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, aReq)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/internal/validators"
) )
@@ -51,14 +52,14 @@ type AntiAffinityRuleAddRequest struct {
type wrapperAntiAffinityRuleAddRequest struct { type wrapperAntiAffinityRuleAddRequest struct {
AntiAffinityRuleAddRequest AntiAffinityRuleAddRequest
AsyncMode bool `url:"async_mode"` AsyncMode bool `url:"async_mode" json:"async_mode"`
} }
// AntiAffinityRuleAdd add anti affinity rule // AntiAffinityRuleAdd add anti affinity rule
func (c Compute) AntiAffinityRuleAdd(ctx context.Context, req AntiAffinityRuleAddRequest) (bool, error) { func (c Compute) AntiAffinityRuleAdd(ctx context.Context, req AntiAffinityRuleAddRequest) (AntiAffinityRuleAddResult, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err)) return AntiAffinityRuleAddResult{}, validators.ValidationErrors(validators.GetErrors(err))
} }
url := "/cloudapi/compute/antiAffinityRuleAdd" url := "/cloudapi/compute/antiAffinityRuleAdd"
@@ -68,17 +69,24 @@ func (c Compute) AntiAffinityRuleAdd(ctx context.Context, req AntiAffinityRuleAd
AsyncMode: false, AsyncMode: false,
} }
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, aReq) res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, aReq)
if err != nil { if err != nil {
return false, err return AntiAffinityRuleAddResult{}, err
} }
result, err := strconv.ParseBool(string(res)) if !req.Migrate {
if err != nil { result, err := strconv.ParseBool(string(res))
return false, err return AntiAffinityRuleAddResult{Ok: result}, err
} }
return result, nil var report MigrationReport
err = json.Unmarshal(res, &report)
if err != nil {
return AntiAffinityRuleAddResult{}, err
}
return AntiAffinityRuleAddResult{Ok: report.Applied, MigrationReport: &report}, nil
} }
// AsyncAntiAffinityRuleAdd add anti affinity rule in async mode // AsyncAntiAffinityRuleAdd add anti affinity rule in async mode
@@ -95,7 +103,7 @@ func (c Compute) AsyncAntiAffinityRuleAdd(ctx context.Context, req AntiAffinityR
AsyncMode: true, AsyncMode: true,
} }
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, aReq) res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, aReq)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -39,10 +39,6 @@ type CreateTemplateFromBlankRequest struct {
// Required: false // Required: false
Password string `url:"password,omitempty" json:"password,omitempty"` Password string `url:"password,omitempty" json:"password,omitempty"`
// Account ID to make the image exclusive
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// Pool for image create // Pool for image create
// Required: false // Required: false
PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"`

View File

@@ -575,6 +575,9 @@ type RecordCompute struct {
// Zone ID // Zone ID
// Required: false // Required: false
ZoneID uint64 `json:"zoneId"` ZoneID uint64 `json:"zoneId"`
// Folder ID
FolderID uint64 `json:"folder_id"`
} }
// Detailed information about ACL Access in a compute // Detailed information about ACL Access in a compute
@@ -1564,6 +1567,9 @@ type ItemCompute struct {
// Zone ID // Zone ID
ZoneID uint64 `json:"zoneId"` ZoneID uint64 `json:"zoneId"`
// Folder ID
FolderID uint64 `json:"folder_id"`
} }
// ListInfoDisks // ListInfoDisks
@@ -1743,3 +1749,47 @@ type CloneStatus struct {
// Progress percent // Progress percent
ProgressPercent int `json:"progress_percent"` ProgressPercent int `json:"progress_percent"`
} }
type MigrationReportItem struct {
// ID of the compute instance
ComputeID uint64 `json:"compute_id"`
// Per-VM action result, not workflow status
// Should be one of:
// - migrated: migration completed; the rule may still be unsatisfied
// - skipped: migration was not needed or applicable
// - not_started: submission was not attempted
// - not_completed: completion is unconfirmed; the VM may have moved
Status string `json:"status"`
// Action details, errors and final rule-placement warnings
Message string `json:"message"`
// Workflow ID for tracking, or nil if submission was not attempted.
// A non-nil ID does not confirm job acceptance
WorkflowJobID *string `json:"workflow_job_id"`
}
type MigrationReport struct {
// Rules were saved; does not confirm migration success or rule satisfaction
Applied bool `json:"applied"`
// Per-compute migration results
Migrations []MigrationReportItem `json:"migrations"`
}
type AffinityRuleAddResult struct {
// Result of the rule addition
Ok bool
// Migration report. Populated only when the request was made with Migrate=true, nil otherwise
MigrationReport *MigrationReport
}
type AntiAffinityRuleAddResult struct {
// Result of the rule addition
Ok bool
// Migration report. Populated only when the request was made with Migrate=true, nil otherwise
MigrationReport *MigrationReport
}

View File

@@ -35,10 +35,6 @@ type FromPlatformDiskRequest struct {
// Required: false // Required: false
Password string `url:"password,omitempty" json:"password,omitempty"` Password string `url:"password,omitempty" json:"password,omitempty"`
// Account ID to make the image exclusive
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// Pool for image create // Pool for image create
// Required: false // Required: false
PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"`

View File

@@ -93,6 +93,9 @@ type ItemReservation struct {
// Account ID // Account ID
AccountID uint64 `json:"account_id"` AccountID uint64 `json:"account_id"`
// Auto assignable
AutoAssignable bool `json:"auto_assignable"`
// ClientType // ClientType
ClientType string `json:"clientType"` ClientType string `json:"clientType"`
@@ -127,6 +130,12 @@ type VNFs struct {
} }
type Excluded struct { type Excluded struct {
// Account ID
AccountID uint64 `json:"account_id"`
// Auto assignable
AutoAssignable bool `json:"auto_assignable"`
// ClientType // ClientType
ClientType string `json:"clientType"` ClientType string `json:"clientType"`

View File

@@ -230,6 +230,10 @@ type CreateRequest struct {
// Required: false // Required: false
PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"`
// List of PCI device IDs to attach to this VM
// Required: false
PCIdevices []uint64 `url:"pcidevices,omitempty" json:"pcidevices,omitempty"`
// Zone ID // Zone ID
// Required: false // Required: false
ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"`
@@ -252,6 +256,10 @@ type CreateRequest struct {
// Required: false // Required: false
// Default: "auto" // Default: "auto"
MemoryBalloonMode string `url:"memory_balloon_mode,omitempty" json:"memory_balloon_mode,omitempty" validate:"omitempty,memoryBalloonMode"` MemoryBalloonMode string `url:"memory_balloon_mode,omitempty" json:"memory_balloon_mode,omitempty" validate:"omitempty,memoryBalloonMode"`
// List of vGPU IDs to attach to the VM
// Required: false
VGPUs []uint64 `url:"vgpus,omitempty" json:"vgpus,omitempty"`
} }
// GetRAM returns RAM field values // GetRAM returns RAM field values

View File

@@ -92,6 +92,10 @@ type CreateBlankRequest struct {
// Required: false // Required: false
PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"`
// List of PCI device IDs to attach to this VM
// Required: false
PCIdevices []uint64 `url:"pcidevices,omitempty" json:"pcidevices,omitempty"`
// VM type linux, windows or unknown // VM type linux, windows or unknown
// Required: false // Required: false
LoaderType string `url:"loaderType,omitempty" json:"loaderType,omitempty" validate:"omitempty,loaderType"` LoaderType string `url:"loaderType,omitempty" json:"loaderType,omitempty" validate:"omitempty,loaderType"`
@@ -134,6 +138,10 @@ type CreateBlankRequest struct {
// Required: false // Required: false
// Default: "auto" // Default: "auto"
MemoryBalloonMode string `url:"memory_balloon_mode,omitempty" json:"memory_balloon_mode,omitempty" validate:"omitempty,memoryBalloonMode"` MemoryBalloonMode string `url:"memory_balloon_mode,omitempty" json:"memory_balloon_mode,omitempty" validate:"omitempty,memoryBalloonMode"`
// List of vGPU IDs to attach to the VM
// Required: false
VGPUs []uint64 `url:"vgpus,omitempty" json:"vgpus,omitempty"`
} }
// GetRAM returns RAM field values // GetRAM returns RAM field values

View File

@@ -15,7 +15,7 @@ type UpdateRequest struct {
APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" validate:"required"` APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" validate:"required"`
// APIs to remove from APIAccess group // APIs to remove from APIAccess group
// Required: false // Required: true
APIs APIsEndpoints `url:"-" json:"-"` APIs APIsEndpoints `url:"-" json:"-"`
} }

View File

@@ -43,6 +43,42 @@ type ItemAudit struct {
// TTL // TTL
TTL string `json:"_ttl"` TTL string `json:"_ttl"`
// Resource group ID
ResgroupID uint64 `json:"resgroup_id"`
// Account ID
AccountID uint64 `json:"account_id"`
// Compute ID
ComputeID uint64 `json:"compute_id"`
// VINS ID
VINSID uint64 `json:"vins_id"`
// Service ID
ServiceID uint64 `json:"service_id"`
// K8S ID
K8SID uint64 `json:"k8s_id"`
// FLIPGroup ID
FLIPGroupID uint64 `json:"flipgroup_id"`
// Load balancer ID
LBID uint64 `json:"lb_id"`
// SEP ID
SEPID uint64 `json:"sep_id"`
// Node IDs
NodeIDs []uint64 `json:"node_ids"`
// Persistent disk IDs associated with the operation
DiskIDs []uint64 `json:"disk_ids"`
// API task ID for asynchronous calls
APITask string `json:"apitask"`
} }
// List of audits // List of audits

View File

@@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/internal/validators"
) )
@@ -53,14 +54,14 @@ type AffinityRuleAddRequest struct {
type wrapperAffinityRuleAddRequest struct { type wrapperAffinityRuleAddRequest struct {
AffinityRuleAddRequest AffinityRuleAddRequest
AsyncMode bool `url:"async_mode"` AsyncMode bool `url:"async_mode" json:"async_mode"`
} }
// AffinityRuleAdd adds affinity rule // AffinityRuleAdd adds affinity rule
func (c Compute) AffinityRuleAdd(ctx context.Context, req AffinityRuleAddRequest) (bool, error) { func (c Compute) AffinityRuleAdd(ctx context.Context, req AffinityRuleAddRequest) (AffinityRuleAddResult, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err)) return AffinityRuleAddResult{}, validators.ValidationErrors(validators.GetErrors(err))
} }
url := "/cloudbroker/compute/affinityRuleAdd" url := "/cloudbroker/compute/affinityRuleAdd"
@@ -70,17 +71,24 @@ func (c Compute) AffinityRuleAdd(ctx context.Context, req AffinityRuleAddRequest
AsyncMode: false, AsyncMode: false,
} }
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, aReq) res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, aReq)
if err != nil { if err != nil {
return false, err return AffinityRuleAddResult{}, err
} }
result, err := strconv.ParseBool(string(res)) if !req.Migrate {
if err != nil { result, err := strconv.ParseBool(string(res))
return false, err return AffinityRuleAddResult{Ok: result}, err
} }
return result, nil var report MigrationReport
err = json.Unmarshal(res, &report)
if err != nil {
return AffinityRuleAddResult{}, err
}
return AffinityRuleAddResult{Ok: report.Applied, MigrationReport: &report}, nil
} }
// AsyncAffinityRuleAdd add affinity rule in async mode // AsyncAffinityRuleAdd add affinity rule in async mode
@@ -97,7 +105,7 @@ func (c Compute) AsyncAffinityRuleAdd(ctx context.Context, req AffinityRuleAddRe
AsyncMode: true, AsyncMode: true,
} }
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, aReq) res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, aReq)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/internal/validators"
) )
@@ -51,14 +52,14 @@ type AntiAffinityRuleAddRequest struct {
type wrapperAntiAffinityRuleAddRequest struct { type wrapperAntiAffinityRuleAddRequest struct {
AntiAffinityRuleAddRequest AntiAffinityRuleAddRequest
AsyncMode bool `url:"async_mode"` AsyncMode bool `url:"async_mode" json:"async_mode"`
} }
// AntiAffinityRuleAdd adds anti affinity rule // AntiAffinityRuleAdd adds anti affinity rule
func (c Compute) AntiAffinityRuleAdd(ctx context.Context, req AntiAffinityRuleAddRequest) (bool, error) { func (c Compute) AntiAffinityRuleAdd(ctx context.Context, req AntiAffinityRuleAddRequest) (AntiAffinityRuleAddResult, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err)) return AntiAffinityRuleAddResult{}, validators.ValidationErrors(validators.GetErrors(err))
} }
url := "/cloudbroker/compute/antiAffinityRuleAdd" url := "/cloudbroker/compute/antiAffinityRuleAdd"
@@ -68,17 +69,24 @@ func (c Compute) AntiAffinityRuleAdd(ctx context.Context, req AntiAffinityRuleAd
AsyncMode: false, AsyncMode: false,
} }
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, aReq) res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, aReq)
if err != nil { if err != nil {
return false, err return AntiAffinityRuleAddResult{}, err
} }
result, err := strconv.ParseBool(string(res)) if !req.Migrate {
if err != nil { result, err := strconv.ParseBool(string(res))
return false, err return AntiAffinityRuleAddResult{Ok: result}, err
} }
return result, nil var report MigrationReport
err = json.Unmarshal(res, &report)
if err != nil {
return AntiAffinityRuleAddResult{}, err
}
return AntiAffinityRuleAddResult{Ok: report.Applied, MigrationReport: &report}, nil
} }
// AsyncAntiAffinityRuleAdd add anti affinity rule in async mode // AsyncAntiAffinityRuleAdd add anti affinity rule in async mode
@@ -95,7 +103,7 @@ func (c Compute) AsyncAntiAffinityRuleAdd(ctx context.Context, req AntiAffinityR
AsyncMode: true, AsyncMode: true,
} }
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, aReq) res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, aReq)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -0,0 +1,83 @@
package compute
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// Target describes a target for migration
type Target struct {
// SEP ID
// Required: true
SepID uint64 `url:"sep_id" json:"sep_id" validate:"required"`
// Name of a pool
// Required: true
PoolName string `url:"pool_name" json:"pool_name" validate:"required"`
// Storage policy ID
// Required: false
StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,omitempty"`
// Disk IDs
// Required: false
DiskIDs []uint64 `url:"disk_ids,omitempty" json:"disk_ids,omitempty"`
}
// Storage describes a destination for a disk
type Storage struct {
// Target for migration
// Required: false
Targets []Target `url:"targets,omitempty" json:"targets,omitempty"`
// Whether a separate source-disk cleanup job should be started after full success
// Required: false
AutoCleanup interface{} `url:"auto_cleanup,omitempty" json:"auto_cleanup,omitempty" validate:"omitempty,isBool"`
}
// MigrateV2Request to start a migration of a compute or a disk
type MigrateV2Request struct {
// ID of compute to migrate
// Required: true
ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"`
// Stateless load balancers support only compute mode, including their local system disk.
// Required: false
Mode string `url:"mode,omitempty" json:"mode,omitempty" validate:"omitempty,migrateV2Mode"`
// ID of node to migrate to
// Required: false
TargetNodeID uint64 `url:"target_node_id,omitempty" json:"target_node_id,omitempty"`
// Destination for a disk
// Required: false
Storage Storage `url:"storage,omitempty" json:"storage,omitempty"`
}
// MigrateV2 starts a migration of a compute or a disk
func (c Compute) MigrateV2(ctx context.Context, req MigrateV2Request) (string, error) {
err := validators.ValidateRequest(req)
if err != nil {
return "", validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudbroker/compute/migrate_v2"
res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req)
if err != nil {
return "", err
}
var resp string
err = json.Unmarshal(res, &resp)
if err != nil {
return "", err
}
return resp, nil
}

View File

@@ -0,0 +1,47 @@
package compute
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// MigrateV2AbortRequest to abort a migration of a compute
type MigrateV2AbortRequest struct {
// Original migration workflow IDs, not agent, cleanup or recovery job IDs
// Required: true
WorkflowJobIDs []string `url:"workflow_job_ids" json:"workflow_job_ids" validate:"required"`
}
// MigrateV2Abort aborts a migration of a compute
func (c Compute) MigrateV2Abort(ctx context.Context, req MigrateV2AbortRequest) (*MigrateV2AbortRecord, error) {
res, err := c.MigrateV2AbortRaw(ctx, req)
if err != nil {
return nil, err
}
info := MigrateV2AbortRecord{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}
// MigrateV2AbortRaw aborts a migration of a compute and returns an array of bytes
func (c Compute) MigrateV2AbortRaw(ctx context.Context, req MigrateV2AbortRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudbroker/compute/migrate_v2_abort"
res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req)
return res, err
}

View File

@@ -0,0 +1,41 @@
package compute
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// MigrateV2CleanupRequest to clean up after a migration of a compute
type MigrateV2CleanupRequest struct {
// Original migration workflow IDs, not agent, cleanup or recovery job IDs
// Required: true
WorkflowJobID string `url:"workflow_job_id" json:"workflow_job_id" validate:"required"`
}
// MigrateV2Cleanup cleans up after a migration of a compute
func (c Compute) MigrateV2Cleanup(ctx context.Context, req MigrateV2CleanupRequest) (string, error) {
err := validators.ValidateRequest(req)
if err != nil {
return "", validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudbroker/compute/migrate_v2_cleanup"
res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req)
if err != nil {
return "", err
}
var resp string
err = json.Unmarshal(res, &resp)
if err != nil {
return "", err
}
return resp, nil
}

View File

@@ -0,0 +1,46 @@
package compute
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// MigrateV2GetRequest to get the information about a workflow job
type MigrateV2GetRequest struct {
// Original migration workflow IDs
// Required: true
WorkflowJobID string `url:"workflow_job_id" json:"workflow_job_id" validate:"required"`
}
// MigrateV2Get gets information about a workflow job as a MigrateV2Record struct
func (c Compute) MigrateV2Get(ctx context.Context, req MigrateV2GetRequest) (*MigrateV2Record, error) {
res, err := c.MigrateV2GetRaw(ctx, req)
if err != nil {
return nil, err
}
info := MigrateV2Record{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}
// GetRaw gets information about a workflow job as an array of bytes
func (c Compute) MigrateV2GetRaw(ctx context.Context, req MigrateV2GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudbroker/compute/migrate_v2_get"
res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req)
return res, err
}

View File

@@ -0,0 +1,67 @@
package compute
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// MigrateV2ListRequest to list migrations
type MigrateV2ListRequest struct {
// Exact APIServer migration workflow job ID
// Required: false
WorkflowJobID string `url:"workflow_job_id,omitempty" json:"workflow_job_id,omitempty"`
// Compute ID
// Required: false
ComputeID uint64 `url:"compute_id,omitempty" json:"compute_id,omitempty"`
// Exact business migration status
// Required: false
Status string `url:"status,omitempty" json:"status,omitempty"`
// Source node ID
// Required: false
SourceNodeID uint64 `url:"source_node_id,omitempty" json:"source_node_id,omitempty"`
// Target node ID
// Required: false
TargetNodeID uint64 `url:"target_node_id,omitempty" json:"target_node_id,omitempty"`
// Whether retained source disks still require cleanup
// Required: false
CleanupRequired interface{} `url:"cleanup_required,omitempty" json:"cleanup_required,omitempty" validate:"omitempty,isBool"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
}
// MigrateV2List gets list of the jobs
func (c Compute) MigrateV2List(ctx context.Context, req MigrateV2ListRequest) (*ListMigrateV2, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudbroker/compute/migrate_v2_list"
res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req)
if err != nil {
return nil, err
}
list := ListMigrateV2{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}

View File

@@ -0,0 +1,51 @@
package compute
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// MigrateResolveRequest to resolve a migration
type MigrateV2ResolveRequest struct {
// Workflow job ID
// Required: true
WorkflowJobID string `url:"workflow_job_id" json:"workflow_job_id" validate:"required"`
// Take over cleanup
// Required: false
TakeOverCleanup interface{} `url:"take_over_cleanup,omitempty" json:"take_over_cleanup,omitempty" validate:"omitempty,isBool"`
}
// MigrateV2Resolve resolves a migration of a compute
func (c Compute) MigrateV2Resolve(ctx context.Context, req MigrateV2ResolveRequest) (*MigrateV2ResolveRecord, error) {
res, err := c.MigrateV2ResolveRaw(ctx, req)
if err != nil {
return nil, err
}
info := MigrateV2ResolveRecord{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}
// MigrateV2ResolveRaw resolves a migration of a compute and returns an array of bytes
func (c Compute) MigrateV2ResolveRaw(ctx context.Context, req MigrateV2ResolveRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudbroker/compute/migrate_v2_resolve"
res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req)
return res, err
}

View File

@@ -938,6 +938,9 @@ type InfoCompute struct {
// Zone ID // Zone ID
ZoneID uint64 `json:"zoneId"` ZoneID uint64 `json:"zoneId"`
// Folder ID
FolderID uint64 `json:"folder_id"`
} }
type QemuQuest struct { type QemuQuest struct {
@@ -1252,6 +1255,9 @@ type RecordCompute struct {
//todo //todo
_ uint64 `json:"nodeId"` _ uint64 `json:"nodeId"`
// Folder ID
FolderID uint64 `json:"folder_id"`
} }
// Detailed information about ACL Access in a compute // Detailed information about ACL Access in a compute
@@ -2272,3 +2278,376 @@ type CheckComputePlacementItem struct {
} }
type CheckComputePlacementResult map[uint64]CheckComputePlacementItem type CheckComputePlacementResult map[uint64]CheckComputePlacementItem
type Result struct {
// Workflow ID
WorkflowJobID string `json:"workflow_job_id"`
// Compute ID
ComputeID uint64 `json:"compute_id"`
// Status
Status string `json:"status"`
// Job ID
JobID string `json:"job_id"`
// Message
Message string `json:"message"`
}
// MigrateV2 abort record
type MigrateV2AbortRecord struct {
// Results
Results []Result `json:"results"`
}
type Origin struct {
// Type
Type string `json:"type"`
// Correlation ID
CorrelationID string `json:"correlation_id"`
}
type Observation struct {
// Updated at
UpdatedAt uint64 `json:"updated_at"`
// Agent job ID
AgentJobID string `json:"agent_job_id"`
// Attempt
Attempt uint64 `json:"attempt"`
// Operation
Operation string `json:"operation"`
}
type Memory struct {
// Sampled at
SampledAt uint64 `json:"sampled_at"`
// Percent
Percent uint64 `json:"percent"`
// Bytes per second
BytesPerSecond uint64 `json:"bytes_per_second"`
// Processed bytes
ProcessedBytes uint64 `json:"processed_bytes"`
// Remaining bytes
RemainingBytes uint64 `json:"remaining_bytes"`
}
type ProgressDisks struct {
// Scope
Scope string `json:"scope"`
// Target devs
TargetDevs []string `json:"target_devs"`
// Sampled at
SampledAt uint64 `json:"sampled_at"`
// Percent
Percent uint64 `json:"percent"`
// Bytes per second
BytesPerSecond uint64 `json:"bytes_per_second"`
// Processed bytes
ProcessedBytes uint64 `json:"processed_bytes"`
// Remaining bytes
RemainingBytes uint64 `json:"remaining_bytes"`
}
type SystemDisk struct {
// Target dev
TargetDev string `json:"target_dev"`
// Sampled at
SampledAt uint64 `json:"sampled_at"`
// Percent
Percent uint64 `json:"percent"`
// Bytes per second
BytesPerSecond uint64 `json:"bytes_per_second"`
// Processed bytes
ProcessedBytes uint64 `json:"processed_bytes"`
// Remaining bytes
RemainingBytes uint64 `json:"remaining_bytes"`
}
type Progress struct {
// Observation
Observation Observation `json:"observation"`
// Memory
Memory Memory `json:"memory"`
// Disks
Disks ProgressDisks `json:"disks"`
// System disk
SystemDisk SystemDisk `json:"system_disk"`
}
type Disk struct {
// Source disk ID
SourceDiskID uint64 `json:"source_disk_id"`
// Target disk ID
TargetDiskID uint64 `json:"target_disk_id"`
// Target dev
TargetDev string `json:"target_dev"`
// SEP ID
SEPID uint64 `json:"sep_id"`
// Pool name
PoolName string `json:"pool_name"`
// Storage policy ID
StoragePolicyID uint64 `json:"storage_policy_id"`
// State
State string `json:"state"`
}
type Cleanup struct {
// Job ID
JobID string `json:"job_id"`
// Status
Status string `json:"status"`
// Error
Error string `json:"error"`
}
type Resolution struct {
// Resolved at
ResolvedAt string `json:"resolved_at"`
// Resolved by
ResolvedBy string `json:"resolved_by"`
// RequestID
RequestId string `json:"request_id"`
// Take over cleanup
TakeOverCleanup bool `json:"take_over_cleanup"`
// Previous status
PreviousStatus string `json:"previous_status"`
// Previous cleanup required
PreviousCleanupRequired bool `json:"previous_cleanup_required"`
// Previous error
PreviousError string `json:"previous_error"`
}
type MigrateV2Item struct {
// Workflow Job ID
WorkflowJobID string `json:"workflow_job_id"`
// Mode
Mode string `json:"mode"`
// Origin
Origin Origin `json:"origin"`
// Compute ID
ComputeID uint64 `json:"compute_id"`
// Progress
Progress Progress `json:"progress"`
// Domain name
DomainName string `json:"domain_name"`
// Domain UUID
DomainUUID string `json:"domain_uuid"`
// Source node ID
SourceNodeID uint64 `json:"source_node_id"`
// Target node ID
TargetNodeID uint64 `json:"target_node_id"`
// Source tech status
SourceTechStatus string `json:"source_tech_status"`
// Status
Status string `json:"status"`
// Migration result confirmed
MigrationResultConfirmed bool `json:"migration_result_confirmed"`
// Disks
Disks []Disk `json:"disks"`
// Disk IDs not copied
DiskIDsNotCopied []uint64 `json:"disk_ids_not_copied"`
// Error
Error string `json:"error"`
// Cleanup required
CleanupRequired bool `json:"cleanup_required"`
// Cleanup
Cleanup Cleanup `json:"cleanup"`
// Resolution
Resolution Resolution `json:"resolution"`
// Created at
CreatedAt string `json:"created_at"`
// Updated at
UpdatedAt string `json:"updated_at"`
}
type ListMigrateV2 struct {
// Entry count
EntryCount uint64 `json:"entry_count"`
// Data
Data []MigrateV2Item `json:"data"`
}
type MigrateV2Record struct {
// Workflow Job ID
WorkflowJobID string `json:"workflow_job_id"`
// Mode
Mode string `json:"mode"`
// Origin
Origin Origin `json:"origin"`
// Compute ID
ComputeID uint64 `json:"compute_id"`
// Progress
Progress Progress `json:"progress"`
// Domain name
DomainName string `json:"domain_name"`
// Domain UUID
DomainUUID string `json:"domain_uuid"`
// Source node ID
SourceNodeID uint64 `json:"source_node_id"`
// Target node ID
TargetNodeID uint64 `json:"target_node_id"`
// Source tech status
SourceTechStatus string `json:"source_tech_status"`
// Status
Status string `json:"status"`
// Migration result confirmed
MigrationResultConfirmed bool `json:"migration_result_confirmed"`
// Disks
Disks []Disk `json:"disks"`
// Disk IDs not copied
DiskIDsNotCopied []uint64 `json:"disk_ids_not_copied"`
// Error
Error string `json:"error"`
// Cleanup required
CleanupRequired bool `json:"cleanup_required"`
// Cleanup
Cleanup Cleanup `json:"cleanup"`
// Resolution
Resolution Resolution `json:"resolution"`
// Created at
CreatedAt string `json:"created_at"`
// Updated at
UpdatedAt string `json:"updated_at"`
// Logs
Logs []string `json:"logs"`
}
// Migrate V2 resolve record
type MigrateV2ResolveRecord struct {
// Workflow job ID
WorkflowJobID string `json:"workflow_job_id"`
// Compute ID
ComputeID uint64 `json:"compute_id"`
// Status
Status string `json:"status"`
// Cleanup required
CleanupRequired bool `json:"cleanup_required"`
// Resolution
Resolution Resolution `json:"resolution"`
}
type MigrationReportItem struct {
// ID of the compute instance
ComputeID uint64 `json:"compute_id"`
// Per-VM action result, not workflow status
// Should be one of:
// - migrated: migration completed; the rule may still be unsatisfied
// - skipped: migration was not needed or applicable
// - not_started: submission was not attempted
// - not_completed: completion is unconfirmed; the VM may have moved
Status string `json:"status"`
// Action details, errors and final rule-placement warnings
Message string `json:"message"`
// Workflow ID for tracking, or nil if submission was not attempted.
// A non-nil ID does not confirm job acceptance
WorkflowJobID *string `json:"workflow_job_id"`
}
type MigrationReport struct {
// Rules were saved; does not confirm migration success or rule satisfaction
Applied bool `json:"applied"`
// Per-compute migration results
Migrations []MigrationReportItem `json:"migrations"`
}
type AffinityRuleAddResult struct {
// Result of the rule addition
Ok bool
// Migration report. Populated only when the request was made with Migrate=true, nil otherwise
MigrationReport *MigrationReport
}
type AntiAffinityRuleAddResult struct {
// Result of the rule addition
Ok bool
// Migration report. Populated only when the request was made with Migrate=true, nil otherwise
MigrationReport *MigrationReport
}

View File

@@ -20,12 +20,21 @@ type ItemReservation struct {
// Account ID // Account ID
AccountID uint64 `json:"account_id"` AccountID uint64 `json:"account_id"`
// Auto assignable
AutoAssignable bool `json:"auto_assignable"`
// Client type // Client type
ClientType string `json:"clientType"` ClientType string `json:"clientType"`
// Description // Description
Description string `json:"desc"` Description string `json:"desc"`
// Domain name
DomainName string `json:"domainname"`
// Hostname
Hostname string `json:"hostname"`
// IP // IP
IP string `json:"ip"` IP string `json:"ip"`
@@ -37,12 +46,6 @@ type ItemReservation struct {
// Virtual machine ID // Virtual machine ID
VMID uint64 `json:"vmId"` VMID uint64 `json:"vmId"`
// Domain name
DomainName string `json:"domainname"`
// Hostname
Hostname string `json:"hostname"`
} }
// List reservations // List reservations

View File

@@ -117,6 +117,9 @@ type RecordGrid struct {
// Budget // Budget
Budget bool `json:"budget"` Budget bool `json:"budget"`
// Whether VM folders are enabled
VMFoldersEnabled bool `json:"vm_folders_enabled"`
} }
// Information about grid // Information about grid
@@ -165,6 +168,9 @@ type ItemGridList struct {
// Budget // Budget
Budget bool `json:"budget"` Budget bool `json:"budget"`
// Whether VM folders are enabled
VMFoldersEnabled bool `json:"vm_folders_enabled"`
} }
// List Grids // List Grids

View File

@@ -240,6 +240,10 @@ type CreateRequest struct {
// Required: false // Required: false
PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"`
// List of PCI device IDs to attach to this VM
// Required: false
PCIdevices []uint64 `url:"pcidevices,omitempty" json:"pcidevices,omitempty"`
// Zone ID // Zone ID
// Required: false // Required: false
ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"`
@@ -275,6 +279,10 @@ type CreateRequest struct {
// Required: false // Required: false
// Default: "auto" // Default: "auto"
MemoryBalloonMode string `url:"memory_balloon_mode,omitempty" json:"memory_balloon_mode,omitempty" validate:"omitempty,memoryBalloonMode"` MemoryBalloonMode string `url:"memory_balloon_mode,omitempty" json:"memory_balloon_mode,omitempty" validate:"omitempty,memoryBalloonMode"`
// List of vGPU IDs to attach to the VM
// Required: false
VGPUs []uint64 `url:"vgpus,omitempty" json:"vgpus,omitempty"`
} }
// GetRAM returns RAM field values // GetRAM returns RAM field values

View File

@@ -92,6 +92,10 @@ type CreateBlankRequest struct {
// Required: false // Required: false
PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"`
// List of PCI device IDs to attach to this VM
// Required: false
PCIdevices []uint64 `url:"pcidevices,omitempty" json:"pcidevices,omitempty"`
// VM type linux, windows or unknown // VM type linux, windows or unknown
// Required: false // Required: false
LoaderType string `url:"loaderType,omitempty" json:"loaderType,omitempty" validate:"omitempty,loaderType"` LoaderType string `url:"loaderType,omitempty" json:"loaderType,omitempty" validate:"omitempty,loaderType"`
@@ -147,6 +151,10 @@ type CreateBlankRequest struct {
// Required: false // Required: false
// Default: "auto" // Default: "auto"
MemoryBalloonMode string `url:"memory_balloon_mode,omitempty" json:"memory_balloon_mode,omitempty" validate:"omitempty,memoryBalloonMode"` MemoryBalloonMode string `url:"memory_balloon_mode,omitempty" json:"memory_balloon_mode,omitempty" validate:"omitempty,memoryBalloonMode"`
// List of vGPU IDs to attach to the VM
// Required: false
VGPUs []uint64 `url:"vgpus,omitempty" json:"vgpus,omitempty"`
} }
// GetRAM returns RAM field values // GetRAM returns RAM field values

View File

@@ -68,7 +68,7 @@ func TestMigrator(t *testing.T) {
ComputeID: dxVMID, ComputeID: dxVMID,
TargetNodeID: nodeID, TargetNodeID: nodeID,
}, },
SyncMode: true})).Return(b, nil).AnyTimes() AsyncMode: false})).Return(b, nil).AnyTimes()
ok, err := migrator.Migrate(context.Background(), dxVMID, nodeID) ok, err := migrator.Migrate(context.Background(), dxVMID, nodeID)
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -87,8 +87,10 @@ func TestGetListCloudAPI(t *testing.T) {
getResult("Account list", bytes, account.ListAccounts{}, t) getResult("Account list", bytes, account.ListAccounts{}, t)
// Get // Get
listAcc, _ := client.CloudAPI().Account().List(context.Background(), account.ListRequest{}) listAcc, err := client.CloudAPI().Account().List(context.Background(), account.ListRequest{})
if len(listAcc.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.Account.List failed: %v", err)
} else if len(listAcc.Data) > 0 {
id := listAcc.Data[0].ID id := listAcc.Data[0].ID
bytes, err = client.CloudAPI().Account().GetRaw(context.Background(), account.GetRequest{AccountID: id}) bytes, err = client.CloudAPI().Account().GetRaw(context.Background(), account.GetRequest{AccountID: id})
if err != nil { if err != nil {
@@ -107,8 +109,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Audit list", bytes, audit.ListAudits{}, t) getResult("Audit list", bytes, audit.ListAudits{}, t)
// Get // Get
listAudits, _ := client.CloudAPI().Audit().List(context.Background(), audit.ListRequest{}) listAudits, err := client.CloudAPI().Audit().List(context.Background(), audit.ListRequest{})
if len(listAudits.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.Audit.List failed: %v", err)
} else if len(listAudits.Data) > 0 {
id := listAudits.Data[0].GUID id := listAudits.Data[0].GUID
bytes, err = client.CloudAPI().Audit().GetRaw(context.Background(), audit.GetRequest{AuditGuid: id}) bytes, err = client.CloudAPI().Audit().GetRaw(context.Background(), audit.GetRequest{AuditGuid: id})
if err != nil { if err != nil {
@@ -127,8 +131,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Bservice list", bytes, bservice.ListBasicServices{}, t) getResult("Bservice list", bytes, bservice.ListBasicServices{}, t)
// Get // Get
listBServ, _ := client.CloudAPI().BService().List(context.Background(), bservice.ListRequest{}) listBServ, err := client.CloudAPI().BService().List(context.Background(), bservice.ListRequest{})
if len(listBServ.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.BService.List failed: %v", err)
} else if len(listBServ.Data) > 0 {
id := listBServ.Data[0].ID id := listBServ.Data[0].ID
bytes, err = client.CloudAPI().BService().GetRaw(context.Background(), bservice.GetRequest{ServiceID: id}) bytes, err = client.CloudAPI().BService().GetRaw(context.Background(), bservice.GetRequest{ServiceID: id})
if err != nil { if err != nil {
@@ -147,8 +153,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Compute list", bytes, compute.ListComputes{}, t) getResult("Compute list", bytes, compute.ListComputes{}, t)
// Get // Get
listComp, _ := client.CloudAPI().Compute().List(context.Background(), compute.ListRequest{}) listComp, err := client.CloudAPI().Compute().List(context.Background(), compute.ListRequest{})
if listComp != nil && len(listComp.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.Compute.List failed: %v", err)
} else if listComp != nil && len(listComp.Data) > 0 {
id := listComp.Data[0].ID id := listComp.Data[0].ID
bytes, err = client.CloudAPI().Compute().GetRaw(context.Background(), compute.GetRequest{ComputeID: id}) bytes, err = client.CloudAPI().Compute().GetRaw(context.Background(), compute.GetRequest{ComputeID: id})
if err != nil { if err != nil {
@@ -173,8 +181,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Disk list", bytes, disks.ListDisks{}, t) getResult("Disk list", bytes, disks.ListDisks{}, t)
// Get // Get
listDisk, _ := client.CloudAPI().Disks().List(context.Background(), disks.ListRequest{}) listDisk, err := client.CloudAPI().Disks().List(context.Background(), disks.ListRequest{})
if len(listDisk.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.Disks.List failed: %v", err)
} else if len(listDisk.Data) > 0 {
id := listDisk.Data[0].ID id := listDisk.Data[0].ID
bytes, err = client.CloudAPI().Disks().GetRaw(context.Background(), disks.GetRequest{DiskID: id}) bytes, err = client.CloudAPI().Disks().GetRaw(context.Background(), disks.GetRequest{DiskID: id})
if err != nil { if err != nil {
@@ -193,8 +203,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("ExtNet list", bytes, extnet.ListExtNets{}, t) getResult("ExtNet list", bytes, extnet.ListExtNets{}, t)
// Get // Get
listExtNet, _ := client.CloudAPI().ExtNet().List(context.Background(), extnet.ListRequest{}) listExtNet, err := client.CloudAPI().ExtNet().List(context.Background(), extnet.ListRequest{})
if len(listExtNet.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.ExtNet.List failed: %v", err)
} else if len(listExtNet.Data) > 0 {
id := listExtNet.Data[0].ID id := listExtNet.Data[0].ID
bytes, err = client.CloudAPI().ExtNet().GetRaw(context.Background(), extnet.GetRequest{NetID: id}) bytes, err = client.CloudAPI().ExtNet().GetRaw(context.Background(), extnet.GetRequest{NetID: id})
if err != nil { if err != nil {
@@ -213,8 +225,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("FLIPGroup list", bytes, flipgroup.ListFLIPGroups{}, t) getResult("FLIPGroup list", bytes, flipgroup.ListFLIPGroups{}, t)
// Get // Get
listFG, _ := client.CloudAPI().FLIPGroup().List(context.Background(), flipgroup.ListRequest{}) listFG, err := client.CloudAPI().FLIPGroup().List(context.Background(), flipgroup.ListRequest{})
if len(listFG.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.FLIPGroup.List failed: %v", err)
} else if len(listFG.Data) > 0 {
id := listFG.Data[0].ID id := listFG.Data[0].ID
bytes, err = client.CloudAPI().FLIPGroup().GetRaw(context.Background(), flipgroup.GetRequest{FLIPGroupID: id}) bytes, err = client.CloudAPI().FLIPGroup().GetRaw(context.Background(), flipgroup.GetRequest{FLIPGroupID: id})
if err != nil { if err != nil {
@@ -233,8 +247,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Image list", bytes, image.ListImages{}, t) getResult("Image list", bytes, image.ListImages{}, t)
// Get // Get
listImg, _ := client.CloudAPI().Image().List(context.Background(), image.ListRequest{}) listImg, err := client.CloudAPI().Image().List(context.Background(), image.ListRequest{})
if len(listImg.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.Image.List failed: %v", err)
} else if len(listImg.Data) > 0 {
id := listImg.Data[0].ID id := listImg.Data[0].ID
bytes, err = client.CloudAPI().Image().GetRaw(context.Background(), image.GetRequest{ImageID: id}) bytes, err = client.CloudAPI().Image().GetRaw(context.Background(), image.GetRequest{ImageID: id})
if err != nil { if err != nil {
@@ -253,8 +269,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("K8CI list", bytes, k8ci.ListK8CI{}, t) getResult("K8CI list", bytes, k8ci.ListK8CI{}, t)
// Get // Get
listk8ci, _ := client.CloudAPI().K8CI().List(context.Background(), k8ci.ListRequest{}) listk8ci, err := client.CloudAPI().K8CI().List(context.Background(), k8ci.ListRequest{})
if len(listk8ci.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.K8CI.List failed: %v", err)
} else if len(listk8ci.Data) > 0 {
id := listk8ci.Data[0].ID id := listk8ci.Data[0].ID
bytes, err = client.CloudAPI().K8CI().GetRaw(context.Background(), k8ci.GetRequest{K8CIID: id}) bytes, err = client.CloudAPI().K8CI().GetRaw(context.Background(), k8ci.GetRequest{K8CIID: id})
if err != nil { if err != nil {
@@ -273,8 +291,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("K8S list", bytes, k8s.ListK8SClusters{}, t) getResult("K8S list", bytes, k8s.ListK8SClusters{}, t)
// Get // Get
listk8s, _ := client.CloudAPI().K8S().List(context.Background(), k8s.ListRequest{}) listk8s, err := client.CloudAPI().K8S().List(context.Background(), k8s.ListRequest{})
if len(listk8s.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.K8S.List failed: %v", err)
} else if len(listk8s.Data) > 0 {
id := listk8s.Data[0].ID id := listk8s.Data[0].ID
bytes, err = client.CloudAPI().K8S().GetRaw(context.Background(), k8s.GetRequest{K8SID: id}) bytes, err = client.CloudAPI().K8S().GetRaw(context.Background(), k8s.GetRequest{K8SID: id})
if err != nil { if err != nil {
@@ -293,8 +313,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("LB list", bytes, lb.ListLB{}, t) getResult("LB list", bytes, lb.ListLB{}, t)
// Get // Get
listLB, _ := client.CloudAPI().LB().List(context.Background(), lb.ListRequest{}) listLB, err := client.CloudAPI().LB().List(context.Background(), lb.ListRequest{})
if len(listLB.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.LB.List failed: %v", err)
} else if len(listLB.Data) > 0 {
id := listLB.Data[0].ID id := listLB.Data[0].ID
bytes, err = client.CloudAPI().LB().GetRaw(context.Background(), lb.GetRequest{LBID: id}) bytes, err = client.CloudAPI().LB().GetRaw(context.Background(), lb.GetRequest{LBID: id})
if err != nil { if err != nil {
@@ -321,8 +343,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("QoS policy list", bytes, qospolicy.ListQoSPolicies{}, t) getResult("QoS policy list", bytes, qospolicy.ListQoSPolicies{}, t)
// Get // Get
listQosPolicies, _ := client.CloudAPI().QoSPolicy().List(context.Background(), qospolicy.ListRequest{}) listQosPolicies, err := client.CloudAPI().QoSPolicy().List(context.Background(), qospolicy.ListRequest{})
if len(listQosPolicies.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.QoSPolicy.List failed: %v", err)
} else if len(listQosPolicies.Data) > 0 {
id := listQosPolicies.Data[0].ID id := listQosPolicies.Data[0].ID
bytes, err = client.CloudAPI().QoSPolicy().GetRaw(context.Background(), qospolicy.GetRequest{QoSPolicyID: id}) bytes, err = client.CloudAPI().QoSPolicy().GetRaw(context.Background(), qospolicy.GetRequest{QoSPolicyID: id})
if err != nil { if err != nil {
@@ -341,8 +365,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("RG list", bytes, rg.ListResourceGroups{}, t) getResult("RG list", bytes, rg.ListResourceGroups{}, t)
// Get // Get
listRG, _ := client.CloudAPI().RG().List(context.Background(), rg.ListRequest{}) listRG, err := client.CloudAPI().RG().List(context.Background(), rg.ListRequest{})
if len(listRG.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.RG.List failed: %v", err)
} else if len(listRG.Data) > 0 {
id := listRG.Data[0].ID id := listRG.Data[0].ID
bytes, err = client.CloudAPI().RG().GetRaw(context.Background(), rg.GetRequest{RGID: id}) bytes, err = client.CloudAPI().RG().GetRaw(context.Background(), rg.GetRequest{RGID: id})
if err != nil { if err != nil {
@@ -369,8 +395,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Storage policy list", bytes, stpolicy.ListStoragePolicies{}, t) getResult("Storage policy list", bytes, stpolicy.ListStoragePolicies{}, t)
// Get // Get
listStoragePolicies, _ := client.CloudAPI().StPolicy().List(context.Background(), stpolicy.ListRequest{}) listStoragePolicies, err := client.CloudAPI().StPolicy().List(context.Background(), stpolicy.ListRequest{})
if len(listStoragePolicies.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.StPolicy.List failed: %v", err)
} else if len(listStoragePolicies.Data) > 0 {
id := listStoragePolicies.Data[0].ID id := listStoragePolicies.Data[0].ID
bytes, err = client.CloudAPI().StPolicy().GetRaw(context.Background(), stpolicy.GetRequest{StoragePolicyID: id}) bytes, err = client.CloudAPI().StPolicy().GetRaw(context.Background(), stpolicy.GetRequest{StoragePolicyID: id})
if err != nil { if err != nil {
@@ -389,8 +417,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Replication profile list", bytes, stpolicy.ListReplicationProfiles{}, t) getResult("Replication profile list", bytes, stpolicy.ListReplicationProfiles{}, t)
// Get // Get
listReplicationProfiles, _ := client.CloudAPI().StPolicy().ListReplicationProfile(context.Background(), stpolicy.ListReplicationProfileRequest{}) listReplicationProfiles, err := client.CloudAPI().StPolicy().ListReplicationProfile(context.Background(), stpolicy.ListReplicationProfileRequest{})
if len(listReplicationProfiles.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.StPolicy.ListReplicationProfile failed: %v", err)
} else if len(listReplicationProfiles.Data) > 0 {
id := listReplicationProfiles.Data[0].ID id := listReplicationProfiles.Data[0].ID
bytes, err = client.CloudAPI().StPolicy().GetReplicationProfileRaw(context.Background(), stpolicy.GetReplicationProfileRequest{ProfileID: id}) bytes, err = client.CloudAPI().StPolicy().GetReplicationProfileRaw(context.Background(), stpolicy.GetReplicationProfileRequest{ProfileID: id})
if err != nil { if err != nil {
@@ -409,8 +439,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Security group list", bytes, secgroup.ListSecurityGroups{}, t) getResult("Security group list", bytes, secgroup.ListSecurityGroups{}, t)
// Get // Get
listSecurityGroups, _ := client.CloudAPI().SecurityGroup().List(context.Background(), secgroup.ListRequest{}) listSecurityGroups, err := client.CloudAPI().SecurityGroup().List(context.Background(), secgroup.ListRequest{})
if len(listSecurityGroups.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.SecurityGroup.List failed: %v", err)
} else if len(listSecurityGroups.Data) > 0 {
id := listSecurityGroups.Data[0].ID id := listSecurityGroups.Data[0].ID
bytes, err = client.CloudAPI().SecurityGroup().GetRaw(context.Background(), secgroup.GetRequest{SecurityGroupID: id}) bytes, err = client.CloudAPI().SecurityGroup().GetRaw(context.Background(), secgroup.GetRequest{SecurityGroupID: id})
if err != nil { if err != nil {
@@ -429,8 +461,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Tasks list", bytes, tasks.ListTasks{}, t) getResult("Tasks list", bytes, tasks.ListTasks{}, t)
// Get // Get
listTasks, _ := client.CloudAPI().Tasks().List(context.Background(), tasks.ListRequest{}) listTasks, err := client.CloudAPI().Tasks().List(context.Background(), tasks.ListRequest{})
if len(listTasks.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.Tasks.List failed: %v", err)
} else if len(listTasks.Data) > 0 {
id := listTasks.Data[0].AuditID id := listTasks.Data[0].AuditID
bytes, err = client.CloudAPI().Tasks().GetRaw(context.Background(), tasks.GetRequest{AuditID: id}) bytes, err = client.CloudAPI().Tasks().GetRaw(context.Background(), tasks.GetRequest{AuditID: id})
if err != nil { if err != nil {
@@ -449,8 +483,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Trunk list", bytes, trunk.ListTrunks{}, t) getResult("Trunk list", bytes, trunk.ListTrunks{}, t)
// Get // Get
listTrunk, _ := client.CloudAPI().Trunk().List(context.Background(), trunk.ListRequest{}) listTrunk, err := client.CloudAPI().Trunk().List(context.Background(), trunk.ListRequest{})
if len(listTrunk.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.Trunk.List failed: %v", err)
} else if len(listTrunk.Data) > 0 {
id := listTrunk.Data[0].ID id := listTrunk.Data[0].ID
bytes, err = client.CloudAPI().Trunk().GetRaw(context.Background(), trunk.GetRequest{TrunkID: id}) bytes, err = client.CloudAPI().Trunk().GetRaw(context.Background(), trunk.GetRequest{TrunkID: id})
if err != nil { if err != nil {
@@ -469,8 +505,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("VINS list", bytes, vins.ListVINS{}, t) getResult("VINS list", bytes, vins.ListVINS{}, t)
// Get // Get
listVINS, _ := client.CloudAPI().VINS().List(context.Background(), vins.ListRequest{}) listVINS, err := client.CloudAPI().VINS().List(context.Background(), vins.ListRequest{})
if len(listVINS.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.VINS.List failed: %v", err)
} else if len(listVINS.Data) > 0 {
id := listVINS.Data[0].ID id := listVINS.Data[0].ID
bytes, err = client.CloudAPI().VINS().GetRaw(context.Background(), vins.GetRequest{VINSID: id}) bytes, err = client.CloudAPI().VINS().GetRaw(context.Background(), vins.GetRequest{VINSID: id})
if err != nil { if err != nil {
@@ -481,24 +519,31 @@ func TestGetListCloudAPI(t *testing.T) {
t.Errorf("Can not test VINS get because VINS list is empty") t.Errorf("Can not test VINS get because VINS list is empty")
} }
// VM folder // VM folder (list requires rg_id)
// List if listRG == nil || len(listRG.Data) == 0 {
bytes, err = client.CloudAPI().VMFolder().ListRaw(context.Background(), vmfolder.ListRequest{}) t.Errorf("Can not test VM folder because VM folder list is empty")
if err != nil { } else {
t.Error(err) rgID := listRG.Data[0].ID
} // List
getResult("VM folder list", bytes, vmfolder.ListVMFolder{}, t) bytes, err = client.CloudAPI().VMFolder().ListRaw(context.Background(), vmfolder.ListRequest{RGID: rgID})
// Get
listVMFolders, _ := client.CloudAPI().VMFolder().List(context.Background(), vmfolder.ListRequest{})
if len(listVMFolders.Data) > 0 {
id := listVMFolders.Data[0].ID
bytes, err = client.CloudAPI().VMFolder().GetRaw(context.Background(), vmfolder.GetRequest{FolderID: id})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
getResult("VM folder get", bytes, vmfolder.RecordVMFolder{}, t) getResult("VM folder list", bytes, vmfolder.ListVMFolder{}, t)
} else { // Get
t.Errorf("Can not test VM folder get because VM folder list is empty") listVMFolders, err := client.CloudAPI().VMFolder().List(context.Background(), vmfolder.ListRequest{RGID: rgID})
if err != nil {
t.Errorf("Can not test get because CloudAPI.VMFolder.List failed: %v", err)
} else if len(listVMFolders.Data) > 0 {
id := listVMFolders.Data[0].ID
bytes, err = client.CloudAPI().VMFolder().GetRaw(context.Background(), vmfolder.GetRequest{FolderID: id})
if err != nil {
t.Error(err)
}
getResult("VM folder get", bytes, vmfolder.RecordVMFolder{}, t)
} else {
t.Errorf("Can not test VM folder get because VM folder list is empty")
}
} }
// VGPU // VGPU
@@ -517,8 +562,10 @@ func TestGetListCloudAPI(t *testing.T) {
} }
getResult("Zone list", bytes, zone.ListZones{}, t) getResult("Zone list", bytes, zone.ListZones{}, t)
// Get // Get
ListZones, _ := client.CloudAPI().Zone().List(context.Background(), zone.ListRequest{}) ListZones, err := client.CloudAPI().Zone().List(context.Background(), zone.ListRequest{})
if len(ListZones.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudAPI.Zone.List failed: %v", err)
} else if len(ListZones.Data) > 0 {
id := ListZones.Data[0].ID id := ListZones.Data[0].ID
bytes, err = client.CloudAPI().Zone().GetRaw(context.Background(), zone.GetRequest{ID: id}) bytes, err = client.CloudAPI().Zone().GetRaw(context.Background(), zone.GetRequest{ID: id})
if err != nil { if err != nil {
@@ -549,8 +596,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Account list", bytes, account_cb.ListAccounts{}, t) getResult("Account list", bytes, account_cb.ListAccounts{}, t)
// Get // Get
listAcc, _ := client.CloudBroker().Account().List(context.Background(), account_cb.ListRequest{}) listAcc, err := client.CloudBroker().Account().List(context.Background(), account_cb.ListRequest{})
if len(listAcc.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Account.List failed: %v", err)
} else if len(listAcc.Data) > 0 {
id := listAcc.Data[0].ID id := listAcc.Data[0].ID
bytes, err = client.CloudBroker().Account().GetRaw(context.Background(), account_cb.GetRequest{AccountID: id}) bytes, err = client.CloudBroker().Account().GetRaw(context.Background(), account_cb.GetRequest{AccountID: id})
if err != nil { if err != nil {
@@ -569,8 +618,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Audit list", bytes, audit_cb.ListAudits{}, t) getResult("Audit list", bytes, audit_cb.ListAudits{}, t)
// Get // Get
listAudits, _ := client.CloudBroker().Audit().List(context.Background(), audit_cb.ListRequest{}) listAudits, err := client.CloudBroker().Audit().List(context.Background(), audit_cb.ListRequest{})
if len(listAudits.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Audit.List failed: %v", err)
} else if len(listAudits.Data) > 0 {
id := listAudits.Data[0].GUID id := listAudits.Data[0].GUID
bytes, err = client.CloudBroker().Audit().GetRaw(context.Background(), audit_cb.GetRequest{AuditGuid: id}) bytes, err = client.CloudBroker().Audit().GetRaw(context.Background(), audit_cb.GetRequest{AuditGuid: id})
if err != nil { if err != nil {
@@ -589,8 +640,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Bservice list", bytes, bservice_cb.ListBasicServices{}, t) getResult("Bservice list", bytes, bservice_cb.ListBasicServices{}, t)
// Get // Get
listBServ, _ := client.CloudBroker().BService().List(context.Background(), bservice_cb.ListRequest{}) listBServ, err := client.CloudBroker().BService().List(context.Background(), bservice_cb.ListRequest{})
if len(listBServ.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.BService.List failed: %v", err)
} else if len(listBServ.Data) > 0 {
id := listBServ.Data[0].ID id := listBServ.Data[0].ID
bytes, err = client.CloudBroker().BService().GetRaw(context.Background(), bservice_cb.GetRequest{ServiceID: id}) bytes, err = client.CloudBroker().BService().GetRaw(context.Background(), bservice_cb.GetRequest{ServiceID: id})
if err != nil { if err != nil {
@@ -609,8 +662,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Compute list", bytes, compute_cb.ListComputes{}, t) getResult("Compute list", bytes, compute_cb.ListComputes{}, t)
// Get // Get
listComp, _ := client.CloudBroker().Compute().List(context.Background(), compute_cb.ListRequest{}) listComp, err := client.CloudBroker().Compute().List(context.Background(), compute_cb.ListRequest{})
if len(listComp.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Compute.List failed: %v", err)
} else if len(listComp.Data) > 0 {
id := listComp.Data[0].ID id := listComp.Data[0].ID
bytes, err = client.CloudBroker().Compute().GetRaw(context.Background(), compute_cb.GetRequest{ComputeID: id}) bytes, err = client.CloudBroker().Compute().GetRaw(context.Background(), compute_cb.GetRequest{ComputeID: id})
if err != nil { if err != nil {
@@ -635,8 +690,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Disk list", bytes, disks_cb.ListDisks{}, t) getResult("Disk list", bytes, disks_cb.ListDisks{}, t)
// Get // Get
listDisk, _ := client.CloudBroker().Disks().List(context.Background(), disks_cb.ListRequest{}) listDisk, err := client.CloudBroker().Disks().List(context.Background(), disks_cb.ListRequest{})
if len(listDisk.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Disks.List failed: %v", err)
} else if len(listDisk.Data) > 0 {
id := listDisk.Data[0].ID id := listDisk.Data[0].ID
bytes, err = client.CloudBroker().Disks().GetRaw(context.Background(), disks_cb.GetRequest{DiskID: id}) bytes, err = client.CloudBroker().Disks().GetRaw(context.Background(), disks_cb.GetRequest{DiskID: id})
if err != nil { if err != nil {
@@ -655,8 +712,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("ExtNet list", bytes, extnet_cb.ListExtNet{}, t) getResult("ExtNet list", bytes, extnet_cb.ListExtNet{}, t)
// Get // Get
listExtNet, _ := client.CloudBroker().ExtNet().List(context.Background(), extnet_cb.ListRequest{}) listExtNet, err := client.CloudBroker().ExtNet().List(context.Background(), extnet_cb.ListRequest{})
if len(listExtNet.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.ExtNet.List failed: %v", err)
} else if len(listExtNet.Data) > 0 {
id := listExtNet.Data[0].ID id := listExtNet.Data[0].ID
bytes, err = client.CloudBroker().ExtNet().GetRaw(context.Background(), extnet_cb.GetRequest{NetID: id}) bytes, err = client.CloudBroker().ExtNet().GetRaw(context.Background(), extnet_cb.GetRequest{NetID: id})
if err != nil { if err != nil {
@@ -675,8 +734,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("FLIPGroup list", bytes, flipgroup_cb.ListFLIPGroups{}, t) getResult("FLIPGroup list", bytes, flipgroup_cb.ListFLIPGroups{}, t)
// Get // Get
listFG, _ := client.CloudBroker().FLIPGroup().List(context.Background(), flipgroup_cb.ListRequest{}) listFG, err := client.CloudBroker().FLIPGroup().List(context.Background(), flipgroup_cb.ListRequest{})
if len(listFG.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.FLIPGroup.List failed: %v", err)
} else if len(listFG.Data) > 0 {
id := listFG.Data[0].ID id := listFG.Data[0].ID
bytes, err = client.CloudBroker().FLIPGroup().GetRaw(context.Background(), flipgroup_cb.GetRequest{FLIPGroupID: id}) bytes, err = client.CloudBroker().FLIPGroup().GetRaw(context.Background(), flipgroup_cb.GetRequest{FLIPGroupID: id})
if err != nil { if err != nil {
@@ -695,8 +756,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Grid list", bytes, grid_cb.ListGrids{}, t) getResult("Grid list", bytes, grid_cb.ListGrids{}, t)
// Get // Get
listGrid, _ := client.CloudBroker().Grid().List(context.Background(), grid_cb.ListRequest{}) listGrid, err := client.CloudBroker().Grid().List(context.Background(), grid_cb.ListRequest{})
if len(listGrid.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Grid.List failed: %v", err)
} else if len(listGrid.Data) > 0 {
id := listGrid.Data[0].ID id := listGrid.Data[0].ID
bytes, err = client.CloudBroker().Grid().GetRaw(context.Background(), grid_cb.GetRequest{GID: id}) bytes, err = client.CloudBroker().Grid().GetRaw(context.Background(), grid_cb.GetRequest{GID: id})
if err != nil { if err != nil {
@@ -715,8 +778,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Image list", bytes, image_cb.ListImages{}, t) getResult("Image list", bytes, image_cb.ListImages{}, t)
// Get // Get
listImg, _ := client.CloudBroker().Image().List(context.Background(), image_cb.ListRequest{}) listImg, err := client.CloudBroker().Image().List(context.Background(), image_cb.ListRequest{})
if len(listImg.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Image.List failed: %v", err)
} else if len(listImg.Data) > 0 {
id := listImg.Data[0].ID id := listImg.Data[0].ID
bytes, err = client.CloudBroker().Image().GetRaw(context.Background(), image_cb.GetRequest{ImageID: id}) bytes, err = client.CloudBroker().Image().GetRaw(context.Background(), image_cb.GetRequest{ImageID: id})
if err != nil { if err != nil {
@@ -735,8 +800,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("K8CI list", bytes, k8ci_cb.ListK8CI{}, t) getResult("K8CI list", bytes, k8ci_cb.ListK8CI{}, t)
// Get // Get
listk8ci, _ := client.CloudBroker().K8CI().List(context.Background(), k8ci_cb.ListRequest{}) listk8ci, err := client.CloudBroker().K8CI().List(context.Background(), k8ci_cb.ListRequest{})
if len(listk8ci.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.K8CI.List failed: %v", err)
} else if len(listk8ci.Data) > 0 {
id := listk8ci.Data[0].ID id := listk8ci.Data[0].ID
bytes, err = client.CloudBroker().K8CI().GetRaw(context.Background(), k8ci_cb.GetRequest{K8CIID: id}) bytes, err = client.CloudBroker().K8CI().GetRaw(context.Background(), k8ci_cb.GetRequest{K8CIID: id})
if err != nil { if err != nil {
@@ -755,8 +822,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("K8S list", bytes, k8s_cb.ListK8S{}, t) getResult("K8S list", bytes, k8s_cb.ListK8S{}, t)
// Get // Get
listk8s, _ := client.CloudBroker().K8S().List(context.Background(), k8s_cb.ListRequest{}) listk8s, err := client.CloudBroker().K8S().List(context.Background(), k8s_cb.ListRequest{})
if len(listk8s.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.K8S.List failed: %v", err)
} else if len(listk8s.Data) > 0 {
id := listk8s.Data[0].ID id := listk8s.Data[0].ID
bytes, err = client.CloudBroker().K8S().GetRaw(context.Background(), k8s_cb.GetRequest{K8SID: id}) bytes, err = client.CloudBroker().K8S().GetRaw(context.Background(), k8s_cb.GetRequest{K8SID: id})
if err != nil { if err != nil {
@@ -775,8 +844,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("LB list", bytes, lb_cb.ListLB{}, t) getResult("LB list", bytes, lb_cb.ListLB{}, t)
// Get // Get
listLB, _ := client.CloudBroker().LB().List(context.Background(), lb_cb.ListRequest{}) listLB, err := client.CloudBroker().LB().List(context.Background(), lb_cb.ListRequest{})
if len(listLB.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.LB.List failed: %v", err)
} else if len(listLB.Data) > 0 {
id := listLB.Data[0].ID id := listLB.Data[0].ID
bytes, err = client.CloudBroker().LB().GetRaw(context.Background(), lb_cb.GetRequest{LBID: id}) bytes, err = client.CloudBroker().LB().GetRaw(context.Background(), lb_cb.GetRequest{LBID: id})
if err != nil { if err != nil {
@@ -795,8 +866,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Node list", bytes, node_cb.ListNodes{}, t) getResult("Node list", bytes, node_cb.ListNodes{}, t)
// Get // Get
listNode, _ := client.CloudBroker().Node().List(context.Background(), node_cb.ListRequest{}) listNode, err := client.CloudBroker().Node().List(context.Background(), node_cb.ListRequest{})
if listNode != nil && len(listNode.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Node.List failed: %v", err)
} else if listNode != nil && len(listNode.Data) > 0 {
id := listNode.Data[0].ID id := listNode.Data[0].ID
bytes, err = client.CloudBroker().Node().GetRaw(context.Background(), node_cb.GetRequest{NID: id}) bytes, err = client.CloudBroker().Node().GetRaw(context.Background(), node_cb.GetRequest{NID: id})
if err != nil { if err != nil {
@@ -841,8 +914,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("QoS policy list", bytes, qospolicy_cb.ListQoSPolicies{}, t) getResult("QoS policy list", bytes, qospolicy_cb.ListQoSPolicies{}, t)
// Get // Get
listQosPolicies, _ := client.CloudBroker().QoSPolicy().List(context.Background(), qospolicy_cb.ListRequest{}) listQosPolicies, err := client.CloudBroker().QoSPolicy().List(context.Background(), qospolicy_cb.ListRequest{})
if len(listQosPolicies.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.QoSPolicy.List failed: %v", err)
} else if len(listQosPolicies.Data) > 0 {
id := listQosPolicies.Data[0].ID id := listQosPolicies.Data[0].ID
bytes, err = client.CloudBroker().QoSPolicy().GetRaw(context.Background(), qospolicy_cb.GetRequest{QoSPolicyID: id}) bytes, err = client.CloudBroker().QoSPolicy().GetRaw(context.Background(), qospolicy_cb.GetRequest{QoSPolicyID: id})
if err != nil { if err != nil {
@@ -861,8 +936,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("RG list", bytes, rg_cb.ListRG{}, t) getResult("RG list", bytes, rg_cb.ListRG{}, t)
// Get // Get
listRG, _ := client.CloudBroker().RG().List(context.Background(), rg_cb.ListRequest{}) listRG, err := client.CloudBroker().RG().List(context.Background(), rg_cb.ListRequest{})
if len(listRG.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.RG.List failed: %v", err)
} else if len(listRG.Data) > 0 {
id := listRG.Data[0].ID id := listRG.Data[0].ID
bytes, err = client.CloudBroker().RG().GetRaw(context.Background(), rg_cb.GetRequest{RGID: id}) bytes, err = client.CloudBroker().RG().GetRaw(context.Background(), rg_cb.GetRequest{RGID: id})
if err != nil { if err != nil {
@@ -881,8 +958,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Security group list", bytes, secgroup_cb.ListSecurityGroups{}, t) getResult("Security group list", bytes, secgroup_cb.ListSecurityGroups{}, t)
// Get // Get
listSecurityGroups, _ := client.CloudBroker().SecurityGroup().List(context.Background(), secgroup_cb.ListRequest{}) listSecurityGroups, err := client.CloudBroker().SecurityGroup().List(context.Background(), secgroup_cb.ListRequest{})
if len(listSecurityGroups.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.SecurityGroup.List failed: %v", err)
} else if len(listSecurityGroups.Data) > 0 {
id := listSecurityGroups.Data[0].ID id := listSecurityGroups.Data[0].ID
bytes, err = client.CloudBroker().SecurityGroup().GetRaw(context.Background(), secgroup_cb.GetRequest{SecurityGroupID: id}) bytes, err = client.CloudBroker().SecurityGroup().GetRaw(context.Background(), secgroup_cb.GetRequest{SecurityGroupID: id})
if err != nil { if err != nil {
@@ -901,8 +980,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("SEP list", bytes, sep_cb.ListSEP{}, t) getResult("SEP list", bytes, sep_cb.ListSEP{}, t)
// Get // Get
listSEP, _ := client.CloudBroker().SEP().List(context.Background(), sep_cb.ListRequest{}) listSEP, err := client.CloudBroker().SEP().List(context.Background(), sep_cb.ListRequest{})
if len(listSEP.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.SEP.List failed: %v", err)
} else if len(listSEP.Data) > 0 {
id := listSEP.Data[0].ID id := listSEP.Data[0].ID
bytes, err = client.CloudBroker().SEP().GetRaw(context.Background(), sep_cb.GetRequest{SEPID: id}) bytes, err = client.CloudBroker().SEP().GetRaw(context.Background(), sep_cb.GetRequest{SEPID: id})
if err != nil { if err != nil {
@@ -927,8 +1008,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Storage policy list", bytes, stpolicy_cb.ListStoragePolicies{}, t) getResult("Storage policy list", bytes, stpolicy_cb.ListStoragePolicies{}, t)
// Get // Get
listStoragePolicies, _ := client.CloudBroker().StPolicy().List(context.Background(), stpolicy_cb.ListRequest{}) listStoragePolicies, err := client.CloudBroker().StPolicy().List(context.Background(), stpolicy_cb.ListRequest{})
if len(listStoragePolicies.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.StPolicy.List failed: %v", err)
} else if len(listStoragePolicies.Data) > 0 {
id := listStoragePolicies.Data[0].ID id := listStoragePolicies.Data[0].ID
bytes, err = client.CloudBroker().StPolicy().GetRaw(context.Background(), stpolicy_cb.GetRequest{StoragePolicyID: id}) bytes, err = client.CloudBroker().StPolicy().GetRaw(context.Background(), stpolicy_cb.GetRequest{StoragePolicyID: id})
if err != nil { if err != nil {
@@ -947,8 +1030,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Replication profile list", bytes, stpolicy_cb.ListReplicationProfiles{}, t) getResult("Replication profile list", bytes, stpolicy_cb.ListReplicationProfiles{}, t)
// Get // Get
listReplicationProfiles, _ := client.CloudBroker().StPolicy().ListReplicationProfile(context.Background(), stpolicy_cb.ListReplicationProfileRequest{}) listReplicationProfiles, err := client.CloudBroker().StPolicy().ListReplicationProfile(context.Background(), stpolicy_cb.ListReplicationProfileRequest{})
if len(listReplicationProfiles.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.StPolicy.ListReplicationProfile failed: %v", err)
} else if len(listReplicationProfiles.Data) > 0 {
id := listReplicationProfiles.Data[0].ID id := listReplicationProfiles.Data[0].ID
bytes, err = client.CloudBroker().StPolicy().GetReplicationProfileRaw(context.Background(), stpolicy_cb.GetReplicationProfileRequest{ProfileID: id}) bytes, err = client.CloudBroker().StPolicy().GetReplicationProfileRaw(context.Background(), stpolicy_cb.GetReplicationProfileRequest{ProfileID: id})
if err != nil { if err != nil {
@@ -975,8 +1060,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Trunk list", bytes, trunk_cb.ListTrunks{}, t) getResult("Trunk list", bytes, trunk_cb.ListTrunks{}, t)
// Get // Get
listTrunks, _ := client.CloudBroker().Trunk().List(context.Background(), trunk_cb.ListRequest{}) listTrunks, err := client.CloudBroker().Trunk().List(context.Background(), trunk_cb.ListRequest{})
if len(listTrunks.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Trunk.List failed: %v", err)
} else if len(listTrunks.Data) > 0 {
id := listTrunks.Data[0].ID id := listTrunks.Data[0].ID
bytes, err = client.CloudBroker().Trunk().GetRaw(context.Background(), trunk_cb.GetRequest{TrunkID: id}) bytes, err = client.CloudBroker().Trunk().GetRaw(context.Background(), trunk_cb.GetRequest{TrunkID: id})
if err != nil { if err != nil {
@@ -995,8 +1082,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("VINS list", bytes, vins_cb.ListVINS{}, t) getResult("VINS list", bytes, vins_cb.ListVINS{}, t)
// Get // Get
listVINS, _ := client.CloudBroker().VINS().List(context.Background(), vins_cb.ListRequest{}) listVINS, err := client.CloudBroker().VINS().List(context.Background(), vins_cb.ListRequest{})
if len(listVINS.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.VINS.List failed: %v", err)
} else if len(listVINS.Data) > 0 {
id := listVINS.Data[0].ID id := listVINS.Data[0].ID
bytes, err = client.CloudBroker().VINS().GetRaw(context.Background(), vins_cb.GetRequest{VINSID: id}) bytes, err = client.CloudBroker().VINS().GetRaw(context.Background(), vins_cb.GetRequest{VINSID: id})
if err != nil { if err != nil {
@@ -1007,24 +1096,31 @@ func TestGetListCloudbroker(t *testing.T) {
t.Errorf("Can not test VINS get because VINS list is empty") t.Errorf("Can not test VINS get because VINS list is empty")
} }
// VM folder // VM folder (list requires rg_id)
// List if listRG == nil || len(listRG.Data) == 0 {
bytes, err = client.CloudBroker().VMFolder().ListRaw(context.Background(), vmfolder_cb.ListRequest{}) t.Errorf("Can not test VM folder because VM folder list is empty")
if err != nil { } else {
t.Error(err) rgID := listRG.Data[0].ID
} // List
getResult("VM folder list", bytes, vmfolder_cb.ListVMFolder{}, t) bytes, err = client.CloudBroker().VMFolder().ListRaw(context.Background(), vmfolder_cb.ListRequest{RGID: rgID})
// Get
listVMFolders, _ := client.CloudBroker().VMFolder().List(context.Background(), vmfolder_cb.ListRequest{})
if len(listVMFolders.Data) > 0 {
id := listVMFolders.Data[0].ID
bytes, err = client.CloudBroker().VMFolder().GetRaw(context.Background(), vmfolder_cb.GetRequest{FolderID: id})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
getResult("VM folder get", bytes, vmfolder_cb.RecordVMFolder{}, t) getResult("VM folder list", bytes, vmfolder_cb.ListVMFolder{}, t)
} else { // Get
t.Errorf("Can not test VM folder get because VM folder list is empty") listVMFolders, err := client.CloudBroker().VMFolder().List(context.Background(), vmfolder_cb.ListRequest{RGID: rgID})
if err != nil {
t.Errorf("Can not test get because CloudBroker.VMFolder.List failed: %v", err)
} else if len(listVMFolders.Data) > 0 {
id := listVMFolders.Data[0].ID
bytes, err = client.CloudBroker().VMFolder().GetRaw(context.Background(), vmfolder_cb.GetRequest{FolderID: id})
if err != nil {
t.Error(err)
}
getResult("VM folder get", bytes, vmfolder_cb.RecordVMFolder{}, t)
} else {
t.Errorf("Can not test VM folder get because VM folder list is empty")
}
} }
// Zone // Zone
@@ -1035,8 +1131,10 @@ func TestGetListCloudbroker(t *testing.T) {
} }
getResult("Zone list", bytes, zone.ListZones{}, t) getResult("Zone list", bytes, zone.ListZones{}, t)
// Get // Get
ListZones, _ := client.CloudBroker().Zone().List(context.Background(), zone_cb.ListRequest{}) ListZones, err := client.CloudBroker().Zone().List(context.Background(), zone_cb.ListRequest{})
if len(ListZones.Data) > 0 { if err != nil {
t.Errorf("Can not test get because CloudBroker.Zone.List failed: %v", err)
} else if len(ListZones.Data) > 0 {
id := ListZones.Data[0].ID id := ListZones.Data[0].ID
bytes, err = client.CloudBroker().Zone().GetRaw(context.Background(), zone_cb.GetRequest{ID: id}) bytes, err = client.CloudBroker().Zone().GetRaw(context.Background(), zone_cb.GetRequest{ID: id})
if err != nil { if err != nil {
@@ -1082,8 +1180,10 @@ func TestGetListSDNAPI(t *testing.T) {
// Users AccessGroups // Users AccessGroups
// list // list
listGroups, _ := client.SDN().AccessGroups().List(context.Background(), acsgroups.ListGroupsRequest{}) listGroups, err := client.SDN().AccessGroups().List(context.Background(), acsgroups.ListGroupsRequest{})
if len(listGroups.AccessGroups) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.AccessGroups.List failed: %v", err)
} else if len(listGroups.AccessGroups) > 0 {
id := listGroups.AccessGroups[0].ID id := listGroups.AccessGroups[0].ID
bytes, err = client.SDN().AccessGroups().UserListRaw(context.Background(), acsgroups.UsersListRequest{AccessGroupID: id}) bytes, err = client.SDN().AccessGroups().UserListRaw(context.Background(), acsgroups.UsersListRequest{AccessGroupID: id})
if err != nil { if err != nil {
@@ -1111,8 +1211,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("Address pools list", bytes, adrspools.AddressPoolsList{}, t) getResult("Address pools list", bytes, adrspools.AddressPoolsList{}, t)
// Get // Get
listStoragePolicies, _ := client.SDN().AddressPools().List(context.Background(), adrspools.ListAddressPoolsRequest{}) listStoragePolicies, err := client.SDN().AddressPools().List(context.Background(), adrspools.ListAddressPoolsRequest{})
if len(listStoragePolicies.Pools) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.AddressPools.List failed: %v", err)
} else if len(listStoragePolicies.Pools) > 0 {
id := listStoragePolicies.Pools[0].ID id := listStoragePolicies.Pools[0].ID
bytes, err = client.SDN().AddressPools().GetRaw(context.Background(), adrspools.GetRequest{ID: id}) bytes, err = client.SDN().AddressPools().GetRaw(context.Background(), adrspools.GetRequest{ID: id})
if err != nil { if err != nil {
@@ -1131,8 +1233,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("Security policies list", bytes, secpolicies.SecurityPolicyList{}, t) getResult("Security policies list", bytes, secpolicies.SecurityPolicyList{}, t)
// Get // Get
listPolicies, _ := client.SDN().SecurityPolicies().List(context.Background(), secpolicies.ListRequest{}) listPolicies, err := client.SDN().SecurityPolicies().List(context.Background(), secpolicies.ListRequest{})
if len(listPolicies) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.SecurityPolicies.List failed: %v", err)
} else if len(listPolicies) > 0 {
id := listPolicies[0].ID id := listPolicies[0].ID
bytes, err = client.SDN().SecurityPolicies().GetRaw(context.Background(), secpolicies.GetRequest{ID: id}) bytes, err = client.SDN().SecurityPolicies().GetRaw(context.Background(), secpolicies.GetRequest{ID: id})
if err != nil { if err != nil {
@@ -1150,8 +1254,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("Security policy rule list", bytes, rule.SecurityRulesList{}, t) getResult("Security policy rule list", bytes, rule.SecurityRulesList{}, t)
// Get // Get
listRules, _ := client.SDN().SecurityPolicies().Rule().List(context.Background(), rule.ListRequest{}) listRules, err := client.SDN().SecurityPolicies().Rule().List(context.Background(), rule.ListRequest{})
if len(listRules) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.SecurityPolicies.Rule.List failed: %v", err)
} else if len(listRules) > 0 {
id := listRules[0].ID id := listRules[0].ID
securityPolicyID := listRules[0].SecurityPolicyID securityPolicyID := listRules[0].SecurityPolicyID
bytes, err = client.SDN().SecurityPolicies().Rule().GetRaw(context.Background(), rule.GetRequest{ bytes, err = client.SDN().SecurityPolicies().Rule().GetRaw(context.Background(), rule.GetRequest{
@@ -1173,8 +1279,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("ExtNet list", bytes, extnet_sdn.ListExtNet{}, t) getResult("ExtNet list", bytes, extnet_sdn.ListExtNet{}, t)
// Get // Get
listExtNet, _ := client.SDN().ExtNet().List(context.Background(), extnet_sdn.ListRequest{}) listExtNet, err := client.SDN().ExtNet().List(context.Background(), extnet_sdn.ListRequest{})
if len(listExtNet) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.ExtNet.List failed: %v", err)
} else if len(listExtNet) > 0 {
id := listExtNet[0].ID id := listExtNet[0].ID
bytes, err = client.SDN().ExtNet().GetRaw(context.Background(), extnet_sdn.GetRequest{ExtNetID: id}) bytes, err = client.SDN().ExtNet().GetRaw(context.Background(), extnet_sdn.GetRequest{ExtNetID: id})
if err != nil { if err != nil {
@@ -1193,8 +1301,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("NetObjGroup list", bytes, netobjgroups.NetObjGroupList{}, t) getResult("NetObjGroup list", bytes, netobjgroups.NetObjGroupList{}, t)
// Get // Get
listNetObjGroups, _ := client.SDN().NetworkObjectGroups().List(context.Background(), netobjgroups.ListRequest{}) listNetObjGroups, err := client.SDN().NetworkObjectGroups().List(context.Background(), netobjgroups.ListRequest{})
if len(listNetObjGroups.Objects) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.NetworkObjectGroups.List failed: %v", err)
} else if len(listNetObjGroups.Objects) > 0 {
id := listNetObjGroups.Objects[0].ID id := listNetObjGroups.Objects[0].ID
bytes, err = client.SDN().NetworkObjectGroups().GetRaw(context.Background(), netobjgroups.GetRequest{NetObjGroupID: id}) bytes, err = client.SDN().NetworkObjectGroups().GetRaw(context.Background(), netobjgroups.GetRequest{NetObjGroupID: id})
if err != nil { if err != nil {
@@ -1213,8 +1323,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("Segments list", bytes, segments.ListSegment{}, t) getResult("Segments list", bytes, segments.ListSegment{}, t)
// Get // Get
listSegments, _ := client.SDN().Segments().List(context.Background(), segments.ListRequest{}) listSegments, err := client.SDN().Segments().List(context.Background(), segments.ListRequest{})
if len(listSegments) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.Segments.List failed: %v", err)
} else if len(listSegments) > 0 {
id := listSegments[0].ID id := listSegments[0].ID
bytes, err = client.SDN().Segments().GetRaw(context.Background(), segments.GetRequest{SegmentID: id}) bytes, err = client.SDN().Segments().GetRaw(context.Background(), segments.GetRequest{SegmentID: id})
if err != nil { if err != nil {
@@ -1233,8 +1345,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("Hypervisors list", bytes, hypervisors.HypervisorsList{}, t) getResult("Hypervisors list", bytes, hypervisors.HypervisorsList{}, t)
// Get // Get
listHypervisors, _ := client.SDN().Hypervisors().List(context.Background(), hypervisors.ListRequest{}) listHypervisors, err := client.SDN().Hypervisors().List(context.Background(), hypervisors.ListRequest{})
if len(listHypervisors) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.Hypervisors.List failed: %v", err)
} else if len(listHypervisors) > 0 {
id := listHypervisors[0].Name id := listHypervisors[0].Name
bytes, err = client.SDN().Hypervisors().GetRaw(context.Background(), hypervisors.GetRequest{Name: id}) bytes, err = client.SDN().Hypervisors().GetRaw(context.Background(), hypervisors.GetRequest{Name: id})
if err != nil { if err != nil {
@@ -1253,8 +1367,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("FloatingIPs list", bytes, flips.FloatingIPsList{}, t) getResult("FloatingIPs list", bytes, flips.FloatingIPsList{}, t)
// Get // Get
listFloatingIPs, _ := client.SDN().FloatingIPs().List(context.Background(), flips.ListRequest{}) listFloatingIPs, err := client.SDN().FloatingIPs().List(context.Background(), flips.ListRequest{})
if len(listFloatingIPs.Objects) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.FloatingIPs.List failed: %v", err)
} else if len(listFloatingIPs.Objects) > 0 {
id := listFloatingIPs.Objects[0].ID id := listFloatingIPs.Objects[0].ID
bytes, err = client.SDN().FloatingIPs().GetRaw(context.Background(), flips.GetRequest{FloatingIPID: id}) bytes, err = client.SDN().FloatingIPs().GetRaw(context.Background(), flips.GetRequest{FloatingIPID: id})
if err != nil { if err != nil {
@@ -1273,8 +1389,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("Logical ports list", bytes, logicalports.LogicalPortsList{}, t) getResult("Logical ports list", bytes, logicalports.LogicalPortsList{}, t)
// Get // Get
listLP, _ := client.SDN().LogicalPorts().List(context.Background(), logicalports.ListRequest{}) listLP, err := client.SDN().LogicalPorts().List(context.Background(), logicalports.ListRequest{})
if len(listLP.Ports) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.LogicalPorts.List failed: %v", err)
} else if len(listLP.Ports) > 0 {
id := listStoragePolicies.Pools[0].ID id := listStoragePolicies.Pools[0].ID
bytes, err = client.SDN().LogicalPorts().GetRaw(context.Background(), logicalports.GetRequest{ID: id}) bytes, err = client.SDN().LogicalPorts().GetRaw(context.Background(), logicalports.GetRequest{ID: id})
if err != nil { if err != nil {
@@ -1293,8 +1411,10 @@ func TestGetListSDNAPI(t *testing.T) {
} }
getResult("Routers list", bytes, routers.RoutersList{}, t) getResult("Routers list", bytes, routers.RoutersList{}, t)
// Get // Get
routerList, _ := client.SDN().Routers().List(context.Background(), routers.ListRequest{}) routerList, err := client.SDN().Routers().List(context.Background(), routers.ListRequest{})
if len(routerList) > 0 { if err != nil {
t.Errorf("Can not test get because SDN.Routers.List failed: %v", err)
} else if len(routerList) > 0 {
id := routerList[0].ID id := routerList[0].ID
bytes, err = client.SDN().Routers().GetRaw(context.Background(), routers.GetRequest{ID: id}) bytes, err = client.SDN().Routers().GetRaw(context.Background(), routers.GetRequest{ID: id})
if err != nil { if err != nil {

View File

@@ -694,6 +694,12 @@ func getRequestsMapCloudbroker() map[string]interface{} {
"/restmachine/cloudbroker/compute/migrate_abort": compute_cb.MigrateAbortRequest{}, "/restmachine/cloudbroker/compute/migrate_abort": compute_cb.MigrateAbortRequest{},
"/restmachine/cloudbroker/compute/start_migration_out": compute_cb.StartMigrationOutRequest{}, "/restmachine/cloudbroker/compute/start_migration_out": compute_cb.StartMigrationOutRequest{},
"/restmachine/cloudbroker/compute/stop_migration_out": compute_cb.StopMigrationOutRequest{}, "/restmachine/cloudbroker/compute/stop_migration_out": compute_cb.StopMigrationOutRequest{},
"/restmachine/cloudbroker/compute/migrate_v2_resolve": compute_cb.MigrateV2ResolveRequest{},
"/restmachine/cloudbroker/compute/migrate_v2_list": compute_cb.MigrateV2ListRequest{},
"/restmachine/cloudbroker/compute/migrate_v2_get": compute_cb.MigrateV2GetRequest{},
"/restmachine/cloudbroker/compute/migrate_v2_abort": compute_cb.MigrateV2AbortRequest{},
"/restmachine/cloudbroker/compute/migrate_v2_cleanup": compute_cb.MigrateV2CleanupRequest{},
"/restmachine/cloudbroker/compute/migrate_v2": compute_cb.MigrateV2Request{},
// disks // disks
"/restmachine/cloudbroker/disks/change_disk_storage_policy": disks_cb.ChangeDiskStoragePolicyRequest{}, "/restmachine/cloudbroker/disks/change_disk_storage_policy": disks_cb.ChangeDiskStoragePolicyRequest{},
@@ -1173,16 +1179,6 @@ func getRequestsMapCloudbroker() map[string]interface{} {
"/restmachine/cloudbroker/zone/test_cpu_alignment_profile": zone_cb.TestCPUAlignmentProfileRequest{}, "/restmachine/cloudbroker/zone/test_cpu_alignment_profile": zone_cb.TestCPUAlignmentProfileRequest{},
"/restmachine/cloudbroker/zone/set_resources_limit": zone_cb.SetResourcesLimitRequest{}, "/restmachine/cloudbroker/zone/set_resources_limit": zone_cb.SetResourcesLimitRequest{},
"/restmachine/cloudbroker/zone/update_cpu_alignment_profile": zone_cb.UpdateCPUAlignmentProfileRequest{}, "/restmachine/cloudbroker/zone/update_cpu_alignment_profile": zone_cb.UpdateCPUAlignmentProfileRequest{},
// security policy
"/sdn/security_policy/create": secpolicies.CreateRequest{},
"/sdn/security_policy/delete": secpolicies.DeleteRequest{},
"/sdn/security_policy/get": secpolicies.GetRequest{},
"/sdn/security_policy/list": secpolicies.ListRequest{},
"/sdn/security_policy/move": secpolicies.MoveRequest{},
"/sdn/security_policy/update": secpolicies.UpdateRequest{},
"/sdn/security_policy/rule/get": rule.GetRequest{},
"/sdn/security_policy/rule/list": rule.ListRequest{},
} }
} }
@@ -1211,6 +1207,16 @@ func getRequestsMapSDN() map[string]interface{} {
"/restmachine/sdn/default_security_policy/list": defsecpolicies.ListRequest{}, "/restmachine/sdn/default_security_policy/list": defsecpolicies.ListRequest{},
"/restmachine/sdn/default_security_policy/update": defsecpolicies.UpdateRequest{}, "/restmachine/sdn/default_security_policy/update": defsecpolicies.UpdateRequest{},
// security policy
"/restmachine/sdn/security_policy/create": secpolicies.CreateRequest{},
"/restmachine/sdn/security_policy/delete": secpolicies.DeleteRequest{},
"/restmachine/sdn/security_policy/get": secpolicies.GetRequest{},
"/restmachine/sdn/security_policy/list": secpolicies.ListRequest{},
"/restmachine/sdn/security_policy/move": secpolicies.MoveRequest{},
"/restmachine/sdn/security_policy/update": secpolicies.UpdateRequest{},
"/restmachine/sdn/security_policy/rule/get": rule.GetRequest{},
"/restmachine/sdn/security_policy/rule/list": rule.ListRequest{},
// extnet // extnet
"/restmachine/sdn/external_network/port_add": extnet_sdn.PortAddRequest{}, "/restmachine/sdn/external_network/port_add": extnet_sdn.PortAddRequest{},
"/restmachine/sdn/external_network/port_update": extnet_sdn.PortUpdateRequest{}, "/restmachine/sdn/external_network/port_update": extnet_sdn.PortUpdateRequest{},

View File

@@ -0,0 +1,446 @@
package test
import (
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
)
// transport defines which struct tags are used to serialize request on the wire
const (
transportForm = "form"
transportJSON = "json"
)
// sentField describes one request parameter exactly as SDK puts it on the wire
type sentField struct {
// Name of the parameter on the wire
Name string
// Name of the go field (for messages)
GoName string
// Go type of the field, nil if it can not be resolved
Type reflect.Type
// Field is not sent when it has zero value
OmitEmpty bool
// Value of `validate` tag
Validate string
}
// sdkCall describes one DecortApiCall* invocation found in SDK sources
type sdkCall struct {
// Directory of package the call was found in
Dir string
// Name of the type passed as params (may be unexported wrapper), empty for nil params
TypeName string
Transport string
// Name of function the call was found in
FuncName string
}
// sdkIndex stores parsed SDK packages: struct declarations and API calls by url
type sdkIndex struct {
// dir -> type name -> type expression
types map[string]map[string]ast.Expr
// url (e.g. "/cloudapi/compute/start") -> calls
calls map[string][]sdkCall
}
var (
sdkIndexOnce sync.Once
sdkIndexCached *sdkIndex
sdkIndexErr error
)
// getSDKIndex parses SDK sources in ../../pkg once per test run
func getSDKIndex() (*sdkIndex, error) {
sdkIndexOnce.Do(func() {
sdkIndexCached, sdkIndexErr = buildSDKIndex("../../pkg")
})
return sdkIndexCached, sdkIndexErr
}
func buildSDKIndex(root string) (*sdkIndex, error) {
idx := newSDKIndex()
fset := token.NewFileSet()
err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
return nil
}
file, err := parser.ParseFile(fset, path, nil, 0)
if err != nil {
return err
}
idx.addFile(filepath.Dir(path), file)
return nil
})
if err != nil {
return nil, err
}
return idx, nil
}
func newSDKIndex() *sdkIndex {
return &sdkIndex{
types: make(map[string]map[string]ast.Expr),
calls: make(map[string][]sdkCall),
}
}
// addFile collects type declarations and API calls of one file
func (idx *sdkIndex) addFile(dir string, file *ast.File) {
if idx.types[dir] == nil {
idx.types[dir] = make(map[string]ast.Expr)
}
for _, decl := range file.Decls {
switch d := decl.(type) {
case *ast.GenDecl:
for _, spec := range d.Specs {
if ts, ok := spec.(*ast.TypeSpec); ok {
idx.types[dir][ts.Name.Name] = ts.Type
}
}
case *ast.FuncDecl:
if d.Body != nil {
idx.addFuncCalls(dir, d)
}
}
}
}
// addFuncCalls finds DecortApiCall* invocations in function body and resolves url and type of params
func (idx *sdkIndex) addFuncCalls(dir string, fn *ast.FuncDecl) {
// variable name -> type name
varTypes := make(map[string]string)
for _, field := range fn.Type.Params.List {
for _, name := range field.Names {
varTypes[name.Name] = typeNameOf(field.Type)
}
}
// variable name -> last assigned string literal
strVars := make(map[string]string)
ast.Inspect(fn.Body, func(n ast.Node) bool {
switch node := n.(type) {
case *ast.AssignStmt:
for i, lhs := range node.Lhs {
ident, ok := lhs.(*ast.Ident)
if !ok || i >= len(node.Rhs) {
continue
}
if s, ok := stringLiteral(node.Rhs[i]); ok {
strVars[ident.Name] = s
}
if name := typeNameOfValue(node.Rhs[i]); name != "" {
varTypes[ident.Name] = name
}
}
case *ast.ValueSpec:
for i, name := range node.Names {
if node.Type != nil {
varTypes[name.Name] = typeNameOf(node.Type)
}
if i < len(node.Values) {
if s, ok := stringLiteral(node.Values[i]); ok {
strVars[name.Name] = s
}
if tn := typeNameOfValue(node.Values[i]); tn != "" {
varTypes[name.Name] = tn
}
}
}
case *ast.CallExpr:
sel, ok := node.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
var urlArg, paramsArg ast.Expr
transport := transportForm
switch sel.Sel.Name {
case "DecortApiCall", "DecortApiCallMP":
if len(node.Args) != 4 {
return true
}
urlArg, paramsArg = node.Args[2], node.Args[3]
case "DecortApiCallCtype":
if len(node.Args) != 5 {
return true
}
urlArg, paramsArg = node.Args[2], node.Args[4]
if ctype, ok := node.Args[3].(*ast.SelectorExpr); ok && ctype.Sel.Name == "MIMEJSON" {
transport = transportJSON
}
default:
return true
}
url, ok := stringLiteral(urlArg)
if !ok {
ident, isIdent := urlArg.(*ast.Ident)
if !isIdent {
return true
}
if url, ok = strVars[ident.Name]; !ok {
return true
}
}
call := sdkCall{Dir: dir, Transport: transport, FuncName: fn.Name.Name}
switch p := paramsArg.(type) {
case *ast.Ident:
if p.Name != "nil" {
call.TypeName = varTypes[p.Name]
}
default:
call.TypeName = typeNameOfValue(paramsArg)
}
idx.calls[url] = append(idx.calls[url], call)
}
return true
})
}
func stringLiteral(e ast.Expr) (string, bool) {
lit, ok := e.(*ast.BasicLit)
if !ok || lit.Kind != token.STRING {
return "", false
}
s, err := strconv.Unquote(lit.Value)
return s, err == nil
}
// typeNameOfValue returns type name of expressions like T{...} or &T{...}
func typeNameOfValue(e ast.Expr) string {
switch v := e.(type) {
case *ast.CompositeLit:
return typeNameOf(v.Type)
case *ast.UnaryExpr:
return typeNameOfValue(v.X)
}
return ""
}
// typeNameOf returns name of local named type (T or *T), empty string otherwise
func typeNameOf(e ast.Expr) string {
switch v := e.(type) {
case *ast.Ident:
return v.Name
case *ast.StarExpr:
return typeNameOf(v.X)
}
return ""
}
// callsForPath returns SDK calls for platform path like "/restmachine/cloudapi/compute/start"
func (idx *sdkIndex) callsForPath(path string) []sdkCall {
return idx.calls[strings.TrimPrefix(path, "/restmachine")]
}
// sentFields returns flattened list of parameters SDK puts on the wire for the call
func (idx *sdkIndex) sentFields(call sdkCall) ([]sentField, bool) {
if call.TypeName == "" {
return nil, true
}
expr, ok := idx.types[call.Dir][call.TypeName]
if !ok {
return nil, false
}
st, ok := expr.(*ast.StructType)
if !ok {
return nil, false
}
fields := idx.structFields(call.Dir, st, call.Transport, map[string]bool{call.TypeName: true})
// serialized wrapper field inherits `validate` of the skipped (url:"-") request field with the same json name
if call.Transport == transportForm {
skipped := idx.skippedFormFields(call.Dir, st, map[string]bool{call.TypeName: true})
for i := range fields {
if fields[i].Validate != "" {
continue
}
if validate, ok := skipped[strings.TrimSpace(fields[i].Name)]; ok {
fields[i].Validate = validate
}
}
}
return fields, true
}
// skippedFormFields returns json name -> validate tag of fields that are not sent in form requests (url:"-")
func (idx *sdkIndex) skippedFormFields(dir string, st *ast.StructType, visited map[string]bool) map[string]string {
result := make(map[string]string)
for _, field := range st.Fields.List {
var tag reflect.StructTag
if field.Tag != nil {
if s, err := strconv.Unquote(field.Tag.Value); err == nil {
tag = reflect.StructTag(s)
}
}
urlTag, hasURLTag := tag.Lookup("url")
if len(field.Names) == 0 && !hasURLTag {
name := typeNameOf(field.Type)
if embedded, ok := idx.types[dir][name].(*ast.StructType); ok && !visited[name] {
visited[name] = true
for k, v := range idx.skippedFormFields(dir, embedded, visited) {
result[k] = v
}
delete(visited, name)
}
continue
}
if urlTag != "-" {
continue
}
jsonName := strings.TrimSpace(strings.Split(tag.Get("json"), ",")[0])
if jsonName != "" && jsonName != "-" {
result[jsonName] = tag.Get("validate")
}
}
return result
}
func (idx *sdkIndex) structFields(dir string, st *ast.StructType, transport string, visited map[string]bool) []sentField {
var result []sentField
for _, field := range st.Fields.List {
var tag reflect.StructTag
if field.Tag != nil {
if s, err := strconv.Unquote(field.Tag.Value); err == nil {
tag = reflect.StructTag(s)
}
}
wireTag, hasWireTag := tag.Lookup(transportTagKey(transport))
// embedded struct without tag is flattened by both encoding/json and go-querystring
if len(field.Names) == 0 && !hasWireTag {
name := typeNameOf(field.Type)
if embedded, ok := idx.types[dir][name].(*ast.StructType); ok && !visited[name] {
visited[name] = true
result = append(result, idx.structFields(dir, embedded, transport, visited)...)
delete(visited, name)
}
continue
}
goNames := make([]string, 0, len(field.Names))
for _, n := range field.Names {
goNames = append(goNames, n.Name)
}
if len(goNames) == 0 {
goNames = append(goNames, typeNameOf(field.Type))
}
for _, goName := range goNames {
if !ast.IsExported(goName) {
continue
}
name, omitEmpty, skip := parseWireTag(wireTag, goName)
if skip {
continue
}
result = append(result, sentField{
Name: name,
GoName: goName,
Type: idx.resolveType(dir, field.Type, map[string]bool{}),
OmitEmpty: omitEmpty,
Validate: tag.Get("validate"),
})
}
}
return result
}
func transportTagKey(transport string) string {
if transport == transportJSON {
return "json"
}
return "url"
}
// parseWireTag returns wire name of the field, omitempty flag and whether the field is skipped (tag "-")
func parseWireTag(tag, goName string) (string, bool, bool) {
parts := strings.Split(tag, ",")
// keep tag name as is (including spaces) so that typos in tags are reported
name := parts[0]
if name == "-" && len(parts) == 1 {
return "", false, true
}
if name == "" {
name = goName
}
var omitEmpty bool
for _, opt := range parts[1:] {
if strings.TrimSpace(opt) == "omitempty" {
omitEmpty = true
}
}
return name, omitEmpty, false
}
var (
emptyInterfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
structPlaceholder = reflect.TypeOf(struct{}{})
basicTypes = map[string]reflect.Type{
"bool": reflect.TypeOf(false),
"string": reflect.TypeOf(""),
"int": reflect.TypeOf(int(0)),
"int8": reflect.TypeOf(int8(0)),
"int16": reflect.TypeOf(int16(0)),
"int32": reflect.TypeOf(int32(0)),
"int64": reflect.TypeOf(int64(0)),
"uint": reflect.TypeOf(uint(0)),
"uint8": reflect.TypeOf(uint8(0)),
"uint16": reflect.TypeOf(uint16(0)),
"uint32": reflect.TypeOf(uint32(0)),
"uint64": reflect.TypeOf(uint64(0)),
"float32": reflect.TypeOf(float32(0)),
"float64": reflect.TypeOf(float64(0)),
"any": emptyInterfaceType,
}
)
// resolveType converts type expression to reflect.Type for kind comparison (nil for types from other packages)
func (idx *sdkIndex) resolveType(dir string, e ast.Expr, visited map[string]bool) reflect.Type {
switch v := e.(type) {
case *ast.Ident:
if t, ok := basicTypes[v.Name]; ok {
return t
}
if visited[v.Name] {
return nil
}
underlying, ok := idx.types[dir][v.Name]
if !ok {
return nil
}
visited[v.Name] = true
return idx.resolveType(dir, underlying, visited)
case *ast.StarExpr:
return idx.resolveType(dir, v.X, visited)
case *ast.StructType:
return structPlaceholder
case *ast.InterfaceType:
return emptyInterfaceType
case *ast.ArrayType:
elem := idx.resolveType(dir, v.Elt, visited)
if elem == nil {
elem = emptyInterfaceType
}
return reflect.SliceOf(elem)
case *ast.MapType:
return reflect.MapOf(reflect.TypeOf(""), emptyInterfaceType)
}
return nil
}

View File

@@ -5,27 +5,72 @@ import (
"io" "io"
"os" "os"
"reflect" "reflect"
"sort"
"strings" "strings"
"testing" "testing"
) )
// extractTypeFromSchema extracts type from schema, handling oneOf cases // platformParam describes one request parameter from platform swagger
// When oneOf contains a type and null, it returns the non-null type type platformParam struct {
Name string
// OpenAPI type: integer, string, boolean, number, array, object or empty if unknown
Type string
// OpenAPI type of array items, empty if unknown
Items string
Required bool
// Value of "default" from swagger, nil if not set
Default interface{}
}
// extractTypeFromSchema extracts non-null type from "type" (string or array), "oneOf"/"anyOf" or "$ref" (object)
func extractTypeFromSchema(schema map[string]interface{}) string { func extractTypeFromSchema(schema map[string]interface{}) string {
// Check for direct type switch typ := schema["type"].(type) {
if paramType, ok := schema["type"].(string); ok { case string:
return paramType return typ
case []interface{}:
for _, item := range typ {
if s, ok := item.(string); ok && s != "null" {
return s
}
}
} }
// Check for oneOf for _, key := range []string{"oneOf", "anyOf"} {
if oneOf, ok := schema["oneOf"].([]interface{}); ok { variants, ok := schema[key].([]interface{})
for _, item := range oneOf { if !ok {
if itemMap, ok := item.(map[string]interface{}); ok { continue
if itemType, ok := itemMap["type"].(string); ok { }
// Skip null type, return the first non-null type found for _, variant := range variants {
if itemType != "null" { if variantMap, ok := variant.(map[string]interface{}); ok {
return itemType if variantType := extractTypeFromSchema(variantMap); variantType != "" && variantType != "null" {
} return variantType
}
}
}
}
if _, ok := schema["$ref"]; ok {
return "object"
}
return ""
}
// extractItemsType extracts type of array items from schema, including arrays declared inside oneOf/anyOf
func extractItemsType(schema map[string]interface{}) string {
if items, ok := schema["items"].(map[string]interface{}); ok {
return extractTypeFromSchema(items)
}
for _, key := range []string{"oneOf", "anyOf"} {
variants, ok := schema[key].([]interface{})
if !ok {
continue
}
for _, variant := range variants {
if variantMap, ok := variant.(map[string]interface{}); ok && extractTypeFromSchema(variantMap) == "array" {
if items := extractItemsType(variantMap); items != "" {
return items
} }
} }
} }
@@ -34,150 +79,84 @@ func extractTypeFromSchema(schema map[string]interface{}) string {
return "" return ""
} }
func getParameters(input map[string]interface{}) []interface{} { // getParameters returns request parameters of all methods, content types and query/path parameters of a path
var emptySlice []interface{} func getParameters(input map[string]interface{}) []platformParam {
methods := []string{"get", "post", "put", "delete", "patch", "head", "options"} params := make(map[string]platformParam)
add := func(name string, schema map[string]interface{}, required bool) {
var methodData interface{} p := platformParam{Name: name, Required: required}
found := false if schema != nil {
p.Type = extractTypeFromSchema(schema)
for _, method := range methods { if p.Type == "array" {
if data, ok := input[method]; ok { p.Items = extractItemsType(schema)
methodData = data }
found = true p.Default = schema["default"]
break
} }
if prev, ok := params[name]; ok {
p.Required = p.Required || prev.Required
if p.Type == "" {
p.Type, p.Items = prev.Type, prev.Items
}
if p.Default == nil {
p.Default = prev.Default
}
}
params[name] = p
} }
if !found { for _, method := range []string{"get", "post", "put", "delete", "patch", "head", "options"} {
return emptySlice operation, ok := input[method].(map[string]interface{})
} if !ok {
continue
}
parameters, ok := methodData.(map[string]interface{}) if requestBody, ok := operation["requestBody"].(map[string]interface{}); ok {
if !ok { content, _ := requestBody["content"].(map[string]interface{})
return emptySlice for _, media := range content {
} mediaMap, _ := media.(map[string]interface{})
schema, _ := mediaMap["schema"].(map[string]interface{})
var result []interface{} if schema == nil {
continue
if requestBody, ok := parameters["requestBody"].(map[string]interface{}); ok {
if content, ok := requestBody["content"].(map[string]interface{}); ok {
// Check for application/x-www-form-urlencoded, application/json, or multipart/form-data
var schemaData map[string]interface{}
var found bool
if formData, ok := content["application/x-www-form-urlencoded"].(map[string]interface{}); ok {
if schema, ok := formData["schema"].(map[string]interface{}); ok {
schemaData = schema
found = true
} }
} else if jsonData, ok := content["application/json"].(map[string]interface{}); ok { requiredFields := make(map[string]bool)
if schema, ok := jsonData["schema"].(map[string]interface{}); ok { if req, ok := schema["required"].([]interface{}); ok {
schemaData = schema for _, r := range req {
found = true if reqStr, ok := r.(string); ok {
requiredFields[reqStr] = true
}
}
} }
} else if multipartData, ok := content["multipart/form-data"].(map[string]interface{}); ok { properties, _ := schema["properties"].(map[string]interface{})
if schema, ok := multipartData["schema"].(map[string]interface{}); ok { for name, prop := range properties {
schemaData = schema propMap, _ := prop.(map[string]interface{})
found = true add(name, propMap, requiredFields[name])
} }
} }
}
if found && schemaData != nil { if parameters, ok := operation["parameters"].([]interface{}); ok {
if properties, ok := schemaData["properties"].(map[string]interface{}); ok { for _, p := range parameters {
requiredFields := make(map[string]bool) param, ok := p.(map[string]interface{})
if req, ok := schemaData["required"].([]interface{}); ok { if !ok {
for _, r := range req { continue
if reqStr, ok := r.(string); ok {
requiredFields[reqStr] = true
}
}
}
for name, prop := range properties {
propMap, ok := prop.(map[string]interface{})
if !ok {
continue
}
newParam := make(map[string]interface{})
newParam["name"] = name
propType := extractTypeFromSchema(propMap)
if propType != "" {
newParam["type"] = propType
}
newParam["required"] = requiredFields[name]
if propType == "array" {
// Try to get items from propMap first
if items, ok := propMap["items"].(map[string]interface{}); ok {
newParam["items"] = items
} else if oneOf, ok := propMap["oneOf"].([]interface{}); ok {
// If items not in propMap, try to get from oneOf array element
for _, item := range oneOf {
if itemMap, ok := item.(map[string]interface{}); ok {
if itemType, ok := itemMap["type"].(string); ok && itemType == "array" {
if items, ok := itemMap["items"].(map[string]interface{}); ok {
newParam["items"] = items
break
}
}
}
}
}
}
result = append(result, newParam)
}
} }
name, ok := param["name"].(string)
if !ok {
continue
}
schema, _ := param["schema"].(map[string]interface{})
required, _ := param["required"].(bool)
add(name, schema, required)
} }
} }
} }
if params, ok := parameters["parameters"].([]interface{}); ok { result := make([]platformParam, 0, len(params))
for _, p := range params { for _, p := range params {
param, ok := p.(map[string]interface{}) result = append(result, p)
if !ok {
continue
}
newParam := make(map[string]interface{})
if name, ok := param["name"].(string); ok {
newParam["name"] = name
}
if schema, ok := param["schema"].(map[string]interface{}); ok {
paramType := extractTypeFromSchema(schema)
if paramType != "" {
newParam["type"] = paramType
}
if paramType == "array" {
if items, ok := schema["items"].(map[string]interface{}); ok {
newParam["items"] = items
} else if oneOf, ok := schema["oneOf"].([]interface{}); ok {
for _, item := range oneOf {
if itemMap, ok := item.(map[string]interface{}); ok {
if itemType, ok := itemMap["type"].(string); ok && itemType == "array" {
if items, ok := itemMap["items"].(map[string]interface{}); ok {
newParam["items"] = items
break
}
}
}
}
}
}
}
// Handle required field
if required, ok := param["required"].(bool); ok {
newParam["required"] = required
} else {
newParam["required"] = false
}
result = append(result, newParam)
}
} }
sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name })
if len(result) > 0 { return result
return result
}
return emptySlice
} }
func getBytesFromJSON(fileName string, t *testing.T) []byte { func getBytesFromJSON(fileName string, t *testing.T) []byte {
@@ -195,6 +174,7 @@ func getBytesFromJSON(fileName string, t *testing.T) []byte {
return bytes return bytes
} }
// getErrorsFromJSON compares platform requests from swagger with the values SDK passes to DecortApiCall*
func getErrorsFromJSON(bytes []byte, t *testing.T, cloud string) { func getErrorsFromJSON(bytes []byte, t *testing.T, cloud string) {
var requests map[string]interface{} var requests map[string]interface{}
@@ -209,227 +189,272 @@ func getErrorsFromJSON(bytes []byte, t *testing.T, cloud string) {
t.Fatalf("Wrong cloud provided, expected `cloudapi`, `cloudbroker` or `sdn`, got %s", cloud) t.Fatalf("Wrong cloud provided, expected `cloudapi`, `cloudbroker` or `sdn`, got %s", cloud)
} }
var dataLogs []string index, err := getSDKIndex()
if err != nil {
t.Fatalf("can not parse SDK sources: %v", err)
}
paths, err := getMapFromFile(bytes) paths, err := getMapFromFile(bytes)
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
} }
i := 0
for k, v := range paths { requestPaths := make([]string, 0, len(requests))
for k := range requests {
requestPaths = append(requestPaths, k)
}
sort.Strings(requestPaths)
var absentOnPlatform, invalidKeys []string
for _, k := range requestPaths {
if !strings.HasPrefix(k, "/restmachine/"+cloud+"/") {
invalidKeys = append(invalidKeys, k)
continue
}
// exclude deprecated urls from analysis // exclude deprecated urls from analysis
if !validateUrlFromJson(k) { if !validateUrlFromJson(k) {
continue continue
} }
v, ok := paths[k]
params := getParameters(v.(map[string]interface{}))
structure, ok := requests[k]
if !ok { if !ok {
absentOnPlatform = append(absentOnPlatform, k)
continue continue
} }
i++
typStruct := reflect.TypeOf(structure)
var errs []string
// empty request case
if len(params) == 0 && structure == nil {
continue
}
if len(params) != typStruct.NumField() {
errs = append(errs, fmt.Sprintf("Platform (%d) and golang structure (%d) have different amount of fields.", len(params), typStruct.NumField()))
}
paramMap := make(map[string]bool)
paramRequiredMap := make(map[string]bool)
for _, p := range params {
param, ok := p.(map[string]interface{})
if !ok {
continue
}
name, ok := param["name"].(string)
if ok {
paramMap[name] = true
required, ok := param["required"].(bool)
if ok {
paramRequiredMap[name] = required
} else {
paramRequiredMap[name] = false
}
}
}
for _, p := range params {
param, ok := p.(map[string]interface{})
if !ok {
continue
}
name, ok := param["name"].(string)
if !ok {
name = ""
}
required, ok := param["required"].(bool)
if !ok {
required = false
}
typ, ok := p.(map[string]interface{})["type"].(string)
if !ok {
typ = ""
}
var items string
if p.(map[string]interface{})["items"] != nil {
itemsTemp := p.(map[string]interface{})["items"]
if itemsTemp != nil {
itemsType := itemsTemp.(map[string]interface{})["type"]
if itemsType != nil {
items, ok = itemsType.(string)
if !ok {
items = ""
}
}
}
}
var found bool
for i := 0; i < typStruct.NumField(); i++ {
jsonTag := typStruct.Field(i).Tag.Get("json")
validation, _ := typStruct.Field(i).Tag.Lookup("validate")
if checkName(name, jsonTag) {
if !checkRequired(required, validation, typ) {
errs = append(errs, fmt.Sprintf("Field %s has different required parameters on the platform and in golang structure", name))
}
if !checkKind(typ, items, typStruct.Field(i).Type) {
errs = append(errs, fmt.Sprintf("Field %s has different type parameters on the platform and in golang structure", name))
}
found = true
break
}
}
if !found {
errs = append(errs, fmt.Sprintf("Platform has field %s that golang structure doesn't", name))
}
}
// Check if required fields in Go structure are missing from platform JSON
// or if they exist but are not required on platform
for i := 0; i < typStruct.NumField(); i++ {
jsonTag := typStruct.Field(i).Tag.Get("json")
validation, _ := typStruct.Field(i).Tag.Lookup("validate")
fieldName := strings.Split(jsonTag, ",")[0]
if fieldName == "" || fieldName == "-" {
continue
}
if strings.Contains(validation, "required") {
if !paramMap[fieldName] {
errs = append(errs, fmt.Sprintf("Golang structure has required field %s that platform doesn't", fieldName))
} else {
platformRequired := paramRequiredMap[fieldName]
if !platformRequired {
fieldType := typStruct.Field(i).Type
if fieldType.Kind() == reflect.Bool {
errs = append(errs, fmt.Sprintf("Golang structure has required field %s that platform doesn't", fieldName))
}
}
}
}
}
errs := checkPath(k, getParameters(v.(map[string]interface{})), index)
if len(errs) > 0 { if len(errs) > 0 {
msg := fmt.Sprintf("Path %s has following errors: %v", k, errs) t.Errorf("Path %s has following errors: %v", k, errs)
t.Error(msg)
dataLogs = append(dataLogs, msg)
} }
} }
if len(requests) != i { if len(invalidKeys) > 0 {
msg := fmt.Sprintf("Amount of structure checked (%d) is not the same as amount of platform requests available (%d), please check getRequests func in code.", t.Errorf("Request map for %s has keys that don't start with /restmachine/%s/ and are never checked: %v", cloud, cloud, invalidKeys)
i, len(requests)) }
t.Error(msg) if len(absentOnPlatform) > 0 {
dataLogs = append(dataLogs, msg) t.Errorf("Request map has paths that platform doesn't (remove them from request map): %v", absentOnPlatform)
} }
} }
// checkName checks if name field from platform has the same value as json tag in golang structure (maybe including omitempty) // requestVariant is one distinct request SDK sends to a path (e.g. sync and async methods send different wrappers)
func checkName(name, tag string) bool { type requestVariant struct {
return strings.Contains(tag, name) FuncName string
Transport string
Fields []sentField
} }
// checkRequired checks if required field from platform has the same value as validate tag in golang structure // checkPath compares platform parameters of a path with every distinct request SDK sends to this path
func checkRequired(required bool, validation, fieldType string) bool { func checkPath(path string, params []platformParam, index *sdkIndex) []string {
if required && strings.Contains(validation, "required") { calls := index.callsForPath(path)
return true if len(calls) == 0 {
} return []string{"SDK call for this path is not found in sources, can not check it"}
if required && (!strings.Contains(validation, "omitempty") && validation != "") {
return true
}
if !required && (validation == "" || strings.Contains(validation, "omitempty")) {
return true
} }
if fieldType == "boolean" { type variantKey struct{ dir, typeName, transport string }
return true // otherwise we have issues with setting false/true seen := make(map[variantKey]bool)
var variants []requestVariant
var errs []string
for _, call := range calls {
key := variantKey{call.Dir, call.TypeName, call.Transport}
if seen[key] {
continue
}
seen[key] = true
fields, ok := index.sentFields(call)
if !ok {
errs = append(errs, fmt.Sprintf("can not resolve type %s sent by %s", call.TypeName, call.FuncName))
continue
}
variants = append(variants, requestVariant{FuncName: call.FuncName, Transport: call.Transport, Fields: fields})
} }
return append(errs, compareVariants(params, variants)...)
}
// compareVariants returns differences between platform parameters and all requests SDK sends to the path
func compareVariants(params []platformParam, variants []requestVariant) []string {
var errs []string
seenErr := make(map[string]bool)
add := func(msg string) {
if !seenErr[msg] {
seenErr[msg] = true
errs = append(errs, msg)
}
}
byName := make([]map[string]sentField, len(variants))
for i, v := range variants {
byName[i] = make(map[string]sentField, len(v.Fields))
for _, f := range v.Fields {
if _, ok := byName[i][f.Name]; ok && v.Transport == transportForm {
add(fmt.Sprintf("Golang structure sends field %s more than once", f.Name))
}
byName[i][f.Name] = f
}
}
paramsByName := make(map[string]bool, len(params))
for _, p := range params {
paramsByName[p.Name] = true
var sentBy, notSentBy []string
for i, v := range variants {
f, ok := byName[i][p.Name]
if !ok {
notSentBy = append(notSentBy, v.FuncName)
continue
}
sentBy = append(sentBy, v.FuncName)
if !checkKind(p.Type, p.Items, f, v.Transport) {
add(fmt.Sprintf("Field %s has different type parameters on the platform (%s) and in golang structure (%s)",
p.Name, platformTypeString(p), f.Type))
}
if msg := checkRequired(p, f); msg != "" {
add(msg)
}
}
if len(sentBy) == 0 {
add(fmt.Sprintf("Platform has field %s that golang structure doesn't", p.Name))
continue
}
for _, fn := range notSentBy {
if p.Required {
add(fmt.Sprintf("Required field %s is not sent by %s", p.Name, fn))
} else if p.Type == "boolean" && p.Default == true {
add(fmt.Sprintf("Field %s is not sent by %s, platform default is true", p.Name, fn))
}
}
}
for _, v := range variants {
for _, f := range v.Fields {
if !paramsByName[f.Name] {
add(fmt.Sprintf("Golang structure has field %q (%s) that platform doesn't", f.Name, f.GoName))
}
}
}
return errs
}
func platformTypeString(p platformParam) string {
if p.Type == "array" && p.Items != "" {
return "array of " + p.Items
}
return p.Type
}
// hasValidateTag checks if `validate` tag contains exactly the given tag
func hasValidateTag(validate, tag string) bool {
for _, v := range strings.Split(validate, ",") {
if strings.TrimSpace(v) == tag {
return true
}
}
return false return false
} }
// checkKind checks if type field from platform has the same value as field type in golang structure func isIntKind(k reflect.Kind) bool {
func checkKind(platformType, items string, typ reflect.Type) bool { switch k {
//nolint case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
switch typ.Kind() { reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Uint, reflect.Uint64, reflect.Uint16, reflect.Uint32, reflect.Uint8: return true
if platformType == "integer" { }
return true return false
} }
return false
case reflect.String: // checkRequired returns description of required mismatch between platform and golang field or empty string
func checkRequired(p platformParam, f sentField) string {
// validators without omitempty fail on zero value, so such field can not be omitted either
goRequired := hasValidateTag(f.Validate, "required") ||
(f.Validate != "" && !hasValidateTag(f.Validate, "omitempty"))
// numbers and booleans without omitempty are always sent and zero is a meaningful value for them
alwaysSent := false
if f.Type != nil && !f.OmitEmpty {
kind := f.Type.Kind()
alwaysSent = isIntKind(kind) || kind == reflect.Bool || kind == reflect.Float32 || kind == reflect.Float64
}
if p.Required && !goRequired && !alwaysSent {
return fmt.Sprintf("Field %s is required on the platform but is optional in golang structure", p.Name)
}
if !p.Required && hasValidateTag(f.Validate, "required") {
return fmt.Sprintf("Field %s is optional on the platform but is required in golang structure", p.Name)
}
return ""
}
// checkKind checks if type field from platform corresponds to field type in golang structure
func checkKind(platformType, items string, f sentField, transport string) bool {
typ := f.Type
// type can not be resolved on one of the sides
if typ == nil || platformType == "" {
return true
}
for typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
kind := typ.Kind()
switch {
case isIntKind(kind):
return platformType == "integer"
case kind == reflect.String:
if platformType == "string" { if platformType == "string" {
return true return true
} }
return false // json-encoded object or array of objects
return transport == transportForm &&
(platformType == "object" || (platformType == "array" && items == "object"))
case reflect.Bool: case kind == reflect.Bool:
if platformType == "boolean" { return platformType == "boolean"
return true
case kind == reflect.Float32 || kind == reflect.Float64:
return platformType == "number"
case kind == reflect.Interface:
if hasValidateTag(f.Validate, "isBool") {
return platformType == "boolean"
} }
return false return true
case reflect.Float32, reflect.Float64: case kind == reflect.Map || kind == reflect.Struct:
if platformType == "number" { return platformType == "object"
return true
}
return false
case reflect.Array, reflect.Slice: case kind == reflect.Array || kind == reflect.Slice:
if platformType != "array" { if platformType != "array" {
return false return false
} }
elem := typ.Elem() elem := typ.Elem()
switch elem.Kind() { for elem.Kind() == reflect.Ptr {
case reflect.String: elem = elem.Elem()
if items == "string" { }
return true // items type is not declared on the platform
} if items == "" {
return false return true
}
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Uint, reflect.Uint64, reflect.Uint16, reflect.Uint32, reflect.Uint8: switch {
if items == "integer" { case elem.Kind() == reflect.String:
return true // list of json-encoded objects
} return items == "string" || (transport == transportForm && items == "object")
return false case isIntKind(elem.Kind()):
return items == "integer"
default: // for cases like dataDisks etc. case elem.Kind() == reflect.Bool:
return true return items == "boolean"
case elem.Kind() == reflect.Float32 || elem.Kind() == reflect.Float64:
return items == "number"
case elem.Kind() == reflect.Struct || elem.Kind() == reflect.Map:
return items == "object"
default:
return true
} }
default: // for cases like drivers etc
return true
} }
return true
} }