This commit is contained in:
2025-09-19 13:45:58 +03:00
parent abd35f858c
commit befff7acd9
28 changed files with 900 additions and 82 deletions

View File

@@ -0,0 +1,81 @@
package audit
// FilterByID returns ListAudits with specified ID.
func (la ListAudits) FilterByID(guid string) ListAudits {
predicate := func(ia ItemAudit) bool {
return ia.GUID == guid
}
return la.FilterFunc(predicate)
}
// FilterByCall returns ListAudits with specified call.
func (la ListAudits) FilterByCall(call string) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.Call == call
}
return la.FilterFunc(predicate)
}
// FilterByCorrelationID returns ListAudits with specified correlation id.
func (la ListAudits) FilterByCorrelationID(correlationID string) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.CorrelationID == correlationID
}
return la.FilterFunc(predicate)
}
// FilterByRemoteAddr returns ListAudits with specified remote address.
func (la ListAudits) FilterByRemoteAddr(remoteAddr string) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.RemoteAddr == remoteAddr
}
return la.FilterFunc(predicate)
}
// FilterByUser returns ListAudits with specified user name.
func (la ListAudits) FilterByUser(user string) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.User == user
}
return la.FilterFunc(predicate)
}
// FilterByStatusCode return ListAudits with specified status code.
func (la ListAudits) FilterByStatusCode(statusCode uint64) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.StatusCode == statusCode
}
return la.FilterFunc(predicate)
}
// FilterFunc allows filtering ListAudits based on a user-specified predicate.
func (la ListAudits) FilterFunc(predicate func(ItemAudit) bool) ListAudits {
var result ListAudits
for _, item := range la.Data {
if predicate(item) {
result.Data = append(result.Data, item)
}
}
result.EntryCount = uint64(len(result.Data))
return result
}
// FindOne returns first found ItemAudit
// If none was found, returns an empty struct.
func (la ListAudits) FindOne() ItemAudit {
if len(la.Data) == 0 {
return ItemAudit{}
}
return la.Data[0]
}

View File

@@ -0,0 +1,115 @@
package audit
import (
"testing"
)
var audits = ListAudits{
Data: []ItemAudit{
{
Args: "[]",
Call: "/restmachine/cloudapi/audit/linkedJobs",
GUID: "550e8400-e29b-41d4-a716-446655440001",
CorrelationID: "550e8400-e29b-41d4-a716-446655440001",
Kwargs: `{\"audit_guid\":\"dd8623a1-a887-48c1-a500-c10210d404cf\"}`,
RemoteAddr: "192.168.1.100",
ResponseTime: 1,
Result: `[]`,
StatusCode: 200,
Timestamp: 1640995200,
TimestampEnd: 1640995201,
User: "test@example.com",
TTL: "2025-07-31T14:22:57.028000",
},
{
Args: "[]",
Call: "/restmachine/cloudapi/audit/test",
GUID: "550e8400-e29b-41d4-a716-446655440002",
CorrelationID: "550e8400-e29b-41d4-a716-446655440002",
Kwargs: `{\"audit_guid\":\"dd8623a1-a887-48c1-a500-c10210d404cf\"}`,
RemoteAddr: "192.168.1.105",
ResponseTime: 5,
Result: `[]`,
StatusCode: 400,
Timestamp: 1640995200,
TimestampEnd: 1640995201,
User: "test2@example.com",
TTL: "2025-07-31T14:22:57.028000",
},
},
EntryCount: 2,
}
func TestFilterByID(t *testing.T) {
actual := audits.FilterByID("550e8400-e29b-41d4-a716-446655440002").FindOne()
if actual.GUID != "550e8400-e29b-41d4-a716-446655440002" {
t.Fatal("expected GUID 550e8400-e29b-41d4-a716-446655440002, found: ", actual.GUID)
}
actualEmpty := audits.FilterByID("")
if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
}
}
func TestFilterByCorrelationID(t *testing.T) {
actual := audits.FilterByCorrelationID("550e8400-e29b-41d4-a716-446655440002").FindOne()
if actual.CorrelationID != "550e8400-e29b-41d4-a716-446655440002" {
t.Fatal("expected GUID 550e8400-e29b-41d4-a716-446655440002, found: ", actual.CorrelationID)
}
actualEmpty := audits.FilterByCorrelationID("")
if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
}
}
func TestFilterByRemoteAddr(t *testing.T) {
actual := audits.FilterByRemoteAddr("192.168.1.100").FindOne()
if actual.RemoteAddr != "192.168.1.100" {
t.Fatal("expected remote address 192.168.1.100, found: ", actual.RemoteAddr)
}
actualEmpty := audits.FilterByRemoteAddr("")
if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
}
}
func TestFilterByUser(t *testing.T) {
actual := audits.FilterByUser("test@example.com").FindOne()
if actual.User != "test@example.com" {
t.Fatal("expected user test@example.com, found: ", actual.RemoteAddr)
}
actualEmpty := audits.FilterByUser("")
if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
}
}
func TestFilterByCall(t *testing.T) {
actual := audits.FilterByCall("/restmachine/cloudapi/audit/test").FindOne()
if actual.Call != "/restmachine/cloudapi/audit/test" {
t.Fatal("expected call /restmachine/cloudapi/audit/test, found: ", actual.Call)
}
}
func TestFilterByStatusCode(t *testing.T) {
actual := audits.FilterByStatusCode(200)
for _, item := range actual.Data {
if item.StatusCode != 200 {
t.Fatal("expected 200 status code, found: ", item.StatusCode)
}
}
}

124
pkg/cloudapi/audit/list.go Normal file
View File

@@ -0,0 +1,124 @@
package audit
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ListRequest struct to give list of account audits
type ListRequest struct {
// Find all audits after point in time (unixtime)
// Required: false
TimestampAt uint64 `url:"timestamp_at,omitempty" json:"timestamp_at,omitempty"`
// Find all audits before point in time (unixtime)
// Required: false
TimestampTo uint64 `url:"timestamp_to,omitempty" json:"timestamp_to,omitempty"`
// Find by user (Mongo RegExp supported)
// Required: false
User string `url:"user,omitempty" json:"user,omitempty"`
// Find by api endpoint (Mongo RegExp supported)
// Required: false
Call string `url:"call,omitempty" json:"call,omitempty"`
// Find by request id
// Required: false
RequestID string `url:"request_id,omitempty" json:"request_id,omitempty"`
// Find by HTTP min status code
// Required: false
MinStatusCode uint64 `url:"min_status_code,omitempty" json:"min_status_code,omitempty"`
// Find by HTTP max status code
// Required: false
MaxStatusCode uint64 `url:"max_status_code,omitempty" json:"max_status_code,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty" validate:"omitempty,sortBy"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
// Find by resource group id
// Required: false
RGID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"`
// Find by compute id
// Required: false
ComputeID uint64 `url:"compute_id,omitempty" json:"compute_id,omitempty"`
// Find by account id
// Required: false
AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"`
// Find by vins id
// Required: false
VINSID uint64 `url:"vins_id,omitempty" json:"vins_id,omitempty"`
// Find by service id
// Required: false
ServiceID uint64 `url:"service_id,omitempty" json:"service_id,omitempty"`
// Find by k8s id
// Required: false
K8SID uint64 `url:"k8s_id,omitempty" json:"k8s_id,omitempty"`
// Find by flipgroup id
// Required: false
FLIPGroupID uint64 `url:"flipgroup_id,omitempty" json:"flipgroup_id,omitempty"`
// Find by load balancer id
// Required: false
LBID uint64 `url:"lb_id,omitempty" json:"lb_id,omitempty"`
// Find by sep id
// Required: false
SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"`
// Exclude audit lines from response
// Required: false
ExcludeAuditLines bool `url:"exclude_audit_lines,omitempty" json:"exclude_audit_lines,omitempty"`
}
// List gets audit records for the specified account object
func (a Audit) List(ctx context.Context, req ListRequest) (*ListAudits, error) {
res, err := a.ListRaw(ctx, req)
if err != nil {
return nil, err
}
list := ListAudits{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}
// ListRaw gets list of audit records an array of bytes
func (a Audit) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/audit/list"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -42,3 +42,54 @@ type RecordAudit struct {
// User
User string `json:"user"`
}
// Main info about audit
type ItemAudit struct {
// Args
Args string `json:"args"`
// Call
Call string `json:"call"`
// GUID
GUID string `json:"guid"`
// Correlation ID
CorrelationID string `json:"correlation_id"`
// Kwargs
Kwargs string `json:"kwargs"`
// RemoteAddr
RemoteAddr string `json:"remote_addr"`
// Response time
ResponseTime float64 `json:"responsetime"`
// Result
Result string `json:"result"`
// Status code
StatusCode uint64 `json:"statuscode"`
// Timestamp
Timestamp float64 `json:"timestamp"`
// Timestamp End
TimestampEnd float64 `json:"timestampEnd"`
// User
User string `json:"user"`
// TTL
TTL string `json:"_ttl"`
}
// List of audits
type ListAudits struct {
// Data
Data []ItemAudit `json:"data"`
// EntryCount
EntryCount uint64 `json:"entryCount"`
}

View File

@@ -50,6 +50,14 @@ type DiskAddRequest struct {
// Specify image id for create disk from template
// Required: false
ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"`
// Desired PCI slot (hex string, e.g. "0x1A")
// Required: false
PCISlot string `url:"pci_slot,omitempty" json:"pci_slot,omitempty"`
// Desired bus number (hex string, e.g. "0x03")
// Required: false
BusNumber string `url:"bus_number,omitempty" json:"bus_number,omitempty"`
}
// DiskAdd creates new disk and attach to compute

View File

@@ -21,6 +21,14 @@ type DiskAttachRequest struct {
// Type of the disk B;D
// Required: false
DiskType string `url:"diskType,omitempty" json:"diskType,omitempty" validate:"omitempty,computeDiskType"`
// Desired PCI slot (hex string, e.g. "0x1A")
// Required: false
PCISlot string `url:"pci_slot,omitempty" json:"pci_slot,omitempty"`
// Desired bus number (hex string, e.g. "0x03")
// Required: false
BusNumber string `url:"bus_number,omitempty" json:"bus_number,omitempty"`
}
// DiskAttach attach disk to compute

View File

@@ -392,6 +392,9 @@ type RecordCompute struct {
// List interfaces
Interfaces ListInterfaces `json:"interfaces"`
// Loader meta iso information
LoaderMetaIso LoaderMetaIso `json:"loaderMetaIso"`
// Live migration job ID
LiveMigrationJobID uint64 `json:"live_migration_job_id"`
@@ -519,6 +522,13 @@ type RecordCompute struct {
// Required: false
ZoneID uint64 `json:"zoneId"`
}
type LoaderMetaIso struct {
// Name
DeviceName string `json:"devicename"`
// Path
Path string `json:"path"`
}
type QemuQuest struct {
Enabled bool `json:"enabled"`
@@ -758,9 +768,18 @@ type ItemComputeDisk struct {
// Bus number
BusNumber uint64 `json:"bus_number"`
// Created by
CreatedBy string `json:"createdBy"`
// Created time
CreatedTime uint64 `json:"createdTime"`
// Device name
DeviceName string `json:"devicename"`
// Deleted by
DeletedBy string `json:"deletedBy"`
// Deleted time
DeletedTime uint64 `json:"deletedTime"`
@@ -871,6 +890,9 @@ type ItemComputeDisk struct {
// Updated by
UpdatedBy string `json:"updatedBy"`
// UpdatedTime
UpdatedTime uint64 `json:"updatedTime"`
}
type ItemReplication struct {

View File

@@ -30,10 +30,6 @@ type CreateRequest struct {
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
// Max IOPS disk can perform defaults to 2000
// Required: false
IOPS uint64 `url:"iops,omitempty" json:"iops,omitempty"`
// Storage endpoint provider ID to create disk
// Required: false
SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"`

View File

@@ -69,6 +69,10 @@ type ListRequest struct {
// Find by enabled True or False
// Required: false
Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"`
// Find by storage policy id
// Required: false
StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,omitempty"`
}
// List gets list of available images as a ListImages struct, optionally filtering by account ID

View File

@@ -50,6 +50,9 @@ type ItemImage struct {
// Status
Status string `json:"status"`
// Storage policy ID
StoragePolicyID uint64 `json:"storage_policy_id"`
// Type
Type string `json:"type"`

View File

@@ -9,10 +9,6 @@ import (
)
type ListRequest struct {
// ID of account ID
// Required: true
AccountID uint64 `url:"account_id" json:"account_id" validate:"required"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -45,6 +41,10 @@ type ListRequest struct {
// Required: false
SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"`
// ID of account ID
// Required: false
AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"`
// Search by resgroup id
// Required: false
ResgroupID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"`

View File

@@ -211,6 +211,9 @@ type RecordVNFDev struct {
// VNC password
VNCPassword string `json:"vncPasswd"`
// Zone ID
ZoneID uint64 `json:"zoneId"`
}
// VNF config
@@ -269,6 +272,9 @@ type ItemVNFInterface struct {
// Enabled
Enabled bool `json:"enabled"`
// Enable security groups
EnableSecGroups bool `json:"enable_secgroups"`
// FLIPGroup ID
FLIPGroupID uint64 `json:"flipgroupId"`
@@ -305,6 +311,9 @@ type ItemVNFInterface struct {
// NodeID
NodeID int64 `json:"nodeId"`
// List of security groups
SecGroups []uint64 `json:"security_groups"`
// SDNInterfaceID
SDNInterfaceID string `json:"sdn_interface_id"`
@@ -418,6 +427,9 @@ type RecordNAT struct {
// Type
Type string `json:"type"`
// Zone ID
ZoneID uint64 `json:"zoneId"`
}
// NAT configuration
@@ -486,6 +498,9 @@ type RecordGW struct {
// Type
Type string `json:"type"`
// Zone ID
ZoneID uint64 `json:"zoneId"`
}
// GW configuration
@@ -576,6 +591,9 @@ type RecordDHCP struct {
// Type
Type string `json:"type"`
// Zone ID
ZoneID uint64 `json:"zoneId"`
}
// DHCP configuration