commit a25a3c2e5c26af6077bd2c502ed1bcb06151e621 Author: asteam Date: Tue Oct 1 11:15:36 2024 +0300 v1.0.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..42aadc1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +cmd/ +.idea/ +.vscode/ +.fleet/ +.DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b754401 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +## Version 1.0.0 + +### Feature + +#### cluster: +- Add endpoint List +- Add endpoint ListVNetworks + +#### extstorage: +- Add endpoint List + +#### folder +- Add endpoint List + +#### image: +- Add endpoint List + +#### node +- Add endpoint List + +#### respool: +- Add endpoint List + +#### template: +- Add endpoint List +- Add endpoint Get + +#### vm: +- Add endpoint List +- Add enpdoint Get +- Add endpoint Create +- Add endpoint GetGetDisks +- Add endpoint PowerOff +- Add endpoint PowerOn \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..647d9fa --- /dev/null +++ b/README.md @@ -0,0 +1,431 @@ +# Dynamix standart SDK + +Dynamix standart SDK - это библиотека, написанная на языке GO, позволяющая взаимодействовать с API облачной платформы **Vcontrol**. Библиотека содержит в себе структуры и методы, необходимые для отправки запросов. Dynamix standart SDK имеет встроенный http-клиент и поддерживает разные способы авторизации на платформе. Библиотека так же содержит в себе модели ответов от платформы. + +## Оглавление + +- [Dynamix standart SDK](#dynamix-standart-sdk) + - [Оглавление](#оглавление) + - [Установка](#установка) + - [Список API](#список-api) + - [API](#api) + - [Работа с библиотекой](#работа-с-библиотекой) + - [Настройка конфигурации клиента](#настройка-конфигурации-клиента) + - [Пример конфигурации клиента](#пример-конфигурации-клиента) + - [Парсинг конфигурации из файла](#парсинг-конфигурации-из-файла) + - [Пример JSON конфигурации](#пример-json-конфигурации) + - [Пример YAML конфигурации](#пример-yaml-конфигурации) + - [Создание клиента](#создание-клиента) + - [Пример](#пример) + - [Создание структуры запроса](#создание-структуры-запроса) + - [Пример комментариев структуры](#пример-комментариев-структуры) + - [Пример создания запроса для развертывания виртуальной машины:](#пример-создания-запроса-для-развертывания-виртуальной-машины) + - [Выполнение запроса](#выполнение-запроса) + - [Пример выполнения запроса](#пример-выполнения-запроса) + - [Пример выполнения GetRaw и ListRaw запросов](#пример-выполнения-getraw-и-listraw-запросов) + + +## Установка + +Выполните команду в терминале: + +```bash +go get -u repository.basistech.ru/BASIS/dynamix-standart-go-sdk +``` + +## Список API + +Библиотека поддерживает одну группу API платформы: + +- `api` - группа, которая позволяет воспользоваться всем стандартным функционалом платформы; + +### API + +Данная группа ручек позволяет выполнять следующие операции в платформе: + +- `Cluster` - управление списками кластеров +- `Extstorage` - управление хранилищами для дисков ВМ; +- `Folder` - управление списками логических папок; +- `Image` - управление списками образов ВМ; +- `Node` - управление списками хостов; +- `Respool` - управление списками пулов ресурcов; +- `Tempate` - управление шаблонами виртуальных машин; +- `VM` - управление виртуальными машинами; + +## Работа с библиотекой + +Алгоритм работы с библиотекой выглядит следующим образом: + +1. Выполнение одного из действий: +- настройка конфигурации клиента; +- парсинг конфигурации из файла. +2. Создание клиента. +3. Создание структуры запроса. +4. Выполнение запроса. + +### Настройка конфигурации клиента + +Сначала, необходимо создать переменную конфигурации клиента. Конфигурация состоит как из обязательных, так и необязательных полей. +| Поле | Тип | Обязательный | Описание | +| --- | --- | --- | --- | +| Username | string | Да | username пользователя для выполнения запроса | +| Password | string | Да | password пользователя для выполнения запроса | +| VControlURL | string | Да | URL адрес платформы, с которой будет осуществляться взаимодействие | +| Retries | uint | Нет | Кол-во неудачных попыток выполнения запроса, по умолчанию - 5 | +| Timeout | config.Duration | Нет | Таймаут HTTP клиента, по умолчанию - без ограничений | +| SSLSkipVerify | bool | Нет | Пропуск проверки подлинности сертификата | +| Token | string | Нет | JWT токен | + +##### Пример конфигурации клиента + +```go +import ( +"repository.basistech.ru/BASIS/dynamix-standart-go-sdk/config" +) + +func main(){ +// Настройка конфигурации +cfg := config.Config{ + Username: "", + Password: "", + VControlURL: "", + Retries: 5, + SSLSkipVerify: true, +} + +cfg.SetTimeout(5 * time.Minute) +} +``` + +#### Парсинг конфигурации из файла + +Также возможно создать переменную конфигурации из JSON или YAML файла, используя функцию `ParseConfigJSON` (или `ParseConfigYAML`) из пакета config. +
+*См. пример файлов конфигурации ниже и в директории `samples/`.* + +```go +import ( +"repository.basistech.ru/BASIS/dynamix-standart-go-sdk/config" +) + +func main() { +// Парсинг конфигурации из JSON-файла +cfg, _ := config.ParseConfigJSON("") +} +``` + +#### Пример JSON конфигурации + +```json +{ + "username": "", + "password": "", + "vControlURL": "", + "retries": 5, + "timeout": "5m", + "sslSkipVerify": false +} +``` + +#### Пример YAML конфигурации + +```yaml +username: +password: +vControlURL: +retries: 5 +timeout: 5m +sslSkipVerify: false +``` + +### Создание клиента + +Создание клиента происходит с помощью функции-строителя `New` из основного пакета `dynamix-standart-go-sdk`, для избежания проблем с именами, пакету можно присвоить алиас `vcontrol`. Функция принимает конфигурацию, возвращает структуру `VControlClient`, с помощью которой можно взаимодействовать с платформой. + +### Пример + +```go +package main + +import ( + vcontrol "repository.basistech.ru/BASIS/dynamix-standart-go-sdk" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/config" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + Username: "", + Password: "", + VControlURL: "", + Retries: 5, + } + + cfg.SetTimeout(5*time.Minute) + + // Создание клиента + client := vcontrol.New(cfg) +} +``` + +### Создание структуры запроса + +Структуры запросов определены в пакете `pkg/`: + +В пакете находятся пакеты групп API: + +- **api**: + - `pkg/cluster` - для `Cluster` + - `pkg/extstorage` - для `External storage` + - `pkg/folder` - для `Folrer` + - `pkg/image` - для `Image` + - `pkg/node` - для `Node` + - `pkg/respool` - для `Resourse pool` + - `pkg/tempalte` - для `Template` + - `pkg/vm` - для `VM` + +В каждом пакете находится пакет `requests` с структурами запросов + +Все поля структуры имеют описание, в которых содержится: + +- Их назначение; +- Обязательный или нет - поле required в комментариях; +- Доп. информация (допустимые значения, значения по умолчанию). + +#### Пример комментариев структуры +```go +// ListImageRequest struct to get list of images +type ListImageRequest struct { + // Name of image + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Size of image + // Required: false + Size string `url:"size,omitempty" json:"size,omitempty"` + + // Type of image + // Required: false + ImageType string `url:"image_type,omitempty" json:"image_type,omitempty" validate:"omitempty,image_type"` + + // Status of image + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty" validate:"omitempty,image_status"` + + // ShareID of image + // Required: false + ShareID string `url:"share_id,omitempty" json:"share_id,omitempty"` + + // Active if True Image is active + // Required: false + Active interface{} `url:"active,omitempty" json:"active,omitempty" validate:"omitempty,is_bool"` + + // IsTemplateImage If True Image is template + // Required: false + // Default: False + IsTemplateImage bool `url:"is_template_image,omitempty" json:"is_template_image,omitempty"` + + // Mark of image + // Required: false + Mark string `url:"mark,omitempty" json:"mark,omitempty"` + + // image for filtering by text field + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // List columns which will be used by filter_text + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // List of columns for sorting. + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Visibility status of the interface (visible, all, deleted). + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` + + // Filter VM Images that are unavailable now, because share is unavailable + // Required: false + HideUnavailable interface{} `url:"hide_unavailable,omitempty" json:"hide_unavailable,omitempty" validate:"omitempty,is_bool"` + + // Number of the page to return. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items to return per page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} +``` + +#### Пример создания запроса для развертывания виртуальной машины: + +```go +package main + +import ( + vcontrol "repository.basistech.ru/BASIS/dynamix-standart-go-sdk" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/config" + vm "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" +) + +func main() { + // Настройка конфигурации + // Настройка конфигурации + cfg := config.Config{ + Username: "", + Password: "", + VControlURL: "", + Retries: 5, + } + + // Создание клиента + client := vcontrol.New(cfg) + + // Создание структуры запроса + // CreateRequest - реквест на создание виртуальной машины + req := vm.CreateVMRequest{ + Name: "VMname", + RAMSize: 1024, + VideoRAMSize: 64 + CPUCount: 1, + CPUSockets: 1, + } +} +``` + +### Выполнение запроса + +Чтобы выполнить запрос, необходимо: + +1. Вызвать у клиента метод, отвечающий за определение группы API для взаимодействия, на данный момент это `.API()`. Данный метод возвращает соответствующие структуры, с помощью которых можно совершать запросы. +2. Вызвать у возвращенной структуры метод, определяющий группу ручек для взаимодействия. + + Доступные методы для `.API()`: + +- `.Cluster()` - для работы с `Cluster` +- `.Extstorage()` - для работы с `External storage` +- `.Folder()` - для работы с `Folder` +- `.Image()` - для работы с `Image` +- `.Node()` - для работы с `Node` +- `.Respool()` - для работы с `Resource pool` +- `.Template()` - для работы с `Template` +- `.VM()` - для работы с `VM` + +3. Вызвать метод, отвечающий за выполнение запроса и передать в него: + +- контекст; +- структуру запроса. + +4. Обработать результат и ошибки. + +Т.к. все вызовы методов идут последовательно, можно их объединить в конвейер: +Общий вид конвейера будет выглядеть так: + +```go + client..<группа>.<метод> +``` + +#### Пример выполнения запроса + +```go +package main + +import ( + "context" + "fmt" + "log" + "time" + + vcontrol "repository.basistech.ru/BASIS/dynamix-standart-go-sdk" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/config" + vm "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" + ) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + Username: "", + Password: "", + VControlURL: "", + Retries: 5, + } + + cfg.SetTimeout(5 * time.Minute) + + // Создание клиента + client := vcontrol.New(cfg) + + // Создание структуры запроса + req := vm.CreateVMRequest{ + Name: "VMname", + RAMSize: 1024, + VideoRAMSize: 64, + CPUCount: 1, + CPUSockets: 1, + } + + // Выполнение запроса с помощью конвейера + res, err := client.API().VM().Create(context.Background(), req) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%+v", res) +} +``` + +Для запросов Get и List реализованы запросы GetRaw и ListRaw, которые возвращают ответ не в виде соответствующей структуры, а в виде массива байт (JSON). +Выполнение таких запросов происходит аналогично. + +#### Пример выполнения GetRaw и ListRaw запросов + +```go +package main + +import ( + "context" + "fmt" + "log" + "time" + + vcontrol "repository.basistech.ru/BASIS/dynamix-standart-go-sdk" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/config" + vm "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + Username: "", + Password: "", + VControlURL: "", + Retries: 5, + } + + cfg.SetTimeout(5 * time.Minute) + + // Создание клиента + client := vcontrol.New(cfg) + + // Создание структуры запроса + // 1. Создание структуры запроса GetVMRequest на получение информации об виртуальной машине и выполнение GetRaw запроса с помощью конвейера + req1 := vm.GetVMRequest{ + VmID: 360, + } + res1, err := client.API().VM().GetRaw(context.Background(), req1) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(res1)) + + // 2. Создание структуры запроса ListVMRequest на получение информации об виртуальных машинах и выполнение ListRaw запроса с помощью конвейера + req2 := vm.ListVMRequest{} + res2, err := client.API().VM().ListRaw(context.Background(), req2) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(res2)) +} +``` diff --git a/client.go b/client.go new file mode 100644 index 0000000..689fa94 --- /dev/null +++ b/client.go @@ -0,0 +1,185 @@ +package vcontrolsdk + +import ( + "bytes" + "context" + "crypto/tls" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + "time" + + "github.com/google/go-querystring/query" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/config" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + api "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg" +) + +// VControlClient is HTTP-client for platform +type VControlClient struct { + URL string + client *http.Client + cfg config.Config + expiryTime time.Time + mutex *sync.Mutex +} + +// client builder +func New(cfg config.Config) *VControlClient { + if cfg.Retries == 0 { + cfg.Retries = 5 + } + + var expiryTime time.Time + + if cfg.Token != "" { + expiryTime = time.Now().AddDate(0, 0, 1) + } + + return &VControlClient{ + URL: cfg.VControlURL, + client: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: cfg.SSLSkipVerify, + }, + }, + }, + cfg: trimConfig(&cfg), + expiryTime: expiryTime, + mutex: &sync.Mutex{}, + } +} + +// API builder +func (ldc *VControlClient) API() *api.API { + return api.New(ldc) +} + +// ApiCall method for sending requests to the platform +func (ldc *VControlClient) ApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { + // get token + if err := ldc.getToken(ctx); err != nil { + return nil, err + } + + var body *bytes.Buffer + + byteSlice, ok := params.([]byte) + if ok { + body = bytes.NewBuffer(byteSlice) + } else { + values, err := query.Values(params) + if err != nil { + return nil, err + } + body = bytes.NewBufferString(values.Encode()) + } + + req, err := http.NewRequestWithContext(ctx, method, ldc.URL+constants.API+url, body) + if err != nil { + return nil, err + } + + // perform request + respBytes, err := ldc.do(req) + if err != nil { + return nil, err + } + + return respBytes, err +} + +func (ldc *VControlClient) getToken(ctx context.Context) error { + ldc.mutex.Lock() + defer ldc.mutex.Unlock() + + // new token is not needed + if ldc.cfg.Token != "" || !time.Now().After(ldc.expiryTime) { + return nil + } + + // set up request headers and body + body := fmt.Sprintf("login=%s&password=%s", url.QueryEscape(ldc.cfg.Username), url.QueryEscape(ldc.cfg.Password)) + bodyReader := strings.NewReader(body) + req, _ := http.NewRequestWithContext(ctx, "POST", ldc.cfg.VControlURL+constants.API+constants.APIv0+"/auth", bodyReader) + + // request token + resp, err := ldc.client.Do(req) + if err != nil { + return fmt.Errorf("cannot get token: %w", err) + } + defer resp.Body.Close() + + var respBodyBytes []byte + respBodyBytes, err = io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("cannot get token: %w", err) + } + + if resp.StatusCode != 200 { + return fmt.Errorf("cannot get token: %s", respBodyBytes) + } + + var token string + cookies := resp.Cookies() + for _, c := range cookies { + if c.Name == "api_token" { + token = c.Value + } + } + + // save token in config + ldc.cfg.Token = token + ldc.expiryTime = time.Now().AddDate(0, 0, 1) + + return nil +} + +// do method performs request and returns response as an array of bytes and nil error in case of response status code 200. +// In any other cases do returns nil response and error. +// Retries are implemented in case of connection reset errors. +func (ldc *VControlClient) do(req *http.Request) ([]byte, error) { + + req.Header.Set("Cookie", fmt.Sprintf("api_token=%s", ldc.cfg.Token)) + req.Header.Set("Accept", "application/json") + + buf, err := io.ReadAll(req.Body) + if err != nil { + return nil, err + } + + req.Body.Close() + req.Body = io.NopCloser(bytes.NewBuffer(buf)) + + resp, err := ldc.client.Do(req) + if resp != nil { + defer resp.Body.Close() + } + + // handle http request errors + if err != nil { + return nil, err + } + if resp == nil { + return nil, fmt.Errorf("got empty response without error") + } + + // handle successful request + respBytes, _ := io.ReadAll(resp.Body) + if resp.StatusCode == 200 { + return respBytes, nil + } + + // handle errors with status code other than 200 + err = fmt.Errorf("%s", respBytes) + return nil, fmt.Errorf("could not execute request: %w", err) +} + +func trimConfig(cfg *config.Config) config.Config { + cfg.VControlURL = strings.TrimSuffix(cfg.VControlURL, "/") + return *cfg +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..4d060d9 --- /dev/null +++ b/config/config.go @@ -0,0 +1,95 @@ +package config + +import ( + "encoding/json" + "os" + "time" + + "gopkg.in/yaml.v2" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" +) + +// Client configuration +type Config struct { + // ServiceAccount username + // Required: false + // Example : "admin_user" + Username string `json:"username" yaml:"username" validate:"required"` + + // ServiceAccount password + // Required: false + // Example: "[1o>hYkjnJr)HI78q7t&#%8Lm" + Password string `json:"password" yaml:"password" validate:"required"` + + // Platform token + // Required: false + // Example: "158e76424b0d4810b6086hgbhj928fc4a6bc06e" + Token string `json:"token" yaml:"token"` + + // Address of the platform on which the actions are planned + // Required: true + // Example: "https://192.168.0.1" + VControlURL string `json:"vControlURL" yaml:"vControlURL" validate:"url"` + + // Amount platform request attempts + // Default value: 5 + // Required: false + Retries uint64 `json:"retries" yaml:"retries"` + + // Skip verify + // Required: false + SSLSkipVerify bool `json:"sslSkipVerify" yaml:"sslSkipVerify"` + + // HTTP client timeout, unlimited if left empty + // Required: false + Timeout Duration `json:"timeout" yaml:"timeout"` +} + +// SetTimeout is used to set HTTP client timeout. +func (c *Config) SetTimeout(dur time.Duration) { + c.Timeout = Duration(dur) +} + +// ParseConfigJSON parses Config from specified JSON-formatted file. +func ParseConfigJSON(path string) (Config, error) { + file, err := os.ReadFile(path) + if err != nil { + return Config{}, err + } + + var config Config + + err = json.Unmarshal(file, &config) + if err != nil { + return Config{}, err + } + + err = validators.ValidateConfig(config) + if err != nil { + return Config{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return config, nil +} + +// ParseConfigYAML parses Config from specified YAML-formatted file. +func ParseConfigYAML(path string) (Config, error) { + file, err := os.ReadFile(path) + if err != nil { + return Config{}, err + } + + var config Config + + err = yaml.Unmarshal(file, &config) + if err != nil { + return Config{}, err + } + + err = validators.ValidateConfig(config) + if err != nil { + return Config{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return config, nil +} diff --git a/config/timeouts.go b/config/timeouts.go new file mode 100644 index 0000000..924158a --- /dev/null +++ b/config/timeouts.go @@ -0,0 +1,54 @@ +package config + +import ( + "encoding/json" + "fmt" + "time" +) + +// Duration is a wrapper around time.Duration (used for better user experience) +type Duration time.Duration + +func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { + var v interface{} + if err := unmarshal(&v); err != nil { + return err + } + switch value := v.(type) { + case string: + tmp, err := time.ParseDuration(value) + if err != nil { + return err + } + *d = Duration(tmp) + return nil + case float64: + return nil + default: + return fmt.Errorf("invalid duration %v", value) + } +} + +func (d *Duration) UnmarshalJSON(b []byte) error { + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { + return err + } + switch value := v.(type) { + case string: + tmp, err := time.ParseDuration(value) + if err != nil { + return err + } + *d = Duration(tmp) + return nil + case float64: + return nil + default: + return fmt.Errorf("invalid duration %v", value) + } +} + +func (d *Duration) Get() time.Duration { + return time.Duration(*d) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a6f7242 --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module repository.basistech.ru/BASIS/dynamix-standart-go-sdk + +go 1.20 + +require github.com/google/go-querystring v1.1.0 + +require github.com/go-playground/validator/v10 v10.11.2 + +require gopkg.in/yaml.v2 v2.4.0 + +require ( + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..332a8ed --- /dev/null +++ b/go.sum @@ -0,0 +1,39 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/interfaces/caller.go b/interfaces/caller.go new file mode 100644 index 0000000..511c3d7 --- /dev/null +++ b/interfaces/caller.go @@ -0,0 +1,9 @@ +package interfaces + +import "context" + +// Interface for sending requests to platform +type Caller interface { + // VControlApiCall method for sending requests to the platform + ApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) +} diff --git a/internal/constants/constants.go b/internal/constants/constants.go new file mode 100644 index 0000000..f35464f --- /dev/null +++ b/internal/constants/constants.go @@ -0,0 +1,7 @@ +package constants + +const ( + API = "/api" + APIv0 = "/0" + APIv1 = "/v1.0.0" +) diff --git a/internal/multierror/join.go b/internal/multierror/join.go new file mode 100644 index 0000000..e5afca3 --- /dev/null +++ b/internal/multierror/join.go @@ -0,0 +1,41 @@ +package multierror + +func Join(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + var b []byte + for i, err := range e.errs { + if i > 0 { + b = append(b, '\n') + } + b = append(b, err.Error()...) + } + return string(b) +} + +func (e *joinError) Unwrap() []error { + return e.errs +} diff --git a/internal/validators/custom.go b/internal/validators/custom.go new file mode 100644 index 0000000..40649f7 --- /dev/null +++ b/internal/validators/custom.go @@ -0,0 +1,247 @@ +package validators + +import ( + "net/url" + "reflect" + + "github.com/go-playground/validator/v10" +) + +// virtTypeValidator validates the virtualization type +func virtTypeValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, virtTypeValues) +} + +// vmStatusValidator validates the VM status +func vmStatusValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, vmStatusValues) +} + +// cpuUnitsValidator validates the CPU units (2-262144) +func cpuUnitsValidator(fl validator.FieldLevel) bool { + cpuUnits := fl.Field().Int() + return IsInRange(cpuUnits, cpuUnitsRange) +} + +// cpuLimitValidator validates the CPU limit (0-100) +func cpuLimitValidator(fl validator.FieldLevel) bool { + cpuLimit := fl.Field().Int() + return IsInRange(cpuLimit, cpuLimitRange) +} + +// ioPriorityValidator validates the IO priority (0-7) +func ioPriorityValidator(fl validator.FieldLevel) bool { + ioPriority := fl.Field().Int() + return IsInRange(ioPriority, ioPriorityRange) +} + +// videoRamValidator validates the video RAM limit (1-1024) +func videoRamValidator(fl validator.FieldLevel) bool { + videoRam := fl.Field().Int() + return IsInRange(videoRam, videoRamRange) +} + +// memGuaranteeSizeValidator validates the memory guarantee size (-1, 100) +func memGuaranteeSizeValidator(fl validator.FieldLevel) bool { + memGuarantee := fl.Field().Int() + return IsInRange(memGuarantee, memGuaranteeSizeRange) +} + +// haStatusValidator validates the HA status +func haStatusValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, haStatusValues) +} + +// visibilityValidator validates the visibility +func visibilityValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, visibilityValues) +} + +// metricsPeriodValidator validates the metrics +func metricsPeriodValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, metricPeriodValues) +} + +// networkTypeValidator validates the network type +func networkTypeValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, networkTypeValues) +} + +// imageTypeValidator validates the image type +func imageTypeValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, imageTypeValues) +} + +// imageStatusValidator validates the image status +func imageStatusValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, imageStatusValues) +} + +// isBoolTypeValidator validates that field value == bool +func isBoolTypeValidator(fe validator.FieldLevel) bool { + return fe.Field().CanConvert(reflect.TypeOf(true)) +} + +// extStorageStatusValidator validates the external storage status +func extStorageStatusValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, extStorageStatusValues) +} + +// extStorageTypeValidator validates the external storage type +func extStorageTypeValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, extStorageTypeValues) +} + +// nodeMaintenanceStatusValidator validates the node maintenance status +func nodeMaintenanceStatusValidator(fl validator.FieldLevel) bool { + fieldSlice, ok := fl.Field().Interface().([]string) + if !ok { + return false + } + + for _, value := range fieldSlice { + if !IsInSlice(value, nodeMaintenanceStatusValues) { + return false + } + } + + return true +} + +// nodeAgentStatusValidator validates the node agent status +func nodeAgentStatusValidator(fl validator.FieldLevel) bool { + fieldSlice, ok := fl.Field().Interface().([]string) + if !ok { + return false + } + + for _, value := range fieldSlice { + if !IsInSlice(value, nodeAgentStatusValues) { + return false + } + } + + return true +} + +// haMultiStatusValidator validates the HA status +func haMultiStatusValidator(fl validator.FieldLevel) bool { + fieldSlice, ok := fl.Field().Interface().([]string) + if !ok { + return false + } + + for _, value := range fieldSlice { + if !IsInSlice(value, haStatusValues) { + return false + } + } + + return true +} + +// nodeStatusInHaClusterValidator validates the metrics +func nodeStatusInHaClusterValidator(fl validator.FieldLevel) bool { + fieldSlice, ok := fl.Field().Interface().([]string) + if !ok { + return false + } + + for _, value := range fieldSlice { + if !IsInSlice(value, nodeStatusInHaClusterValues) { + return false + } + } + + return true +} + +// affinityRuleValidator validates the affinity rule value +func affinityRuleValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, affinityRuleValues) +} + +// guestOSValidator validates the guest os value +func guestOSValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, guestOSValues) +} + +// guestOSVersionValidator validates the guest os version value +func guestOSVersionValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, guestOSVersionValues) +} + +// autoStartValidator validates the virtual machine startup options value +func autoStartValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, autoStartValues) +} + +// autoStopValidator validates the virtual machine shutdown mode value +func autoStopValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, autoStopValues) +} + +// vncModeValidator validates the vnc mode value +func vncModeValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, vncModeValues) +} + +// chipsetValidator validates the chipset value +func chipsetValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, chipsetValues) +} + +// clockOffsetValidator validates the VM clock offset value +func clockOffsetValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, clockOffsetValues) +} + +// templateStatusValidator validates the template status value +func templateStatusValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return IsInSlice(fieldValue, templateStatusValues) +} + +func urlValidartor(fl validator.FieldLevel) bool { + fieldValues := fl.Field().String() + + _, err := url.ParseRequestURI(fieldValues) + return err == nil +} diff --git a/internal/validators/helper.go b/internal/validators/helper.go new file mode 100644 index 0000000..50e860a --- /dev/null +++ b/internal/validators/helper.go @@ -0,0 +1,47 @@ +package validators + +import ( + "errors" + + "github.com/go-playground/validator/v10" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/multierror" +) + +func ValidateRequest(req interface{}) error { + validate := getVControlValidator() + return validate.Struct(req) +} + +func ValidateConfig(cfg interface{}) error { + validate := getVControlValidator() + return validate.Struct(cfg) +} + +func ValidationError(fe validator.FieldError) error { + return errors.New(errorMessage(fe)) +} + +func ValidationErrors(fes []validator.FieldError) error { + errs := make([]error, 0, len(fes)) + for _, fe := range fes { + errs = append(errs, ValidationError(fe)) + } + return multierror.Join(errs...) +} + +func GetErrors(err error) validator.ValidationErrors { + return err.(validator.ValidationErrors) +} + +func IsInSlice(str string, target []string) bool { + for _, v := range target { + if v == str { + return true + } + } + return false +} + +func IsInRange(num int64, target []int64) bool { + return num >= target[0] && num <= target[1] +} diff --git a/internal/validators/messages.go b/internal/validators/messages.go new file mode 100644 index 0000000..9d106f3 --- /dev/null +++ b/internal/validators/messages.go @@ -0,0 +1,85 @@ +package validators + +import ( + "fmt" + "strings" + + "github.com/go-playground/validator/v10" +) + +func errorMessage(fe validator.FieldError) string { + prefix := "validation-error:" + + switch fe.Tag() { + case "required": + return fmt.Sprintf("%s %s is required", prefix, fe.Field()) + case "virt_type": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(virtTypeValues)) + case "vm_status": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(vmStatusValues)) + case "cpu_units": + return fmt.Sprintf("%s %s must be between %s", prefix, fe.Field(), joinRange(cpuUnitsRange)) + case "cpu_limit": + return fmt.Sprintf("%s %s must be between %s", prefix, fe.Field(), joinRange(cpuLimitRange)) + case "video_ram": + return fmt.Sprintf("%s %s must be between %s", prefix, fe.Field(), joinRange(videoRamRange)) + case "io_priority": + return fmt.Sprintf("%s %s must be between %s", prefix, fe.Field(), joinRange(ioPriorityRange)) + case "mem_guarantee_size": + return fmt.Sprintf("%s %s must be between %s", prefix, fe.Field(), joinRange(memGuaranteeSizeRange)) + case "ha_status": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(haStatusValues)) + case "visibility": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(visibilityValues)) + case "metrics_period": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(metricPeriodValues)) + case "network_type": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(networkTypeValues)) + case "image_type": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(imageTypeValues)) + case "image_status": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(imageStatusValues)) + case "extstorage_status": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(extStorageStatusValues)) + case "extstorage_type": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(extStorageTypeValues)) + case "maintenance_status": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(nodeMaintenanceStatusValues)) + case "agent_update_status": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(nodeAgentStatusValues)) + case "node_status_in_ha_cluster": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(nodeStatusInHaClusterValues)) + case "affinity": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(affinityRuleValues)) + case "guest_os": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(guestOSValues)) + case "guest_os_version": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(guestOSVersionValues)) + case "auto_start": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(autoStartValues)) + case "auto_stop": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(autoStopValues)) + case "vnc_mode": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(vncModeValues)) + case "chipset": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(chipsetValues)) + case "clock_offset": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(clockOffsetValues)) + case "template_status": + return fmt.Sprintf("%s %s must be one of the following: %s", prefix, fe.Field(), joinValues(templateStatusValues)) + case "is_bool": + return fmt.Sprintf("%s %s must be bool type", prefix, fe.Field()) + case "url": + return fmt.Sprintf("%s %s: unexpected URL format", prefix, fe.Field()) + } + + return fe.Error() +} + +func joinValues(values []string) string { + return strings.Join(values, ", ") +} + +func joinRange(lim []int64) string { + return fmt.Sprintf("%d and %d", lim[0], lim[1]) +} diff --git a/internal/validators/validator.go b/internal/validators/validator.go new file mode 100644 index 0000000..7295a6d --- /dev/null +++ b/internal/validators/validator.go @@ -0,0 +1,210 @@ +package validators + +import ( + "sync" + + "github.com/go-playground/validator/v10" +) + +var ( + once sync.Once + VControlValidator = validator.New() +) + +// getVControlValidator returns singleton instance of VControlValidator. +func getVControlValidator() *validator.Validate { + once.Do(func() { + err := registerAllValidators(VControlValidator) + if err != nil { + panic(err) + } + }) + + return VControlValidator +} + +// registerAllValidators registers all custom validators in VControlValidator. +func registerAllValidators(validate *validator.Validate) error { + + // Register virt_type validator + err := validate.RegisterValidation("virt_type", virtTypeValidator) + if err != nil { + return err + } + + // Register vm status validator + err = validate.RegisterValidation("vm_status", vmStatusValidator) + if err != nil { + return err + } + + // Register cpu_units validator + err = validate.RegisterValidation("cpu_units", cpuUnitsValidator) + if err != nil { + return err + } + + // Register cpu_limit validator + err = validate.RegisterValidation("cpu_limit", cpuLimitValidator) + if err != nil { + return err + } + + // Register mem_guarantee_size validator + err = validate.RegisterValidation("mem_guarantee_size", memGuaranteeSizeValidator) + if err != nil { + return err + } + + // Register video_ram validator + err = validate.RegisterValidation("video_ram", videoRamValidator) + if err != nil { + return err + } + + // Register mem_guarantee_size validator + err = validate.RegisterValidation("metrics_period", metricsPeriodValidator) + if err != nil { + return err + } + + // Register ha_status validator + err = validate.RegisterValidation("ha_status", haStatusValidator) + if err != nil { + return err + } + + // Register visibility validator + err = validate.RegisterValidation("visibility", visibilityValidator) + if err != nil { + return err + } + + // Register visibility validator + err = validate.RegisterValidation("image_type", imageTypeValidator) + if err != nil { + return err + } + + // Register visibility validator + err = validate.RegisterValidation("image_status", imageStatusValidator) + if err != nil { + return err + } + + // Register bool validator + err = validate.RegisterValidation("is_bool", isBoolTypeValidator) + if err != nil { + return err + } + + // Register network_type validator + err = validate.RegisterValidation("network_type", networkTypeValidator) + if err != nil { + return err + } + + // Register external storage status validator + err = validate.RegisterValidation("extstorage_status", extStorageStatusValidator) + if err != nil { + return err + } + + // Register external storage type validator + err = validate.RegisterValidation("extstorage_type", extStorageTypeValidator) + if err != nil { + return err + } + + // Register node maintenance status validator + err = validate.RegisterValidation("maintenance_status", nodeMaintenanceStatusValidator) + if err != nil { + return err + } + + // Register node maintenance status validator + err = validate.RegisterValidation("agent_update_status", nodeAgentStatusValidator) + if err != nil { + return err + } + + // Register multi choose ha status validator + err = validate.RegisterValidation("ha_multi_status", haMultiStatusValidator) + if err != nil { + return err + } + + // Register node status on ha cluster validator + err = validate.RegisterValidation("node_status_in_ha_cluster", nodeStatusInHaClusterValidator) + if err != nil { + return err + } + + // Register affinity rule validator + err = validate.RegisterValidation("affinity", affinityRuleValidator) + if err != nil { + return err + } + + // Register guest os validator + err = validate.RegisterValidation("guest_os", guestOSValidator) + if err != nil { + return err + } + + // Register guest os version validator + err = validate.RegisterValidation("guest_os_version", guestOSVersionValidator) + if err != nil { + return err + } + + // Register io priority validator + err = validate.RegisterValidation("io_priority", ioPriorityValidator) + if err != nil { + return err + } + + // Register auto start validator + err = validate.RegisterValidation("auto_start", autoStartValidator) + if err != nil { + return err + } + + // Register auto stop validator + err = validate.RegisterValidation("auto_stop", autoStopValidator) + if err != nil { + return err + } + + // Register vnc mode validator + err = validate.RegisterValidation("vnc_mode", vncModeValidator) + if err != nil { + return err + } + + // Register chipset validator + err = validate.RegisterValidation("chipset", chipsetValidator) + if err != nil { + return err + } + + // Register clock offset validator + err = validate.RegisterValidation("clock_offset", clockOffsetValidator) + if err != nil { + return err + } + + // Register template status validator + err = validate.RegisterValidation("template_status", templateStatusValidator) + if err != nil { + return err + } + + // Register url validator + err = validate.RegisterValidation("url", urlValidartor) + if err != nil { + return err + } + + return nil +} diff --git a/internal/validators/values.go b/internal/validators/values.go new file mode 100644 index 0000000..59f6a91 --- /dev/null +++ b/internal/validators/values.go @@ -0,0 +1,35 @@ +package validators + +var ( + metricPeriodValues = []string{"latest", "hour", "day", "week"} + imageTypeValues = []string{"OPTICAL", "HDD"} + imageStatusValues = []string{"CREATING", "UPLOADING", "READY", "DELETING", "FAILED_TO_CREATE"} + visibilityValues = []string{"visible", "deleted", "all"} + networkTypeValues = []string{"HOST_ONLY", "CLUSTER_NETWORK", "BRIDGED_ETHERNET"} + virtTypeValues = []string{"VM", "CT"} + vmStatusValues = []string{ + "RUNNING", "PAUSED", "STOPPED", "SUSPENDED", + "ERROR", "DELETED", "CREATING", "FAILED_TO_CREATE", + "NODE_OFFLINE", + } + haStatusValues = []string{"ACTIVE", "INACTIVE", "ERROR", "UNKNOWN", "NO LICENSE"} + extStorageStatusValues = []string{"READY", "UNMOUNTED", "UNAVAILABLE", "PARTIALLY_AVAILABLE", "UNKNOWN"} + extStorageTypeValues = []string{"NFSv3", "iSCSI", "blockdev"} + nodeMaintenanceStatusValues = []string{"PREPARING", "INTERRUPTED", "ON_MAINTENANCE"} + nodeAgentStatusValues = []string{"UPDATED", "UPDATE_IN_PROGRESS", "MISMATCH_VERSION", "UPDATE_ERROR"} + nodeStatusInHaClusterValues = []string{"IDLE", "ATTACHING", "MEMBER", "CANDIDATE", "MASTER", "EXISTING", "NET_ISOLATED", "DISK_ISOLATED", "FAILED", "UNKNOWN"} + affinityRuleValues = []string{"SHOULD", "MUST"} + guestOSValues = []string{"ANDROID", "CHROMEOS", "FREEBSD", "LINUX", "MACOS", "MSDOS", "NETWARE", "OS2", "OTHER", "SOLARIS", "WINDOWS"} + guestOSVersionValues = []string{"ANDROID_2_x", "ANDROID_3_x", "ANDROID_4_0", "ANDROID_LAST", "ANDROID_OTHER", "ASTRA_LINUX", "BSD_4X", "BSD_5X", "BSD_6X", "BSD_7X", "BSD_8X", "BSD_LAST", "BSD_OTHER", "CHROMEOS_1x", "CHROMEOS_LAST", "CHROMEOS_OTHER", "DOS_LAST", "DOS_MS622", "DOS_OTHER", "LIN_CENTOS", "LIN_CENTOS_7", "LIN_CENTOS_8", "LIN_DEBIAN", "LIN_FEDORA", "LIN_FEDORA_5", "LIN_KRNL_24", "LIN_KRNL_26", "LIN_LAST", "LIN_MAGEIA", "LIN_MANDRAKE", "LIN_MINT", "LIN_OPENSUSE", "LIN_OTHER", "LIN_PSBM", "LIN_REDHAT", "LIN_REDHAT_7", "LIN_REDHAT_8", "LIN_RHLES3", "LIN_RH_LEGACY", "LIN_SLES11", "LIN_SLES12", "LIN_SLES9", "LIN_SUSE", "LIN_UBUNTU", "LIN_VZLINUX", "LIN_VZLINUX_7", "LIN_VZLINUX_8", "LIN_XANDROS", "LIN_CLOUDLINUX", "LIN_CLOUDLINUX_7", "MACOS_LAST", "MACOS_LEOPARD", "MACOS_SNOW_LEOPARD", "MACOS_TIGER", "MACOS_UNIVERSAL", "NET_4X", "NET_5X", "NET_6X", "NET_LAST", "NET_OTHER", "OS2_ECS11", "OS2_ECS12", "OS2_LAST", "OS2_OTHER", "OS2_WARP3", "OS2_WARP4", "OS2_WARP45", "OTH_LAST", "OTH_OPENSTEP", "OTH_OTHER", "OTH_QNX", "SOL_10", "SOL_11", "SOL_9", "SOL_LAST", "SOL_OPEN", "SOL_OTHER", "WIN_2003", "WIN_2008", "WIN_2012", "WIN_2016", "WIN_2019", "WIN_2K", "WIN_311", "WIN_95", "WIN_98", "WIN_LAST", "WIN_ME", "WIN_NT", "WIN_OTHER", "WIN_VISTA", "WIN_WINDOWS10", "WIN_WINDOWS7", "WIN_WINDOWS8", "WIN_WINDOWS8_1", "WIN_XP"} + autoStartValues = []string{"OFF", "ON", "AUTO", "START_APP", "OPEN_WINDOW"} + autoStopValues = []string{"STOP", "SUSPEND", "SHUTDOWN"} + vncModeValues = []string{"disabled", "auto", "manual"} + chipsetValues = []string{"I440FX", "Q35"} + clockOffsetValues = []string{"UTS", "LOCALTIME"} + templateStatusValues = []string{"unknown", "creating", "error", "deleted", "failed_to_create", "ready"} + cpuUnitsRange = []int64{2, 262144} + cpuLimitRange = []int64{0, 100} + ioPriorityRange = []int64{0, 7} + videoRamRange = []int64{1, 1024} + memGuaranteeSizeRange = []int64{-1, 100} +) diff --git a/pkg/api.go b/pkg/api.go new file mode 100644 index 0000000..c1e3eaf --- /dev/null +++ b/pkg/api.go @@ -0,0 +1,13 @@ +package api + +import "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" + +type API struct { + client interfaces.Caller +} + +func New(client interfaces.Caller) *API { + return &API{ + client: client, + } +} diff --git a/pkg/cluster.go b/pkg/cluster.go new file mode 100644 index 0000000..55a9df8 --- /dev/null +++ b/pkg/cluster.go @@ -0,0 +1,10 @@ +package api + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/cluster" +) + +// Accessing the Cluster method group +func (ca *API) Cluster() *cluster.Cluster { + return cluster.New(ca.client) +} diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go new file mode 100644 index 0000000..bdeb29a --- /dev/null +++ b/pkg/cluster/cluster.go @@ -0,0 +1,15 @@ +package cluster + +import "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" + +// Structure for creating request to cluster +type Cluster struct { + client interfaces.Caller +} + +// Builder for vms endpoints +func New(client interfaces.Caller) *Cluster { + return &Cluster{ + client, + } +} diff --git a/pkg/cluster/list.go b/pkg/cluster/list.go new file mode 100644 index 0000000..48824ec --- /dev/null +++ b/pkg/cluster/list.go @@ -0,0 +1,42 @@ +package cluster + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/cluster/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/cluster/requests" +) + +// List gets a list of all clusters +func (c Cluster) List(ctx context.Context, req requests.ListClusterRequest) (*models.ListClusterResponse, error) { + + res, err := c.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListClusterResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListRaw gets a list of all clusters as an array of bytes +func (c Cluster) ListRaw(ctx context.Context, req requests.ListClusterRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/cluster" + + res, err := c.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cluster/list_v_network.go b/pkg/cluster/list_v_network.go new file mode 100644 index 0000000..8c2d643 --- /dev/null +++ b/pkg/cluster/list_v_network.go @@ -0,0 +1,43 @@ +package cluster + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/cluster/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/cluster/requests" +) + +// ListVNetworks Returns a list of v_networks for a specific cluster +func (c Cluster) ListVNetworks(ctx context.Context, req requests.ListVNetworksRequest) (*models.ListVNetwork, error) { + + res, err := c.ListVNetworksRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListVNetwork{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListVNetworksRaw gets a list of all v_network the user has access to as an array of bytes +func (c Cluster) ListVNetworksRaw(ctx context.Context, req requests.ListVNetworksRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv1 + "/cluster/" + fmt.Sprint(req.ClusterID) + "/v_network" + + res, err := c.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cluster/models/model_list.go b/pkg/cluster/models/model_list.go new file mode 100644 index 0000000..475cd90 --- /dev/null +++ b/pkg/cluster/models/model_list.go @@ -0,0 +1,85 @@ +package models + +import ( + "encoding/json" + "time" +) + +// ListClusterResponse represents the response for a list of clusters. +type ListClusterResponse struct { + List ListCluster `json:"cluster_list"` + RequestID string `json:"request_id"` +} + +type ListCluster struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []ItemCluster `json:"items"` + HasMore bool `json:"has_more"` +} + +// ItemCluster represents the details of a specific cluster. +type ItemCluster struct { + FenixClusterStatus string `json:"fenix_cluster_status"` + ClusterID int `json:"cluster_id"` + MemoryOvercommit float64 `json:"memory_overcommit"` + CPUOvercommit float64 `json:"cpu_overcommit"` + Created time.Time `json:"created"` + StorageName string `json:"storage_name"` + StorageType string `json:"storage_type"` + FenixBroadcastIP string `json:"fenix_broadcast_ip"` + Modified time.Time `json:"modified"` + FenixPingIP string `json:"fenix_ping_ip"` + Deleted time.Time `json:"deleted"` + DrsMode string `json:"drs_mode"` + Name string `json:"name"` + FenixClusterID int `json:"fenix_cluster_id"` + VstorageClusterID int `json:"vstorage_cluster_id"` + FenixBalancingStrategy string `json:"fenix_balancing_strategy"` + FenixEnabled bool `json:"fenix_enabled"` + NodeCount int `json:"node_count"` + VMTotalRAM int `json:"vm_total_ram"` + VMCount int `json:"vm_count"` + ResourceMetrics interface{} `json:"resource_metrics"` +} + +type ResourceMetrics struct { + CPU int `json:"cpu"` + MemoryAvailable float64 `json:"memory_available"` + AvgUtilization float64 `json:"avg_utilization"` + MaxMemoryAvailable float64 `json:"max_memory_available"` + MaxCPUAvailable float64 `json:"max_cpu_available"` + MigrationsCount int `json:"migrations_count"` + CPUAvailable float64 `json:"cpu_available"` + Memory int `json:"memory"` + StandardDeviation float64 `json:"standard_deviation"` + MaxStandardDeviation float64 `json:"max_standard_deviation"` +} + +func (ic *ItemCluster) UnmarshalJSON(data []byte) error { + type Alias ItemCluster + temp := &struct { + ResourceMetrics json.RawMessage `json:"resource_metrics,omitempty"` + *Alias + }{ + Alias: (*Alias)(ic), + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + if len(temp.ResourceMetrics) > 0 { + var metrics ResourceMetrics + if err := json.Unmarshal(temp.ResourceMetrics, &metrics); err != nil { + return err + } + ic.ResourceMetrics = metrics + } else { + ic.ResourceMetrics = nil + } + + return nil +} diff --git a/pkg/cluster/models/model_v_network.go b/pkg/cluster/models/model_v_network.go new file mode 100644 index 0000000..448f0cf --- /dev/null +++ b/pkg/cluster/models/model_v_network.go @@ -0,0 +1,57 @@ +package models + +import "time" + +type ListVNetwork struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []VNetworkItem `json:"items"` + HasMore bool `json:"has_more"` + RequestID string `json:"request_id"` +} + +type VNetworkItem struct { + AliasID int `json:"alias_id"` + NetworkType string `json:"network_type"` + ClusterID int `json:"cluster_id"` + VirtualNetworkID int `json:"virtual_network_id"` + Created time.Time `json:"created"` + Modified time.Time `json:"modified"` + ExternalUUID int `json:"external_uuid"` + Deleted time.Time `json:"deleted"` + Name string `json:"name"` + BridgeInterfaceID int `json:"bridge_interface_id"` + VlanID int `json:"vlan_id"` + Description string `json:"description"` + VlanInterfaceID int `json:"vlan_interface_id"` + NetworkInterfaceID int `json:"network_interface_id"` + ExternalResourceID int `json:"external_resource_id"` + NodeID int `json:"node_id"` + NodeCount int `json:"node_count"` + VMCount int `json:"vm_count"` + RunningVMCount int `json:"running_vm_count"` + Alias string `json:"alias"` + Node Node `json:"node"` +} + +type Node struct { + MaintenanceStatus string `json:"maintenance_status"` + ClusterID int `json:"cluster_id"` + Created time.Time `json:"created"` + VstorageStatus string `json:"vstorage_status"` + IPAddress string `json:"ip_address"` + Status string `json:"status"` + Modified time.Time `json:"modified"` + Deleted time.Time `json:"deleted"` + ServerUUID string `json:"server_uuid"` + Name string `json:"name"` + VstorageHostID int `json:"vstorage_host_id"` + Compute bool `json:"compute"` + VstorageClusterID int `json:"vstorage_cluster_id"` + SdkStatus string `json:"sdk_status"` + Description string `json:"description"` + StorageStatus string `json:"storage_status"` + NodeID int `json:"node_id"` +} diff --git a/pkg/cluster/requests/request_list.go b/pkg/cluster/requests/request_list.go new file mode 100644 index 0000000..c061c9e --- /dev/null +++ b/pkg/cluster/requests/request_list.go @@ -0,0 +1,68 @@ +package requests + +import "time" + +// ListClusterRequest struct to get a list of clusters +type ListClusterRequest struct { + // Cluster name. Searching by partial matching. Ignored if NameExact is provided. + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Exact cluster name. + // Required: false + NameExact string `url:"name_exact,omitempty" json:"name_exact,omitempty"` + + // VStorage Cluster ID. + // Required: false + VStorageClusterID int `url:"vstorage_cluster_id,omitempty" json:"vstorage_cluster_id,omitempty"` + + // External Storage ID. + // Required: false + ExternalStorageID int `url:"external_storage_id,omitempty" json:"external_storage_id,omitempty"` + + // List of cluster IDs to exclude from listing. + // Required: false + ExcludeIDs []int `url:"exclude_ids,omitempty" json:"exclude_ids,omitempty"` + + // Template for filtering by any text field. + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // Filter clusters created before this date. + // Required: false + CreatedBefore time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Filter clusters created after this date. + // Required: false + CreatedAfter time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` + + // List columns which will be used by FilterText. + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // Field name or list of field names used for sorting. Ascending sort is default. For descending sort, use "-" before name. + // Default sorting field: "name". + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Visibility options: "visible" - Only active clusters, "deleted" - Only removed clusters, "all" - All clusters. + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` + + // Add resource metrics. + // Required: false + WithResourceMetrics bool `url:"with_resource_metrics,omitempty" json:"with_resource_metrics,omitempty"` + + // Include external resource's clusters. + // Default: false + // Required: false + IncludeExternal bool `url:"include_external,omitempty" json:"include_external,omitempty"` + + // Number of returning page. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items on page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} diff --git a/pkg/cluster/requests/request_v_network.go b/pkg/cluster/requests/request_v_network.go new file mode 100644 index 0000000..2de7a63 --- /dev/null +++ b/pkg/cluster/requests/request_v_network.go @@ -0,0 +1,57 @@ +package requests + +import "time" + +type ListVNetworksRequest struct { + // Cluster id where virtual net is attached + // Required: true + ClusterID int `url:"cluster_id" json:"cluster_id" validate:"required"` + + // ID of alias + // Required: false + AliasID int `url:"alias_id,omitempty" json:"alias_id,omitempty"` + + // Name of the vnet + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Virtual networks for filtering by and text field + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // List columns which will be used by filter_text + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // Filter entities created before this date. + // Required: false + CreatedBefore time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Filter entities created after this date. + // Required: false + CreatedAfter time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` + + // List of columns for sorting. + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Visibility status of the interface (visible, all, deleted). + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` + + // NetworkType type of virtual network (HOST_ONLY, CLUSTER_NETWORK, BRIDGED_ETHERNET). + // Required: false + NetworkType string `url:"network_type,omitempty" json:"network_type,omitempty" validate:"omitempty,network_type"` + + // NetworkType type of virtual network (HOST_ONLY, CLUSTER_NETWORK, BRIDGED_ETHERNET). + // Required: false + FilterByUsePermission bool `url:"filter_by_use_permission,omitempty" json:"filter_by_use_permission,omitempty"` + + // Number of the page to return. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items to return per page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} diff --git a/pkg/extstorage.go b/pkg/extstorage.go new file mode 100644 index 0000000..b4cdddf --- /dev/null +++ b/pkg/extstorage.go @@ -0,0 +1,10 @@ +package api + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/extstorage" +) + +// Accessing the ExtStorage method group +func (ca *API) ExtStorage() *extstorage.ExtStorage { + return extstorage.New(ca.client) +} diff --git a/pkg/extstorage/extstorage.go b/pkg/extstorage/extstorage.go new file mode 100644 index 0000000..345859c --- /dev/null +++ b/pkg/extstorage/extstorage.go @@ -0,0 +1,15 @@ +package extstorage + +import "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" + +// Structure for creating request to external storage +type ExtStorage struct { + client interfaces.Caller +} + +// Builder for external storage endpoints +func New(client interfaces.Caller) *ExtStorage { + return &ExtStorage{ + client, + } +} diff --git a/pkg/extstorage/list.go b/pkg/extstorage/list.go new file mode 100644 index 0000000..7a0ce90 --- /dev/null +++ b/pkg/extstorage/list.go @@ -0,0 +1,42 @@ +package extstorage + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/extstorage/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/extstorage/requests" +) + +// List gets a list of all storages. +func (e ExtStorage) List(ctx context.Context, req requests.ListExtStorageRequest) (*models.ListExtStorageResponse, error) { + + res, err := e.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListExtStorageResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListRaw gets a list of all storages as an array of bytes +func (e ExtStorage) ListRaw(ctx context.Context, req requests.ListExtStorageRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/external_storage" + + res, err := e.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/extstorage/models/model_list.go b/pkg/extstorage/models/model_list.go new file mode 100644 index 0000000..0db3e4f --- /dev/null +++ b/pkg/extstorage/models/model_list.go @@ -0,0 +1,45 @@ +package models + +import "time" + +type ListExtStorageResponse struct { + List ListExtStorage `json:"external_storage_list"` + RequestID string `json:"request_id"` +} + +type ListExtStorage struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []ItemStorage `json:"items"` + HasMore bool `json:"has_more"` +} + +type ItemStorage struct { + ClusterName string `json:"cluster_name"` + ExternalStorageID int `json:"external_storage_id"` + Path string `json:"path"` + ClusterID int `json:"cluster_id"` + MaxDisksNum int `json:"max_disks_num"` + StorageType string `json:"storage_type"` + CacheStatus string `json:"cache_status"` + Username string `json:"username"` + Server string `json:"server"` + CacheSizeMb int `json:"cache_size_mb"` + UsedMb int `json:"used_mb"` + StoragePath string `json:"storage_path"` + CachePath string `json:"cache_path"` + Status string `json:"status"` + Deleted time.Time `json:"deleted"` + Description string `json:"description"` + CacheEnabled bool `json:"cache_enabled"` + Iqn string `json:"iqn"` + Created time.Time `json:"created"` + Modified time.Time `json:"modified"` + MountPoint string `json:"mount_point"` + Name string `json:"name"` + TotalMb int `json:"total_mb"` + VMHomeDirSizeGb int `json:"vm_home_dir_size_gb"` + VMCount int `json:"vm_count"` +} diff --git a/pkg/extstorage/requests/request_list.go b/pkg/extstorage/requests/request_list.go new file mode 100644 index 0000000..3c0e704 --- /dev/null +++ b/pkg/extstorage/requests/request_list.go @@ -0,0 +1,65 @@ +package requests + +import "time" + +type ListExtStorageRequest struct { + // Storage description + // Required: false + Description int `url:"description,omitempty" json:"description,omitempty"` + + // Name of the storage + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Storage path + // Required: false + Path string `url:"path,omitempty" json:"path,omitempty"` + + // Storage server + // Required: false + Server string `url:"server,omitempty" json:"server,omitempty"` + + // Status of storage + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty" validate:"omitempty,extstorage_status"` + + // Type of storage + // Required: false + StorageType string `url:"storage_type,omitempty" json:"storage_type,omitempty" validate:"omitempty,extstorage_type"` + + // Cluster ID + // Required: false + ClusterID string `url:"cluster_id,omitempty" json:"cluster_id,omitempty"` + + // Cluster name + // Required: false + ClusterName string `url:"cluster_name,omitempty" json:"cluster_name,omitempty"` + + // List only unused external storages + // Required: false + OnlyUnused interface{} `url:"only_unused,omitempty" json:"only_unused,omitempty" validate:"omitempty,is_bool"` + + // List columns which will be used by filter_text + // Required: false + FilterText string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // Filter entities created before this date. + // Required: false + CreatedBefore time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Filter entities created after this date. + // Required: false + CreatedAfter time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` + + // List of columns for sorting. + // Required: false + // Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Number of the page to return. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items to return per page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} diff --git a/pkg/folder.go b/pkg/folder.go new file mode 100644 index 0000000..d1d4180 --- /dev/null +++ b/pkg/folder.go @@ -0,0 +1,8 @@ +package api + +import "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/folder" + +// Accessing the folders pool method group +func (ca *API) Folder() *folder.Folder { + return folder.New(ca.client) +} diff --git a/pkg/folder/folders.go b/pkg/folder/folders.go new file mode 100644 index 0000000..f494de1 --- /dev/null +++ b/pkg/folder/folders.go @@ -0,0 +1,17 @@ +package folder + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" +) + +// Structure for creating request to folders +type Folder struct { + client interfaces.Caller +} + +// Builder for file share endpoints +func New(client interfaces.Caller) *Folder { + return &Folder{ + client, + } +} diff --git a/pkg/folder/list.go b/pkg/folder/list.go new file mode 100644 index 0000000..46d1e21 --- /dev/null +++ b/pkg/folder/list.go @@ -0,0 +1,42 @@ +package folder + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/folder/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/folder/requests" +) + +// List gets a list of all folder +func (f Folder) List(ctx context.Context, req requests.ListFolderRequest) (*models.ListFolderResponse, error) { + + res, err := f.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListFolderResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListRaw gets a list of all folders as an array of bytes +func (f Folder) ListRaw(ctx context.Context, req requests.ListFolderRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/folder" + + res, err := f.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/folder/models/model_list.go b/pkg/folder/models/model_list.go new file mode 100644 index 0000000..3e0e4dd --- /dev/null +++ b/pkg/folder/models/model_list.go @@ -0,0 +1,26 @@ +package models + +import "time" + +type ListFolderResponse struct { + List ListFolder `json:"folder_list"` + RequestID string `json:"request_id"` +} + +type ListFolder struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []ItemFolder `json:"items"` + HasMore bool `json:"has_more"` +} + +type ItemFolder struct { + FolderID int `json:"folder_id"` + Deleted time.Time `json:"deleted"` + Name string `json:"name"` + IconType any `json:"icon_type"` + Created time.Time `json:"created"` + ParentID any `json:"parent_id"` +} diff --git a/pkg/folder/requests/list_request.go b/pkg/folder/requests/list_request.go new file mode 100644 index 0000000..e7619a0 --- /dev/null +++ b/pkg/folder/requests/list_request.go @@ -0,0 +1,50 @@ +package requests + +import "time" + +// ListRequest represents the request for retrieving a list of folders. +type ListFolderRequest struct { + // Folder name. + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Folder icon type. + // Required: false + IconType string `url:"icon_type,omitempty" json:"icon_type,omitempty"` + + // Folder ID of the parent folder. "root" if there is no parent folder. + // Required: false + ParentID string `url:"parent_id,omitempty" json:"parent_id,omitempty"` + + // Template for filtering by any text field. + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // List of columns that will be used by FilterText. + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // Filter entities created before this date. + // Required: false + CreatedBefore time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Filter entities created after this date. + // Required: false + CreatedAfter time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` + + // Field name or list of field names used for sorting. Ascending sort is default. For descending sort, use "-" before the name. + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Visibility options: "visible" - Only active folders, "deleted" - Only removed folders, "all" - All folders. + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` + + // Number of the returning page. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items on the page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} diff --git a/pkg/image.go b/pkg/image.go new file mode 100644 index 0000000..022e1a6 --- /dev/null +++ b/pkg/image.go @@ -0,0 +1,10 @@ +package api + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/image" +) + +// Accessing the images method group +func (ca *API) Image() *image.Image { + return image.New(ca.client) +} diff --git a/pkg/image/image.go b/pkg/image/image.go new file mode 100644 index 0000000..a206f1c --- /dev/null +++ b/pkg/image/image.go @@ -0,0 +1,17 @@ +package image + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" +) + +// Structure for creating request to images +type Image struct { + client interfaces.Caller +} + +// Builder for images endpoints +func New(client interfaces.Caller) *Image { + return &Image{ + client, + } +} diff --git a/pkg/image/list.go b/pkg/image/list.go new file mode 100644 index 0000000..175cacd --- /dev/null +++ b/pkg/image/list.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/image/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/image/requests" +) + +// List gets a list of all images. +func (i Image) List(ctx context.Context, req requests.ListImageRequest) (*models.ListImage, error) { + + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListImage{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListRaw gets a list of all images as an array of bytes +func (i Image) ListRaw(ctx context.Context, req requests.ListImageRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/image" + + res, err := i.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/image/models/model_list.go b/pkg/image/models/model_list.go new file mode 100644 index 0000000..5cab838 --- /dev/null +++ b/pkg/image/models/model_list.go @@ -0,0 +1,35 @@ +package models + +import "time" + +// ListImage represents the response containing image data. +type ListImage struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []ItemImage `json:"items"` + HasMore bool `json:"has_more"` + RequestID string `json:"request_id"` +} + +// ItemImage represents the details of each image item. +type ItemImage struct { + Active bool `json:"active"` + Filename string `json:"filename"` + IsTemplateImage bool `json:"is_template_image"` + Created time.Time `json:"created"` + Md5 string `json:"md5"` + Status string `json:"status"` + Modified time.Time `json:"modified"` + ImageID int `json:"image_id"` + Deleted time.Time `json:"deleted"` + UUID string `json:"uuid"` + Name string `json:"name"` + Mark string `json:"mark"` + ShareID string `json:"share_id"` + Description string `json:"description"` + Size int `json:"size"` + ImageType string `json:"image_type"` + FileSize int `json:"file_size"` +} diff --git a/pkg/image/requests/request_list.go b/pkg/image/requests/request_list.go new file mode 100644 index 0000000..1fc7bb6 --- /dev/null +++ b/pkg/image/requests/request_list.go @@ -0,0 +1,65 @@ +package requests + +// ListImageRequest struct to get list of images +type ListImageRequest struct { + // Name of image + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Size of image + // Required: false + Size string `url:"size,omitempty" json:"size,omitempty"` + + // Type of image + // Required: false + ImageType string `url:"image_type,omitempty" json:"image_type,omitempty" validate:"omitempty,image_type"` + + // Status of image + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty" validate:"omitempty,image_status"` + + // ShareID of image + // Required: false + ShareID string `url:"share_id,omitempty" json:"share_id,omitempty"` + + // Active if True Image is active + // Required: false + Active interface{} `url:"active,omitempty" json:"active,omitempty" validate:"omitempty,is_bool"` + + // IsTemplateImage If True Image is template + // Required: false + // Default: False + IsTemplateImage bool `url:"is_template_image,omitempty" json:"is_template_image,omitempty"` + + // Mark of image + // Required: false + Mark string `url:"mark,omitempty" json:"mark,omitempty"` + + // image for filtering by text field + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // List columns which will be used by filter_text + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // List of columns for sorting. + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Visibility status of the interface (visible, all, deleted). + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` + + // Filter VM Images that are unavailable now, because share is unavailable + // Required: false + HideUnavailable interface{} `url:"hide_unavailable,omitempty" json:"hide_unavailable,omitempty" validate:"omitempty,is_bool"` + + // Number of the page to return. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items to return per page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} diff --git a/pkg/node.go b/pkg/node.go new file mode 100644 index 0000000..c909b7f --- /dev/null +++ b/pkg/node.go @@ -0,0 +1,10 @@ +package api + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/node" +) + +// Accessing the images method group +func (ca *API) Node() *node.Node { + return node.New(ca.client) +} diff --git a/pkg/node/list.go b/pkg/node/list.go new file mode 100644 index 0000000..8c00609 --- /dev/null +++ b/pkg/node/list.go @@ -0,0 +1,42 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/node/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/node/requests" +) + +// Returns a list of nodes for a specific node +func (n Node) List(ctx context.Context, req requests.ListNodesRequest) (*models.ListNodeResponse, error) { + + res, err := n.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListNodeResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListRaw gets a list of all nodes as an array of bytes +func (n Node) ListRaw(ctx context.Context, req requests.ListNodesRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/node" + + res, err := n.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/node/models/model_list.go b/pkg/node/models/model_list.go new file mode 100644 index 0000000..6e9b977 --- /dev/null +++ b/pkg/node/models/model_list.go @@ -0,0 +1,251 @@ +package models + +import ( + "encoding/json" + "time" +) + +type ListNodeResponse struct { + List ListNode `json:"node_list"` + Task interface{} `json:"task"` //TaskNode + RequestID string `json:"request_id"` +} + +type ListNode struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []ItemNode `json:"items"` + HasMore bool `json:"has_more"` +} + +type TaskNode struct { + Command string `json:"command"` + Started any `json:"started"` //I don't now type + Type string `json:"type"` + Snapshot string `json:"snapshot"` + ParentID int `json:"parent_id"` + Progress string `json:"progress"` + InitiatorID int `json:"initiator_id"` + TimeoutAt time.Time `json:"timeout_at"` + Error string `json:"error"` + Finished time.Time `json:"finished"` + Created time.Time `json:"created"` + Status string `json:"status"` + RequestID string `json:"request_id"` + HasChildren bool `json:"has_children"` + Initiator TaskInitiator `json:"initiator"` +} + +type TaskInitiator struct { + Name string `json:"name"` + UserID int `json:"user_id"` + Email string `json:"email"` + Login string `json:"login"` +} + +type ItemNode struct { + Deleted time.Time `json:"deleted"` + MasterVmsPath string `json:"master_vms_path"` + IPAddress string `json:"ip_address"` + Modified time.Time `json:"modified"` + Arch string `json:"arch"` + TrafficShaping bool `json:"traffic_shaping"` + VstorageHostID int `json:"vstorage_host_id"` + MemoryReservationMb int `json:"memory_reservation_mb"` + MemoryWeight int `json:"memory_weight"` + VstorageIPAddress string `json:"vstorage_ip_address"` + Status string `json:"status"` + Compute bool `json:"compute"` + HaCPUOvercommit float64 `json:"ha_cpu_overcommit"` + HaMemoryOvercommit float64 `json:"ha_memory_overcommit"` + Memory int `json:"memory"` + ImageCachePath string `json:"image_cache_path"` + VstorageMountPoints any `json:"vstorage_mount_points"` //I don't now type + Os string `json:"os"` + MemoryOvercommit float64 `json:"memory_overcommit"` + StorageStatus string `json:"storage_status"` + HwInfo HWInfo `json:"hw_info"` + Created time.Time `json:"created"` + VirtProduct string `json:"virt_product"` + ServerUUID string `json:"server_uuid"` + Deletable bool `json:"deletable"` + CPUReservation int `json:"cpu_reservation"` + Login string `json:"login"` + VstorageClusterID int `json:"vstorage_cluster_id"` + DiskMb float64 `json:"disk_mb"` + Name string `json:"name"` + HaEnabled bool `json:"ha_enabled"` + Metrics interface{} `json:"metrics"` //Metrics + Hostname string `json:"hostname"` + CPUWeight int `json:"cpu_weight"` + ResourceParameters ResourceParameters `json:"resource_parameters"` + NumaInfo NumaInfo `json:"numa_info"` + HaStatus string `json:"ha_status"` + AgentUpdateStatus string `json:"agent_update_status"` + ClusterID int `json:"cluster_id"` + NodeID int `json:"node_id"` + NodeStatusInHaCluster string `json:"node_status_in_ha_cluster"` + VstorageStatus string `json:"vstorage_status"` + VirtProductID string `json:"virt_product_id"` + Description string `json:"description"` + CPU int `json:"cpu"` + CPUOvercommit float64 `json:"cpu_overcommit"` + SdkStatus string `json:"sdk_status"` + VstorageIscsiRoot any `json:"vstorage_iscsi_root"` //I don't now type + MaintenanceStatus string `json:"maintenance_status"` + ClusterName string `json:"cluster_name"` + Shares Shares `json:"shares"` + SdkLicense interface{} `json:"sdk_license"` //SDKLicence + IsPasswordSet bool `json:"is_password_set"` + ResourceMetrics interface{} `json:"resource_metrics"` //ResourceMetrics + BlockdevPaths string `json:"blockdev_paths"` +} + +type HWInfo struct { + CPU struct { + Sockets int `json:"sockets"` + Cores int `json:"cores"` + Cpus int `json:"cpus"` + FrequencyMin float64 `json:"frequency_min"` + FrequencyMax float64 `json:"frequency_max"` + Model string `json:"model"` + } `json:"cpu"` + Memory struct { + SlotsCount int `json:"slots_count"` + UsedSlots []struct { + RAMSlot0 struct { + Size float64 `json:"size"` + } `json:"RAM slot #0"` + } `json:"used_slots"` + } `json:"memory"` + Disk []struct { + Name string `json:"name"` + Size string `json:"size"` + } `json:"disk"` +} + +type Metrics struct { + CPUUsagePercent float64 `json:"cpu_usage_percent"` + CombinedPartitionTotalMb float64 `json:"combined_partition_total_mb"` + SwapTotalMb float64 `json:"swap_total_mb"` + SwapUsedMb float64 `json:"swap_used_mb"` + MemoryFreeMb float64 `json:"memory_free_mb"` + SwapFreeMb float64 `json:"swap_free_mb"` + Modified time.Time `json:"modified"` + UptimeSec float64 `json:"uptime_sec"` + SwapUsagePercent float64 `json:"swap_usage_percent"` + MemoryTotalMb float64 `json:"memory_total_mb"` + CombinedPartitionUsageMb float64 `json:"combined_partition_usage_mb"` + CombinedPartitionUsagePercent float64 `json:"combined_partition_usage_percent"` + MemoryUsedMb float64 `json:"memory_used_mb"` + MemoryUsagePercent float64 `json:"memory_usage_percent"` +} + +type ResourceParameters struct { + VcmmdPolicy string `json:"vcmmd-policy"` + VstorageLimitMax int `json:"vstorage_limit_max"` + VMOvercommitMemory int `json:"vm.overcommit_memory"` + VMSwappiness int `json:"vm.swappiness"` + VMVfsCachePressure int `json:"vm.vfs_cache_pressure"` + VMMinFreeKbytes int `json:"vm.min_free_kbytes"` +} + +type NumaInfo struct { + Num0 []int `json:"0"` +} + +type Shares struct { + Hdd string `json:"hdd"` + Iso string `json:"iso"` +} + +type SDKLicense struct { + NrVms string `json:"nr_vms"` +} + +type ResourceMetrics struct { + MemoryFree float64 `json:"memory_free"` + RunningVmsCount int `json:"running_vms_count"` + SumUtilization float64 `json:"sum_utilization"` + CPUUtilization float64 `json:"cpu_utilization"` + CPUFree float64 `json:"cpu_free"` + MemoryUtilization float64 `json:"memory_utilization"` + CurrentCPUOvercommit float64 `json:"current_cpu_overcommit"` + CurrentMemoryOvercommit float64 `json:"current_memory_overcommit"` +} + +func (ln *ListNodeResponse) UnmarshalJSON(data []byte) error { + type Alias ListNodeResponse + temp := &struct { + TaskNode json.RawMessage `json:"task,omitempty"` + *Alias + }{ + Alias: (*Alias)(ln), + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + if len(temp.TaskNode) > 0 { + var task TaskNode + if err := json.Unmarshal(temp.TaskNode, &task); err != nil { + return err + } + ln.Task = task + } else { + ln.Task = nil + } + + return nil +} + +func (in *ItemNode) UnmarshalJSON(data []byte) error { + type Alias ItemNode + temp := &struct { + Metrics json.RawMessage `json:"metrics,omitempty"` + ResourceMetrics json.RawMessage `json:"resource_metrics,omitempty"` + SdkLicense json.RawMessage `json:"sdk_license,omitempty"` + *Alias + }{ + Alias: (*Alias)(in), + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + if len(temp.Metrics) > 0 { + var metrics Metrics + if err := json.Unmarshal(temp.Metrics, &metrics); err != nil { + return err + } + in.Metrics = metrics + } else { + in.Metrics = nil + } + + if len(temp.ResourceMetrics) > 0 { + var resourceMetrics ResourceMetrics + if err := json.Unmarshal(temp.ResourceMetrics, &resourceMetrics); err != nil { + return err + } + in.ResourceMetrics = resourceMetrics + } else { + in.ResourceMetrics = nil + } + + if len(temp.SdkLicense) > 0 { + var license SDKLicense + if err := json.Unmarshal(temp.SdkLicense, &license); err != nil { + return err + } + in.SdkLicense = license + } else { + in.SdkLicense = nil + } + + return nil +} diff --git a/pkg/node/node.go b/pkg/node/node.go new file mode 100644 index 0000000..3564f4f --- /dev/null +++ b/pkg/node/node.go @@ -0,0 +1,17 @@ +package node + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" +) + +// Structure for creating request to images +type Node struct { + client interfaces.Caller +} + +// Builder for images endpoints +func New(client interfaces.Caller) *Node { + return &Node{ + client, + } +} diff --git a/pkg/node/requests/request_list.go b/pkg/node/requests/request_list.go new file mode 100644 index 0000000..199afaa --- /dev/null +++ b/pkg/node/requests/request_list.go @@ -0,0 +1,130 @@ +package requests + +import "time" + +// ListNodesRequest struct to filter nodes based on given parameters +type ListNodesRequest struct { + // Name of the node to filter by partial matching. Ignored if NameExact is provided. + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Exact name of the node to filter. + // Required: false + NameExact string `url:"name_exact,omitempty" json:"name_exact,omitempty"` + + // Cluster ID where the node is attached. + // Required: false + ClusterID int `url:"cluster_id,omitempty" json:"cluster_id,omitempty"` + + // VStorage cluster ID where the node is attached. + // Required: false + VStorageClusterID int `url:"vstorage_cluster_id,omitempty" json:"vstorage_cluster_id,omitempty"` + + // Include nodes that do not belong to any cluster or VStorage cluster. + // Required: false + AddWithoutCluster bool `url:"add_without_cluster,omitempty" json:"add_without_cluster,omitempty"` + + // Online status filter (multiple choices). + // Required: false + Status []string `url:"status,omitempty" json:"status,omitempty"` + + // Maintenance status filter (multiple choices). + // Required: false + MaintenanceStatus []string `url:"maintenance_status,omitempty" json:"maintenance_status,omitempty" validate:"omitempty,maintenance_status"` + + // Agent update status filter (multiple choices). + // Required: false + AgentUpdateStatus []string `url:"agent_update_status,omitempty" json:"agent_update_status,omitempty" validate:"omitempty,agent_update_status"` + + // Name of the node’s cluster. + // Required: false + ClusterName string `url:"cluster_name,omitempty" json:"cluster_name,omitempty"` + + // Text to search within the nodes. + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // Columns to be used for filtering based on FilterText. + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // OS type filter for the node. + // Required: false + OS string `url:"os,omitempty" json:"os,omitempty"` + + // HA status filter (multiple choices). + // Required: false + HAStatus []string `url:"ha_status,omitempty" json:"ha_status,omitempty" validate:"omitempty,ha_multi_status"` + + // HA status in Fenix cluster filter (multiple choices). + // Required: false + NodeStatusInHACluster []string `url:"node_status_in_ha_cluster,omitempty" json:"node_status_in_ha_cluster,omitempty" validate:"omitempty,node_status_in_ha_cluster"` + + // Filter by IP address. + // Required: false + IPAddress string `url:"ip_address,omitempty" json:"ip_address,omitempty"` + + // Filter by node architecture. + // Required: false + Arch string `url:"arch,omitempty" json:"arch,omitempty"` + + // Filter entities created before this date. + // Required: false + CreatedBefore time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Filter entities created after this date. + // Required: false + CreatedAfter time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` + + // Include node’s current metrics in the response. + // Required: false + WithMetrics bool `url:"with_metrics,omitempty" json:"with_metrics,omitempty"` + + // Include current resource scheduler metrics info. + // Required: false + WithResourceMetrics bool `url:"with_resource_metrics,omitempty" json:"with_resource_metrics,omitempty"` + + // Include external resource’s nodes. + // Required: false + IncludeExternal bool `url:"include_external,omitempty" json:"include_external,omitempty"` + + // Include nodes with enabled traffic shaping. + // Required: false + TrafficShaping bool `url:"traffic_shaping,omitempty" json:"traffic_shaping,omitempty"` + + // List of node IDs to exclude from the listing. + // Required: false + ExcludeIDs []int `url:"exclude_ids,omitempty" json:"exclude_ids,omitempty"` + + // Column sorting order. + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Visibility options: "visible" (default), "deleted", or "all". + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` + + // Number of the page to return. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items to return per page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` + + // Show SDK license information. + // Required: false + WithLicense bool `url:"with_license,omitempty" json:"with_license,omitempty"` + + // Storage status filter. + // Required: false + StorageStatus string `url:"storage_status,omitempty" json:"storage_status,omitempty"` + + // List of virtual network IDs to filter nodes by. + // Required: false + VirtualNetworkIDs []int `url:"virtual_network_ids,omitempty" json:"virtual_network_ids,omitempty"` + + // List of external storage IDs to filter nodes by. Only READY storages are included. + // Required: false + ExternalStorageIDs []int `url:"external_storage_ids,omitempty" json:"external_storage_ids,omitempty"` +} diff --git a/pkg/respool.go b/pkg/respool.go new file mode 100644 index 0000000..93aa287 --- /dev/null +++ b/pkg/respool.go @@ -0,0 +1,10 @@ +package api + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/respool" +) + +// Accessing the Resource pool method group +func (ca *API) ResourcePool() *respool.ResourcePool { + return respool.New(ca.client) +} diff --git a/pkg/respool/list.go b/pkg/respool/list.go new file mode 100644 index 0000000..ac09d10 --- /dev/null +++ b/pkg/respool/list.go @@ -0,0 +1,42 @@ +package respool + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/respool/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/respool/requests" +) + +// List gets a list of all resource pools +func (rp ResourcePool) List(ctx context.Context, req requests.ListResPoolRequest) (*models.ListResPoolResponse, error) { + + res, err := rp.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListResPoolResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListRaw gets a list of all resource pools as an array of bytes +func (rp ResourcePool) ListRaw(ctx context.Context, req requests.ListResPoolRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/resource_pool" + + res, err := rp.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/respool/models/model_list.go b/pkg/respool/models/model_list.go new file mode 100644 index 0000000..ebd5906 --- /dev/null +++ b/pkg/respool/models/model_list.go @@ -0,0 +1,38 @@ +package models + +import "time" + +type ListResPoolResponse struct { + List ListResPool `json:"resource_pool_list"` + RequestID string `json:"request_id"` +} + +type ListResPool struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []ItemResPool `json:"items"` + HasMore bool `json:"has_more"` +} + +type ItemResPool struct { + RAMLimitMb int `json:"ram_limit_mb"` + DiskLimitMb int `json:"disk_limit_mb"` + ClusterID int `json:"cluster_id"` + ExternalUUID string `json:"external_uuid"` + ResourcePoolID int `json:"resource_pool_id"` + Lock bool `json:"lock"` + Deleted time.Time `json:"deleted"` + Name string `json:"name"` + PrimaryExternalStorageID int `json:"primary_external_storage_id"` + ExternalResourceID any `json:"external_resource_id"` + CPULimit int `json:"cpu_limit"` + NodeCount int `json:"node_count"` + Created time.Time `json:"created"` + CPUUsed int `json:"cpu_used"` + RAMUsedMb int `json:"ram_used_mb"` + DiskUsedMb int `json:"disk_used_mb"` + ClusterName string `json:"cluster_name"` + ExternalStorageIds []int `json:"external_storage_ids"` +} diff --git a/pkg/respool/requests/request_list.go b/pkg/respool/requests/request_list.go new file mode 100644 index 0000000..46e985e --- /dev/null +++ b/pkg/respool/requests/request_list.go @@ -0,0 +1,70 @@ +package requests + +import "time" + +// ListResPoolRequest represents the request for retrieving a list of resource pools. +type ListResPoolRequest struct { + // Resource pool name. Searching by partial matching. Ignored if NameExact is provided. + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Exact resource pool name. + // Required: false + NameExact string `url:"name_exact,omitempty" json:"name_exact,omitempty"` + + // Cluster ID. + // Required: false + ClusterID int `url:"cluster_id,omitempty" json:"cluster_id,omitempty"` + + // Cluster name. + // Required: false + ClusterName string `url:"cluster_name,omitempty" json:"cluster_name,omitempty"` + + // Filter resource pools by lock status. + // Required: false + Lock interface{} `url:"lock,omitempty" json:"lock,omitempty" validate:"omitempty,is_bool"` + + // List of resource pool IDs to exclude from listing. + // Required: false + ExcludeIDs []int `url:"exclude_ids,omitempty" json:"exclude_ids,omitempty"` + + // External resource ID where the resource pool is located. + // Required: false + ExternalResourceID int `url:"external_resource_id,omitempty" json:"external_resource_id,omitempty"` + + // Include external resource pools for filtering or not. Default: false. + // Required: false + IncludeExternal bool `url:"include_external,omitempty" json:"include_external,omitempty"` + + // Template for filtering by any text field. + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // List of columns that will be used by FilterText. + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // Filter entities created before this date. + // Required: false + CreatedBefore time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Filter entities created after this date. + // Required: false + CreatedAfter time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` + + // Field name or list of field names used for sorting. Ascending sort is default. For descending sort, use "-" before the name. + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Visibility options: "visible" - Only active resource pools, "deleted" - Only removed resource pools, "all" - All resource pools. + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` + + // Number of the returning page. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items on the page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} diff --git a/pkg/respool/resource_pool.go b/pkg/respool/resource_pool.go new file mode 100644 index 0000000..3f860ec --- /dev/null +++ b/pkg/respool/resource_pool.go @@ -0,0 +1,17 @@ +package respool + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" +) + +// Structure for creating request to resource pools +type ResourcePool struct { + client interfaces.Caller +} + +// Builder for file share endpoints +func New(client interfaces.Caller) *ResourcePool { + return &ResourcePool{ + client, + } +} diff --git a/pkg/template.go b/pkg/template.go new file mode 100644 index 0000000..093a9fe --- /dev/null +++ b/pkg/template.go @@ -0,0 +1,8 @@ +package api + +import "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/template" + +// Accessing the Template method group +func (ca *API) Template() *template.Template { + return template.New(ca.client) +} diff --git a/pkg/template/get.go b/pkg/template/get.go new file mode 100644 index 0000000..c2ea3a2 --- /dev/null +++ b/pkg/template/get.go @@ -0,0 +1,42 @@ +package template + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/template/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/template/requests" +) + +// Get return information about specified template +func (t Template) Get(ctx context.Context, req requests.GetTemplateRequest) (*models.RecordTemplateResponse, error) { + res, err := t.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.RecordTemplateResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// GetRaw return information about specified template an array of bytes +func (t Template) GetRaw(ctx context.Context, req requests.GetTemplateRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/template/" + fmt.Sprint(req.TemplateID) + + res, err := t.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/template/list.go b/pkg/template/list.go new file mode 100644 index 0000000..21e0eb8 --- /dev/null +++ b/pkg/template/list.go @@ -0,0 +1,41 @@ +package template + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/template/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/template/requests" +) + +// List gets a list of all templates the user has access to a ListAccounts struct +func (t Template) List(ctx context.Context, req requests.ListTemplateRequest) (*models.ListTemplate, error) { + res, err := t.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListTemplate{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListRaw gets a list of all templates the user has access to as an array of bytes +func (t Template) ListRaw(ctx context.Context, req requests.ListTemplateRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv1 + "/template" + + res, err := t.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/template/models/model_get.go b/pkg/template/models/model_get.go new file mode 100644 index 0000000..5f327cc --- /dev/null +++ b/pkg/template/models/model_get.go @@ -0,0 +1,110 @@ +package models + +import "time" + +type RecordTemplateResponse struct { + TemplateInfo RecordTemplate `json:"template_info"` + RequestID string `json:"request_id"` +} + +type RecordTemplate struct { + CPULimit int `json:"cpu_limit"` + Status string `json:"status"` + IoPriority int `json:"io_priority"` + HaStatus string `json:"ha_status"` + Description string `json:"description"` + NodeMask string `json:"node_mask"` + Rates any `json:"rates"` + IsInfrastructure bool `json:"is_infrastructure"` + HaEnabled bool `json:"ha_enabled"` + FolderID int `json:"folder_id"` + SystemFlags string `json:"system_flags"` + UsedByDesktop bool `json:"used_by_desktop"` + Location string `json:"location"` + GuestOsVersion string `json:"guest_os_version"` + MaxRAMSize int `json:"max_ram_size"` + ExternalUUID string `json:"external_uuid"` + Domain string `json:"domain"` + RAMHotplugEnabled bool `json:"ram_hotplug_enabled"` + VirtType string `json:"virt_type"` + Created time.Time `json:"created"` + CPUUnits int `json:"cpu_units"` + Ratebound bool `json:"ratebound"` + InstallOs bool `json:"install_os"` + MonitoringEnabled bool `json:"monitoring_enabled"` + RAMSize int `json:"ram_size"` + Maxmemory int `json:"maxmemory"` + Name string `json:"name"` + GuestOs string `json:"guest_os"` + CPUSockets int `json:"cpu_sockets"` + AutoStartDelay int `json:"auto_start_delay"` + CoresCount int `json:"cores_count"` + GuestToolsState string `json:"guest_tools_state"` + Deleted time.Time `json:"deleted"` + ResourcePoolID int `json:"resource_pool_id"` + SmbiosUUID string `json:"smbios_uuid"` + CPUMask string `json:"cpu_mask"` + CPUCount int `json:"cpu_count"` + MacAddresses any `json:"mac_addresses"` + IsLinkedClone bool `json:"is_linked_clone"` + BootDeviceList []BootDevice `json:"boot_device_list"` + IopsLimit int `json:"iops_limit"` + VncHostname string `json:"vnc_hostname"` + Imported bool `json:"imported"` + ExternalStorageName string `json:"external_storage_name"` + CreationDate time.Time `json:"creation_date"` + CPUHotplug bool `json:"cpu_hotplug"` + MemGuaranteeSize int `json:"mem_guarantee_size"` + LinkedVMID int `json:"linked_vm_id"` + ParentUUID string `json:"parent_uuid"` + IsVzTemplate bool `json:"is_vz_template"` + IoLimit int `json:"io_limit"` + Chipset string `json:"chipset"` + VncMode string `json:"vnc_mode"` + DisableAutobalance bool `json:"disable_autobalance"` + ExternalStorageID int `json:"external_storage_id"` + ClusterID int `json:"cluster_id"` + HaPriority int `json:"ha_priority"` + VMID int `json:"vm_id"` + ClockOffset string `json:"clock_offset"` + Lock bool `json:"lock"` + RAMBalloonActualHotplug bool `json:"ram_balloon_actual_hotplug"` + VncPassword string `json:"vnc_password"` + ExternalResourceName string `json:"external_resource_name"` + RAMTotalHotplug int `json:"ram_total_hotplug"` + SecureBoot bool `json:"secure_boot"` + ExternalResourceID int `json:"external_resource_id"` + Affinity string `json:"affinity"` + IsTemplate bool `json:"is_template"` + NodeID int `json:"node_id"` + VncPort int `json:"vnc_port"` + LinkedVMUUID string `json:"linked_vm_uuid"` + VideoRAMSize int `json:"video_ram_size"` + AutoStop string `json:"auto_stop"` + TpmEnabled bool `json:"tpm_enabled"` + UUID string `json:"uuid"` + IPAddress string `json:"ip_address"` + Hostname string `json:"hostname"` + EfiEnabled bool `json:"efi_enabled"` + AutoStart string `json:"auto_start"` + LinkedCloneCount int `json:"linked_clone_count"` + SnapshotCount int `json:"snapshot_count"` + HostDeviceCount int `json:"host_device_count"` + Node Node `json:"node"` + AllMd5 bool `json:"all_md5"` + DisksSize int `json:"disks_size"` + TemplateSize float64 `json:"template_size"` + FullHardDriveSize int `json:"full_hard_drive_size"` + UsedHardDriveSize float64 `json:"used_hard_drive_size"` + ParentTree []ParentTree `json:"parent_tree"` +} + +// ParentTree represents the parent tree information. +type ParentTree struct { + VMID int `json:"vm_id"` + Name string `json:"name"` + UUID string `json:"uuid"` + ParentID int `json:"parent_id"` + IsTemplate bool `json:"is_template"` + Depth int `json:"depth"` +} diff --git a/pkg/template/models/model_list.go b/pkg/template/models/model_list.go new file mode 100644 index 0000000..422b8a3 --- /dev/null +++ b/pkg/template/models/model_list.go @@ -0,0 +1,117 @@ +package models + +import "time" + +type ListTemplate struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []ItemTemplate `json:"items"` + HasMore bool `json:"has_more"` + RequestID string `json:"request_id"` +} + +type ItemTemplate struct { + CPULimit int `json:"cpu_limit"` + Status string `json:"status"` + IoPriority int `json:"io_priority"` + HaStatus string `json:"ha_status"` + Description string `json:"description"` + NodeMask string `json:"node_mask"` + Rates any `json:"rates"` + IsInfrastructure bool `json:"is_infrastructure"` + HaEnabled bool `json:"ha_enabled"` + FolderID int `json:"folder_id"` + SystemFlags string `json:"system_flags"` + UsedByDesktop bool `json:"used_by_desktop"` + Location string `json:"location"` + GuestOsVersion string `json:"guest_os_version"` + MaxRAMSize int `json:"max_ram_size"` + ExternalUUID string `json:"external_uuid"` + Domain string `json:"domain"` + RAMHotplugEnabled bool `json:"ram_hotplug_enabled"` + VirtType string `json:"virt_type"` + Created time.Time `json:"created"` + CPUUnits int `json:"cpu_units"` + Ratebound bool `json:"ratebound"` + InstallOs bool `json:"install_os"` + MonitoringEnabled bool `json:"monitoring_enabled"` + RAMSize int `json:"ram_size"` + Maxmemory int `json:"maxmemory"` + Name string `json:"name"` + GuestOs string `json:"guest_os"` + CPUSockets int `json:"cpu_sockets"` + AutoStartDelay int `json:"auto_start_delay"` + CoresCount int `json:"cores_count"` + GuestToolsState string `json:"guest_tools_state"` + Deleted time.Time `json:"deleted"` + ResourcePoolID int `json:"resource_pool_id"` + SmbiosUUID string `json:"smbios_uuid"` + CPUMask string `json:"cpu_mask"` + CPUCount int `json:"cpu_count"` + MacAddresses any `json:"mac_addresses"` + IsLinkedClone bool `json:"is_linked_clone"` + BootDeviceList []BootDevice `json:"boot_device_list"` + IopsLimit int `json:"iops_limit"` + VncHostname string `json:"vnc_hostname"` + Imported bool `json:"imported"` + ExternalStorageName string `json:"external_storage_name"` + CreationDate time.Time `json:"creation_date"` + CPUHotplug bool `json:"cpu_hotplug"` + MemGuaranteeSize int `json:"mem_guarantee_size"` + LinkedVMID int `json:"linked_vm_id"` + ParentUUID string `json:"parent_uuid"` + IsVzTemplate bool `json:"is_vz_template"` + IoLimit int `json:"io_limit"` + Chipset string `json:"chipset"` + VncMode string `json:"vnc_mode"` + DisableAutobalance bool `json:"disable_autobalance"` + ExternalStorageID int `json:"external_storage_id"` + ClusterID int `json:"cluster_id"` + HaPriority int `json:"ha_priority"` + VMID int `json:"vm_id"` + ClockOffset string `json:"clock_offset"` + Lock bool `json:"lock"` + RAMBalloonActualHotplug bool `json:"ram_balloon_actual_hotplug"` + VncPassword string `json:"vnc_password"` + ExternalResourceName string `json:"external_resource_name"` + RAMTotalHotplug string `json:"ram_total_hotplug"` + SecureBoot bool `json:"secure_boot"` + ExternalResourceID int `json:"external_resource_id"` + Affinity string `json:"affinity"` + IsTemplate bool `json:"is_template"` + NodeID int `json:"node_id"` + VncPort int `json:"vnc_port"` + LinkedVMUUID string `json:"linked_vm_uuid"` + VideoRAMSize int `json:"video_ram_size"` + AutoStop string `json:"auto_stop"` + TpmEnabled bool `json:"tpm_enabled"` + UUID string `json:"uuid"` + IPAddress string `json:"ip_address"` + Hostname string `json:"hostname"` + EfiEnabled bool `json:"efi_enabled"` + AutoStart string `json:"auto_start"` + LinkedCloneCount int `json:"linked_clone_count"` + SnapshotCount int `json:"snapshot_count"` + HostDeviceCount int `json:"host_device_count"` + Node Node `json:"node"` + AllMd5 bool `json:"all_md5"` + DisksSize int `json:"disks_size"` + TemplateSize float64 `json:"template_size"` + FullHardDriveSize int `json:"full_hard_drive_size"` + UsedHardDriveSize float64 `json:"used_hard_drive_size"` +} + +// BootDevice represents the boot device information. +type BootDevice struct { + Type string `json:"type"` + InUse bool `json:"in_use"` + Index int `json:"index"` +} + +// Node represents the node information. +type Node struct { + Name string `json:"name"` + NodeID int `json:"node_id"` +} diff --git a/pkg/template/requests/request_get.go b/pkg/template/requests/request_get.go new file mode 100644 index 0000000..94c6421 --- /dev/null +++ b/pkg/template/requests/request_get.go @@ -0,0 +1,12 @@ +package requests + +// GetTemplateRequest struct to get details of a template. +type GetTemplateRequest struct { + // Id of source template + // Required: true + TemplateID int `url:"template" json:"template" validate:"required"` + + // If true, include VM's hierarchy information. + // Required: false + WithParentTree bool `url:"with_parent_tree,omitempty" json:"with_parent_tree,omitempty"` +} diff --git a/pkg/template/requests/request_list.go b/pkg/template/requests/request_list.go new file mode 100644 index 0000000..33b9c85 --- /dev/null +++ b/pkg/template/requests/request_list.go @@ -0,0 +1,134 @@ +package requests + +import "time" + +// ListTemplateRequest struct to get list of templates +type ListTemplateRequest struct { + // Find by partial name match. Ignored if NameExact is provided. + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by virtualization type. Possible values: "VM", "CT". + // Required: false + VirtType string `url:"virt_type,omitempty" json:"virt_type,omitempty" validate:"omitempty,virt_type"` + + // Find by node identifier,. + // Required: false + Node int `url:"node,omitempty" json:"node,omitempty"` + + // Filter templates by description. + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Filter by OS installation. + // Required: false + InstallOS interface{} `url:"install_os,omitempty" json:"install_os,omitempty" validate:"omitempty,is_bool"` + + // Filter templates by CPU count. + // Required: false + CPUCount int `url:"cpu_count,omitempty" json:"cpu_count,omitempty"` + + // Filter templates by external resource ID. + // Required: false + ExternalResourceID int `url:"external_resource_id,omitempty" json:"external_resource_id,omitempty"` + + // Include external resource pools for filtering. + // Required: false + IncludeExternal bool `url:"include_external,omitempty" json:"include_external,omitempty"` + + // Filter templates by CPU units (2-262144). + // Required: false + CPUUnits int `url:"cpu_units,omitempty" json:"cpu_units,omitempty" validate:"omitempty,cpu_units"` + + // Filter templates by CPU limit (0-100) * number of cores on server. + // Required: false + CPULimit int `url:"cpu_limit,omitempty" json:"cpu_limit,omitempty" validate:"omitempty,cpu_limit"` + + // Filter templates by RAM size in Megabytes. + // Required: false + RAMSize int `url:"ram_size,omitempty" json:"ram_size,omitempty"` + + // Filter templates by video RAM size in Megabytes. + // Required: false + VideoRAMSize int `url:"video_ram_size,omitempty" json:"video_ram_size,omitempty"` + + // Filter templates by Guest OS. + // Required: false + GuestOS string `url:"guest_os,omitempty" json:"guest_os,omitempty" validate:"omitempty,guest_os"` + + // Filter templates by Guest OS version. + // Required: false + GuestOSVersion string `url:"guest_os_version,omitempty" json:"guest_os_version,omitempty" validate:"omitempty,guest_os_version"` + + // Filter templates by location. + // Required: false + Location string `url:"location,omitempty" json:"location,omitempty"` + + // Filter templates by lock status. + // Required: false + Lock interface{} `url:"lock,omitempty" json:"lock,omitempty" validate:"omitempty,is_bool"` + + // Filter templates by status. + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty" validate:"omitempty,template_status"` + + // Filter templates by EFI usage. + // Required: false + EFIEnabled interface{} `url:"efi_enabled,omitempty" json:"efi_enabled,omitempty" validate:"omitempty,is_bool"` + + // Filter templates by RAM hotplug enabled. + // Required: false + RamHotplugEnabled interface{} `url:"ram_hotplug_enabled,omitempty" json:"ram_hotplug_enabled,omitempty" validate:"omitempty,is_bool"` + + // Set memory guarantee size as a percentage of VM RAM. + // Required: false + MemGuaranteeSize int `url:"mem_guarantee_size,omitempty" json:"mem_guarantee_size,omitempty" validate:"omitempty,mem_guarantee_size"` + + // Filter templates by parent UUID. + // Required: false + ParentUUID string `url:"parent_uuid,omitempty" json:"parent_uuid,omitempty"` + + // Filter to search in all text fields. + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // List columns which will be used by FilterText. + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // Filter templates deleted before this date. + // Required: false + DeletedBefore time.Time `url:"deleted_before,omitempty" json:"deleted_before,omitempty"` + + // Filter templates deleted after this date. + // Required: false + DeletedAfter time.Time `url:"deleted_after,omitempty" json:"deleted_after,omitempty"` + + // Filter templates created before this date. + // Required: false + CreatedBefore time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Filter templates created after this date. + // Required: false + CreatedAfter time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` + + // Filter VM templates that are unavailable now, because share is unavailable + // Required: false + HideUnavailable interface{} `url:"hide_unavailable,omitempty" json:"hide_unavailable,omitempty" validate:"omitempty,is_bool"` + + // Number of returning page. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items on page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` + + // Column sorting, format +|-(field). + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Filter template by visibility. Possible values: "visible", "deleted", "all". + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` +} diff --git a/pkg/template/template.go b/pkg/template/template.go new file mode 100644 index 0000000..85933d6 --- /dev/null +++ b/pkg/template/template.go @@ -0,0 +1,17 @@ +package template + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" +) + +// Structure for creating request to template +type Template struct { + client interfaces.Caller +} + +// Builder for vms endpoints +func New(client interfaces.Caller) *Template { + return &Template{ + client, + } +} diff --git a/pkg/vm.go b/pkg/vm.go new file mode 100644 index 0000000..464e7bf --- /dev/null +++ b/pkg/vm.go @@ -0,0 +1,8 @@ +package api + +import "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm" + +// Accessing the VM method group +func (ca *API) VM() *vm.VM { + return vm.New(ca.client) +} diff --git a/pkg/vm/common/power.go b/pkg/vm/common/power.go new file mode 100644 index 0000000..6c1b7aa --- /dev/null +++ b/pkg/vm/common/power.go @@ -0,0 +1,25 @@ +package common + +// PowerVMRequest struct to power on or off a virtual machine. +type PowerVMRequest struct { + // Id of source VM + // Required: true + VmID int `url:"vm" json:"vm" validate:"required"` + + // Command to power on or off the virtual machine. + // Required: true + Command string `url:"command" json:"command" validate:"required"` + + // If true, attempt a graceful operation of the virtual machine. + // Required: false + Graceful bool `url:"graceful" json:"graceful,omitempty"` + + // If true, force the operation of the virtual machine. + // Required: false + Force bool `url:"force" json:"force,omitempty"` +} + +const ( + PowerOn = "vm_start" + PowerOff = "vm_stop" +) diff --git a/pkg/vm/create.go b/pkg/vm/create.go new file mode 100644 index 0000000..796604a --- /dev/null +++ b/pkg/vm/create.go @@ -0,0 +1,84 @@ +package vm + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" +) + +type wrapperCreateVMRequest struct { + requests.CreateVMRequest + BootDevices []string `url:"boot_device,omitempty"` + Device []string `url:"device,omitempty"` +} + +// Create new VM +func (vm VM) Create(ctx context.Context, req requests.CreateVMRequest) (*models.CreateVMResponse, error) { + if len(req.Names) == 0 { + req.Names = []string{req.Name} + } + + res, err := vm.CreateRaw(ctx, req) + if err != nil { + return nil, err + } + + item := models.CreateVMResponse{} + + err = json.Unmarshal(res, &item) + if err != nil { + return nil, err + } + return &item, nil +} + +// CreateRaw return information about created VMas an array of bytes +func (vm VM) CreateRaw(ctx context.Context, req requests.CreateVMRequest) ([]byte, error) { + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + var bootDevices []string + + if req.BootDeviceList != nil && len(req.BootDeviceList) != 0 { + bootDevices = make([]string, 0, len(req.BootDeviceList)) + + for i := range req.BootDeviceList { + d, err := json.Marshal(req.BootDeviceList[i]) + if err != nil { + return nil, err + } + bootDevices = append(bootDevices, string(d)) + } + } + + var devices []string + + if req.Devices != nil && len(req.Devices) != 0 { + devices = make([]string, 0, len(req.Devices)) + + for i := range req.Devices { + d, err := json.Marshal(req.Devices[i]) + if err != nil { + return nil, err + } + devices = append(devices, string(d)) + } + } + + reqWrapped := wrapperCreateVMRequest{ + CreateVMRequest: req, + BootDevices: bootDevices, + Device: devices, + } + + url := constants.APIv0 + "/vms" + + res, err := vm.client.ApiCall(ctx, http.MethodPost, url, reqWrapped) + return res, err +} diff --git a/pkg/vm/get.go b/pkg/vm/get.go new file mode 100644 index 0000000..b70d5cf --- /dev/null +++ b/pkg/vm/get.go @@ -0,0 +1,43 @@ +package vm + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" +) + +// Get return information about specified VM +func (vm VM) Get(ctx context.Context, req requests.GetVMRequest) (*models.RecordVMResponse, error) { + + res, err := vm.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + item := models.RecordVMResponse{} + + err = json.Unmarshal(res, &item) + if err != nil { + return nil, err + } + return &item, nil +} + +// GetRaw return information about specified VMas an array of bytes +func (vm VM) GetRaw(ctx context.Context, req requests.GetVMRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/vm/" + fmt.Sprint(req.VmID) + + res, err := vm.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/vm/get_disks.go b/pkg/vm/get_disks.go new file mode 100644 index 0000000..548b1a9 --- /dev/null +++ b/pkg/vm/get_disks.go @@ -0,0 +1,43 @@ +package vm + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" +) + +// GetDisks return information about disks of the specified VM +func (vm VM) GetDisks(ctx context.Context, req requests.GetDisksRequest) (*models.ListDiskResponse, error) { + + res, err := vm.GetDisksRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.ListDiskResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// GetDisksRaw return information about disks of the specified VM an array of bytes +func (vm VM) GetDisksRaw(ctx context.Context, req requests.GetDisksRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/vm/" + fmt.Sprint(req.VmID) + "/device/hard_disk" + + res, err := vm.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/vm/list.go b/pkg/vm/list.go new file mode 100644 index 0000000..7469a47 --- /dev/null +++ b/pkg/vm/list.go @@ -0,0 +1,45 @@ +package vm + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" +) + +// List gets a list of all vms the user has access to a ListAccounts struct +func (vm VM) List(ctx context.Context, req requests.ListVMRequest) (*models.ListVMResponse, error) { + res, err := vm.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + if req.FolderSubtree == nil { + req.FolderSubtree = true + } + + list := models.ListVMResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// ListRaw gets a list of all vms the user has access to as an array of bytes +func (vm VM) ListRaw(ctx context.Context, req requests.ListVMRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/vm" + + res, err := vm.client.ApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/vm/models/model_create.go b/pkg/vm/models/model_create.go new file mode 100644 index 0000000..1497709 --- /dev/null +++ b/pkg/vm/models/model_create.go @@ -0,0 +1,32 @@ +package models + +import "time" + +type CreateVMResponse struct { + Task CreateTask `json:"task"` + VMIds []int `json:"vm_ids"` + RequestID string `json:"request_id"` +} + +type CreateTask struct { + Error string `json:"error"` + Snapshot string `json:"snapshot"` + ParentID int `json:"parent_id"` + Status string `json:"status"` + Created time.Time `json:"created"` + Progress string `json:"progress"` + Command string `json:"command"` + Finished time.Time `json:"finished"` + Type string `json:"type"` + TimeoutAt time.Time `json:"timeout_at"` + InitiatorID int `json:"initiator_id"` + RequestID string `json:"request_id"` + Started time.Time `json:"started"` + VMID int `json:"vm_id"` + NodeID int `json:"node_id"` + HasChildren bool `json:"has_children"` + Initiator Initiator `json:"initiator"` + Node TaskNode `json:"node"` + Cluster Cluster `json:"cluster"` + VM VM `json:"vm"` +} diff --git a/pkg/vm/models/model_get.go b/pkg/vm/models/model_get.go new file mode 100644 index 0000000..9af8a81 --- /dev/null +++ b/pkg/vm/models/model_get.go @@ -0,0 +1,176 @@ +package models + +import ( + "encoding/json" + "time" +) + +// RecordVMResponse represents the virtual machine information. +type RecordVMResponse struct { + VMInfo RecordVM `json:"vm_info"` + RequestID string `json:"request_id"` +} + +// RecordVM represents the detailed virtual machine information. +type RecordVM struct { + Domain string `json:"domain"` + Status string `json:"status"` + VMID int `json:"vm_id"` + SystemFlags string `json:"system_flags"` + IopsLimit int `json:"iops_limit"` + IsInfrastructure bool `json:"is_infrastructure"` + TpmEnabled bool `json:"tpm_enabled"` + SecureBoot bool `json:"secure_boot"` + Created time.Time `json:"created"` + ResourcePoolID int `json:"resource_pool_id"` + ExternalStorageID int `json:"external_storage_id"` + AutoStartDelay int `json:"auto_start_delay"` + GuestOsVersion string `json:"guest_os_version"` + RAMSize int `json:"ram_size"` + IsTemplate bool `json:"is_template"` + Description string `json:"description"` + RAMHotplugEnabled bool `json:"ram_hotplug_enabled"` + AutoStart string `json:"auto_start"` + MemGuaranteeSize int `json:"mem_guarantee_size"` + RAMTotalHotplug int `json:"ram_total_hotplug"` + Maxmemory int `json:"maxmemory"` + HaPriority int `json:"ha_priority"` + IoPriority int `json:"io_priority"` + CPUSockets int `json:"cpu_sockets"` + NodeMask string `json:"node_mask"` + MaxRAMSize int `json:"max_ram_size"` + Name string `json:"name"` + CPUMask string `json:"cpu_mask"` + NodeID int `json:"node_id"` + BootDeviceList []BootDevice `json:"boot_device_list"` + DisableAutobalance bool `json:"disable_autobalance"` + CPUUnits int `json:"cpu_units"` + SmbiosUUID string `json:"smbios_uuid"` + InstallOs bool `json:"install_os"` + UsedByDesktop bool `json:"used_by_desktop"` + CPUCount int `json:"cpu_count"` + VirtType string `json:"virt_type"` + VncPassword string `json:"vnc_password"` + VncHostname string `json:"vnc_hostname"` + Ratebound bool `json:"ratebound"` + EfiEnabled bool `json:"efi_enabled"` + Affinity string `json:"affinity"` + GuestOs string `json:"guest_os"` + CPUHotplug bool `json:"cpu_hotplug"` + Imported bool `json:"imported"` + ExternalResourceName string `json:"external_resource_name"` + CoresCount int `json:"cores_count"` + AutoStop string `json:"auto_stop"` + Hostname string `json:"hostname"` + VideoRAMSize int `json:"video_ram_size"` + HaStatus string `json:"ha_status"` + MacAddresses []string `json:"mac_addresses"` + ClusterID int `json:"cluster_id"` + ParentUUID string `json:"parent_uuid"` + ClockOffset string `json:"clock_offset"` + IsLinkedClone bool `json:"is_linked_clone"` + GuestToolsState string `json:"guest_tools_state"` + VncMode string `json:"vnc_mode"` + ExternalUUID string `json:"external_uuid"` + Deleted time.Time `json:"deleted"` + LinkedVMUUID string `json:"linked_vm_uuid"` + RAMBalloonActualHotplug int `json:"ram_balloon_actual_hotplug"` + IPAddress string `json:"ip_address"` + Rates any `json:"rates"` //I don't now type + UUID string `json:"uuid"` + CPULimit int `json:"cpu_limit"` + Location string `json:"location"` + CreationDate time.Time `json:"creation_date"` + VncPort int `json:"vnc_port"` + MonitoringEnabled bool `json:"monitoring_enabled"` + ExternalStorageName string `json:"external_storage_name"` + HaEnabled bool `json:"ha_enabled"` + IoLimit int `json:"io_limit"` + ExternalResourceID int `json:"external_resource_id"` + FolderID int `json:"folder_id"` + Lock bool `json:"lock"` + IsVzTemplate bool `json:"is_vz_template"` + Chipset string `json:"chipset"` + LinkedVMID int `json:"linked_vm_id"` + LinkedCloneCount int `json:"linked_clone_count"` + SnapshotCount int `json:"snapshot_count"` + HostDeviceCount int `json:"host_device_count"` + Node Node `json:"node"` + AllMd5 bool `json:"all_md5"` + EditableParams EditableParams `json:"editable_params"` + Metrics interface{} `json:"metrics"` //Metrics + ParentTree []ParentTree `json:"parent_tree"` +} + +// BootDevice represents the boot device information. +type BootDevice struct { + Type string `json:"type"` + InUse bool `json:"in_use"` + Index int `json:"index"` +} + +// Node represents the node information. +type Node struct { + Name string `json:"name"` + NodeID int `json:"node_id"` +} + +// EditableParams represents the editable parameters. +type EditableParams struct { + OnTheFly []string `json:"on_the_fly"` + NeedRestart []string `json:"need_restart"` +} + +// Metrics represents the metrics parameters. +type Metrics struct { + CombinedPartitionTotalMb float64 `json:"combined_partition_total_mb"` + MemoryTotalMb float64 `json:"memory_total_mb"` + DiskIoReadPs float64 `json:"disk_io_read_ps"` + CPUUsagePercent float64 `json:"cpu_usage_percent"` + CombinedPartitionFreeMb float64 `json:"combined_partition_free_mb"` + TrafficInMb float64 `json:"traffic_in_mb"` + TrafficOutMb float64 `json:"traffic_out_mb"` + MemoryFreeMb float64 `json:"memory_free_mb"` + CombinedPartitionUsageMb float64 `json:"combined_partition_usage_mb"` + Modified time.Time `json:"modified"` + UptimeSec float64 `json:"uptime_sec"` + CombinedPartitionUsagePercent float64 `json:"combined_partition_usage_percent"` + DiskIoWritePs float64 `json:"disk_io_write_ps"` + MemoryUsagePercent float64 `json:"memory_usage_percent"` +} + +// ParentTree represents the parent tree information. +type ParentTree struct { + VMID int `json:"vm_id"` + Name string `json:"name"` + UUID string `json:"uuid"` + ParentID int `json:"parent_id"` + IsTemplate bool `json:"is_template"` + Depth int `json:"depth"` +} + +func (vm *RecordVM) UnmarshalJSON(data []byte) error { + type Alias RecordVM + temp := &struct { + Metrics json.RawMessage `json:"metrics,omitempty"` + *Alias + }{ + Alias: (*Alias)(vm), + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + if len(temp.Metrics) > 0 { + var metrics Metrics + if err := json.Unmarshal(temp.Metrics, &metrics); err != nil { + return err + } + vm.Metrics = metrics + } else { + vm.Metrics = nil + } + + return nil +} diff --git a/pkg/vm/models/model_get_disks.go b/pkg/vm/models/model_get_disks.go new file mode 100644 index 0000000..db272ba --- /dev/null +++ b/pkg/vm/models/model_get_disks.go @@ -0,0 +1,48 @@ +package models + +import "time" + +// ListDiskResponse represents the hard disks information. +type ListDiskResponse struct { + List DeviceList `json:"device_list"` + RequestID string `json:"request_id"` +} + +type DeviceList struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []DiskItem `json:"items"` + HasMore bool `json:"has_more"` +} + +type DiskItem struct { + Enabled bool `json:"enabled"` + Status string `json:"status"` + Modified time.Time `json:"modified"` + Created time.Time `json:"created"` + Name string `json:"name"` + StackIndex int `json:"stack_index"` + DiskType string `json:"disk_type"` + ImageID int `json:"image_id"` + Deleted time.Time `json:"deleted"` + ExternalStorageID int `json:"external_storage_id"` + ImagePath string `json:"image_path"` + DeviceID int `json:"device_id"` + IsSharedDisk bool `json:"is_shared_disk"` + ExternalUUID string `json:"external_uuid"` + TierID int `json:"tier_id"` + DeviceIndex int `json:"device_index"` + VolumeID int `json:"volume_id"` + Connected bool `json:"connected"` + DeviceType string `json:"device_type"` + ExternalResourceID int `json:"external_resource_id"` + VMID int `json:"vm_id"` + EmulationType string `json:"emulation_type"` + Size int `json:"size"` + UUID string `json:"uuid"` + InterfaceType string `json:"interface_type"` + EditableParams []string `json:"editable_params"` + DiskCaches []interface{} `json:"disk_caches"` +} diff --git a/pkg/vm/models/model_list.go b/pkg/vm/models/model_list.go new file mode 100644 index 0000000..b4fb374 --- /dev/null +++ b/pkg/vm/models/model_list.go @@ -0,0 +1,107 @@ +package models + +import "time" + +// ListVMResponse represents the entire JSON response +type ListVMResponse struct { + VMList ListVM `json:"vm_list"` + RequestID string `json:"request_id"` +} + +// ListVM represents a list of virtual machines. +type ListVM struct { + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + IsApproximateTotal bool `json:"is_approximate_total"` + Items []ItemVM `json:"items"` + HasMore bool `json:"has_more"` +} + +// ItemVM represents a single virtual machine. +type ItemVM struct { + Domain string `json:"domain"` + Status string `json:"status"` + VMID int `json:"vm_id"` + SystemFlags string `json:"system_flags"` + IopsLimit int `json:"iops_limit"` + IsInfrastructure bool `json:"is_infrastructure"` + TpmEnabled bool `json:"tpm_enabled"` + SecureBoot bool `json:"secure_boot"` + Created time.Time `json:"created"` + ResourcePoolID int `json:"resource_pool_id"` + ExternalStorageID int `json:"external_storage_id"` + AutoStartDelay int `json:"auto_start_delay"` + GuestOsVersion string `json:"guest_os_version"` + RAMSize int `json:"ram_size"` + IsTemplate bool `json:"is_template"` + Description string `json:"description"` + RAMHotplugEnabled bool `json:"ram_hotplug_enabled"` + AutoStart string `json:"auto_start"` + MemGuaranteeSize int `json:"mem_guarantee_size"` + RAMTotalHotplug int `json:"ram_total_hotplug"` + Maxmemory int `json:"maxmemory"` + HaPriority int `json:"ha_priority"` + IoPriority int `json:"io_priority"` + CPUSockets int `json:"cpu_sockets"` + NodeMask string `json:"node_mask"` + MaxRAMSize int `json:"max_ram_size"` + Name string `json:"name"` + CPUMask string `json:"cpu_mask"` + NodeID int `json:"node_id"` + BootDeviceList []BootDevice `json:"boot_device_list"` + DisableAutobalance bool `json:"disable_autobalance"` + CPUUnits int `json:"cpu_units"` + SmbiosUUID string `json:"smbios_uuid"` + InstallOs bool `json:"install_os"` + UsedByDesktop bool `json:"used_by_desktop"` + CPUCount int `json:"cpu_count"` + VirtType string `json:"virt_type"` + VncPassword string `json:"vnc_password"` + VncHostname string `json:"vnc_hostname"` + Ratebound bool `json:"ratebound"` + EfiEnabled bool `json:"efi_enabled"` + Affinity string `json:"affinity"` + GuestOs string `json:"guest_os"` + CPUHotplug bool `json:"cpu_hotplug"` + Imported bool `json:"imported"` + ExternalResourceName string `json:"external_resource_name"` + CoresCount int `json:"cores_count"` + AutoStop string `json:"auto_stop"` + Hostname string `json:"hostname"` + VideoRAMSize int `json:"video_ram_size"` + HaStatus string `json:"ha_status"` + MacAddresses []string `json:"mac_addresses"` + ClusterID int `json:"cluster_id"` + ParentUUID string `json:"parent_uuid"` + ClockOffset string `json:"clock_offset"` + IsLinkedClone bool `json:"is_linked_clone"` + GuestToolsState string `json:"guest_tools_state"` + VncMode string `json:"vnc_mode"` + ExternalUUID string `json:"external_uuid"` + Deleted time.Time `json:"deleted"` + LinkedVMUUID string `json:"linked_vm_uuid"` + RAMBalloonActualHotplug int `json:"ram_balloon_actual_hotplug"` + IPAddress string `json:"ip_address"` + Rates any `json:"rates"` //I don't now type + UUID string `json:"uuid"` + CPULimit int `json:"cpu_limit"` + Location string `json:"location"` + CreationDate time.Time `json:"creation_date"` + VncPort int `json:"vnc_port"` + MonitoringEnabled bool `json:"monitoring_enabled"` + ExternalStorageName string `json:"external_storage_name"` + HaEnabled bool `json:"ha_enabled"` + IoLimit int `json:"io_limit"` + ExternalResourceID int `json:"external_resource_id"` + FolderID int `json:"folder_id"` + Lock bool `json:"lock"` + IsVzTemplate bool `json:"is_vz_template"` + Chipset string `json:"chipset"` + LinkedVMID int `json:"linked_vm_id"` + LinkedCloneCount int `json:"linked_clone_count"` + SnapshotCount int `json:"snapshot_count"` + HostDeviceCount int `json:"host_device_count"` + Node Node `json:"node"` + AllMd5 bool `json:"all_md5"` +} diff --git a/pkg/vm/models/model_power_off.go b/pkg/vm/models/model_power_off.go new file mode 100644 index 0000000..cc5e23f --- /dev/null +++ b/pkg/vm/models/model_power_off.go @@ -0,0 +1,110 @@ +package models + +import "time" + +// DisableInfoResponse represents the response containing disable information for a VM operation. +type DisableInfoResponse struct { + DisableInfo DisableInfo `json:"task_info"` + RequestID string `json:"request_id"` +} + +// DisableInfo represents the detailed task information for a VM operation. +type DisableInfo struct { + Progress int `json:"progress"` + RequestID string `json:"request_id"` + Finished time.Time `json:"finished"` + ParentID int `json:"parent_id"` + NodeID int `json:"node_id"` + Error string `json:"error"` + VMID int `json:"vm_id"` + NewVMStatus string `json:"new_vm_status"` + CurrentVMStatus string `json:"current_vm_status"` + Snapshot string `json:"snapshot"` + InitiatorID int `json:"initiator_id"` + Type string `json:"type"` + Started time.Time `json:"started"` + TimeoutAt time.Time `json:"timeout_at"` + NewNodeID int `json:"new_node_id"` + Status string `json:"status"` + Command string `json:"command"` + Created time.Time `json:"created"` + HasChildren bool `json:"has_children"` + Initiator Initiator `json:"initiator"` + Node TaskNode `json:"node"` + Cluster Cluster `json:"cluster"` + VM VM `json:"vm"` +} + +// Initiator represents the initiator of the task. +type Initiator struct { + Name string `json:"name"` + UserID int `json:"user_id"` + Email string `json:"email"` + Login string `json:"login"` +} + +// TaskNode represents the node information. +type TaskNode struct { + ServerUUID string `json:"server_uuid"` + SDKStatus string `json:"sdk_status"` + NodeID int `json:"node_id"` + Status string `json:"status"` + Compute bool `json:"compute"` + Name string `json:"name"` + VStorageClusterID int `json:"vstorage_cluster_id"` + VStorageStatus string `json:"vstorage_status"` + MaintenanceStatus string `json:"maintenance_status"` + StorageStatus string `json:"storage_status"` + ClusterID int `json:"cluster_id"` + IPAddress string `json:"ip_address"` + Deleted time.Time `json:"deleted"` + Modified time.Time `json:"modified"` + VStorageHostID int `json:"vstorage_host_id"` + Description string `json:"description"` + Created time.Time `json:"created"` +} + +// Cluster represents the cluster information. +type Cluster struct { + CPUOvercommit float64 `json:"cpu_overcommit"` + FenixPingIP string `json:"fenix_ping_ip"` + Created time.Time `json:"created"` + FenixClusterStatus string `json:"fenix_cluster_status"` + StorageName string `json:"storage_name"` + FenixBalancingStrategy string `json:"fenix_balancing_strategy"` + FenixEnabled bool `json:"fenix_enabled"` + Name string `json:"name"` + VStorageClusterID int `json:"vstorage_cluster_id"` + FenixClusterID int `json:"fenix_cluster_id"` + FenixBroadcastIP string `json:"fenix_broadcast_ip"` + DRSMode string `json:"drs_mode"` + MemoryOvercommit float64 `json:"memory_overcommit"` + ClusterID int `json:"cluster_id"` + StorageType string `json:"storage_type"` + Deleted time.Time `json:"deleted"` + Modified time.Time `json:"modified"` + NodeCount int `json:"node_count"` + VMTotalRAM int `json:"vm_total_ram"` + VMCount int `json:"vm_count"` +} + +// VM represents the virtual machine information. +type VM struct { + VMID int `json:"vm_id"` + Name string `json:"name"` + UUID string `json:"uuid"` + IPAddress string `json:"ip_address"` + Status string `json:"status"` + LinkedCloneCount int `json:"linked_clone_count"` + SnapshotCount int `json:"snapshot_count"` + HostDeviceCount int `json:"host_device_count"` + Node NodeRef `json:"node"` + SMBIOSUUID string `json:"smbios_uuid"` + AllMD5 bool `json:"all_md5"` +} + +// NodeRef represents a reference to a node, used within VM information. +type NodeRef struct { + NodeID int `json:"node_id"` + Name string `json:"name"` +} diff --git a/pkg/vm/models/model_power_on.go b/pkg/vm/models/model_power_on.go new file mode 100644 index 0000000..4c63d92 --- /dev/null +++ b/pkg/vm/models/model_power_on.go @@ -0,0 +1,36 @@ +package models + +import "time" + +// EnableInfoResponse represents the response containing enable information for a VM operation. +type EnableInfoResponse struct { + EnableInfo EnableInfo `json:"task_info"` + RequestID string `json:"request_id"` +} + +// EnableInfo represents the detailed task information for a VM operation. +type EnableInfo struct { + Progress int `json:"progress"` + RequestID string `json:"request_id"` + Finished time.Time `json:"finished"` + ParentID int `json:"parent_id"` + NodeID int `json:"node_id"` + Error string `json:"error"` + VMID int `json:"vm_id"` + NewVMStatus string `json:"new_vm_status"` + CurrentVMStatus string `json:"current_vm_status"` + Snapshot string `json:"snapshot"` + InitiatorID int `json:"initiator_id"` + Type string `json:"type"` + Started time.Time `json:"started"` + TimeoutAt time.Time `json:"timeout_at"` + NewNodeID int `json:"new_node_id"` + Status string `json:"status"` + Command string `json:"command"` + Created time.Time `json:"created"` + HasChildren bool `json:"has_children"` + Initiator Initiator `json:"initiator"` + Node TaskNode `json:"node"` + Cluster Cluster `json:"cluster"` + VM VM `json:"vm"` +} diff --git a/pkg/vm/power_off.go b/pkg/vm/power_off.go new file mode 100644 index 0000000..e21bbdd --- /dev/null +++ b/pkg/vm/power_off.go @@ -0,0 +1,50 @@ +package vm + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/common" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" +) + +// PowerOff Power off vm +func (vm VM) PowerOff(ctx context.Context, req requests.PowerOffRequest) (*models.DisableInfoResponse, error) { + + res, err := vm.PowerOffRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.DisableInfoResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// PowerOffRaw Power off vm with response as an array of bytes +func (vm VM) PowerOffRaw(ctx context.Context, req requests.PowerOffRequest) ([]byte, error) { + realReq := common.PowerVMRequest{ + VmID: req.VmID, + Command: common.PowerOff, + Graceful: req.Graceful, + Force: req.Force, + } + + if err := validators.ValidateRequest(realReq); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/vm/" + fmt.Sprint(req.VmID) + "/task" + + res, err := vm.client.ApiCall(ctx, http.MethodPost, url, realReq) + return res, err +} diff --git a/pkg/vm/power_on.go b/pkg/vm/power_on.go new file mode 100644 index 0000000..c8283c1 --- /dev/null +++ b/pkg/vm/power_on.go @@ -0,0 +1,48 @@ +package vm + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/common" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/models" + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/pkg/vm/requests" +) + +// Enable vm +func (vm VM) PowerOn(ctx context.Context, req requests.PowerOnRequest) (*models.EnableInfoResponse, error) { + + res, err := vm.PowerOnRaw(ctx, req) + if err != nil { + return nil, err + } + + list := models.EnableInfoResponse{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + return &list, nil +} + +// Enable vm with response as an array of bytes +func (vm VM) PowerOnRaw(ctx context.Context, req requests.PowerOnRequest) ([]byte, error) { + realReq := common.PowerVMRequest{ + VmID: req.VmID, + Command: common.PowerOn, + } + + if err := validators.ValidateRequest(realReq); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := constants.APIv0 + "/vm/" + fmt.Sprint(req) + "/task" + + res, err := vm.client.ApiCall(ctx, http.MethodPost, url, realReq) + return res, err +} diff --git a/pkg/vm/requests/request_create.go b/pkg/vm/requests/request_create.go new file mode 100644 index 0000000..8158e46 --- /dev/null +++ b/pkg/vm/requests/request_create.go @@ -0,0 +1,404 @@ +package requests + +import "time" + +// CreateVMRequest struct to create a virtual machine. +type CreateVMRequest struct { + // Parent template or VM uuid + // Required: false + ParentUUID string `url:"parent_uuid,omitempty" json:"parent_uuid,omitempty"` + + // VM name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // VM description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // VM location on server + // Required: false + Location string `url:"location,omitempty" json:"location,omitempty"` + + // Is HA mode enabled on VM + // Required: false + HaEnabled bool `url:"ha_enabled" json:"ha_enabled"` + + // Current HA priority of VM + // Required: false + HaPriority int `url:"ha_priority" json:"ha_priority"` + + // VM is infrastructure; specify ``True`` to mark VM as infrastructure + // Required: false + IsInfrastructure bool `url:"is_infrastructure" json:"is_infrastructure"` + + // Is a monitoring via guest tools enabled on VM + // Required: false + MonitoringEnabled bool `url:"monitoring_enabled" json:"monitoring_enabled"` + + // VM CPU units + // Required: false + CPUUnits int `url:"cpu_units,omitempty" json:"cpu_units,omitempty" validate:"omitempty,cpu_units"` + + // VM CPU limit + // Required: false + CPULimit int `url:"cpu_limit" json:"cpu_limit"` + + // Is CPU hotplug mode enabled + // Required: false + CPUHotplug bool `url:"cpu_hotplug,omitempty" json:"cpu_hotplug,omitempty"` + + // CPU sockets + // Required: true + CPUSockets int `url:"cpu_sockets" json:"cpu_sockets" validate:"required"` + + // CPU mask for a virtual machine + // Required: false + CPUMask string `url:"cpu_mask,omitempty" json:"cpu_mask,omitempty"` + + // NUMA node mask for a virtual machine + // Required: false + NodeMask string `url:"node_mask,omitempty" json:"node_mask,omitempty"` + + // Affinity rule type + // Required: false + Affinity string `url:"affinity,omitempty" json:"affinity,omitempty" validate:"omitempty,affinity"` + + // Time delay used during the virtual machine automatic startup + // Required: false + AutoStartDelay int `url:"auto_start_delay,omitempty" json:"auto_start_delay,omitempty"` + + // RAM size in Megabytes + // Required: true + RAMSize int `url:"ram_size,omitempty" json:"ram_size,omitempty" validate:"required"` + + // Required guarantee memory in percent + // Required: false default -1 + MemGuaranteeSize int `url:"mem_guarantee_size,omitempty" json:"mem_guarantee_size,omitempty" validate:"omitempty,mem_guarantee_size"` + + // Is RAM hotplug enabled + // Required: false + RAMHotplugEnabled bool `url:"ram_hotplug_enabled,omitempty" json:"ram_hotplug_enabled,omitempty"` + + // Video RAM size in Megabytes + // Required: true + VideoRAMSize int `url:"video_ram_size,omitempty" json:"video_ram_size,omitempty" validate:"required,video_ram"` + + // VM guest OS type + // Required: false //validation + GuestOs string `url:"guest_os,omitempty" json:"guest_os,omitempty" validate:"omitempty,guest_os"` + + // Is VM uses EFI instead of BIOS + // Required: false + EfiEnabled bool `url:"efi_enabled" json:"efi_enabled"` + + // Enabled TMP + // Required: false + TpmEnabled bool `url:"tpm_enabled" json:"tpm_enabled"` + + // is VM uses Secure Boot + // Required: false + SecureBoot bool `url:"secure_boot" json:"secure_boot"` + + // VM disk I/O priority level + // Required: false ///0-7 + IoPriority int `url:"io_priority,omitempty" json:"io_priority,omitempty" validate:"omitempty,io_priority"` + + // Allowed bandwidth for VM disk I/O operations in bytes + // Required: false + IoLimit int `url:"io_limit,omitempty" json:"io_limit,omitempty"` + + // IOPS limit for VM disk I/O operations + // Required: false + IopsLimit int `url:"iops_limit,omitempty" json:"iops_limit,omitempty"` + + // If True don't move this VM by DRS auto balancing + // Required: false + DisableAutobalance bool `url:"disable_autobalance" json:"disable_autobalance"` + + // If ``True``, the bandwidth guarantee (the global rate parameter) is also the limit for the virtual machine or container + // Required: false + Ratebound bool `url:"ratebound" json:"ratebound"` + + // Boot devices order list + // Required: false + BootDeviceList []BootDevice `url:"-" json:"boot_device_list,omitempty"` + + // VM CPU units + // Required: false + IPAddress any `url:"ip_address,omitempty" json:"ip_address,omitempty"` + + // Guest Tools State + // Required: false + GuestToolsState string `url:"guest_tools_state,omitempty" json:"guest_tools_state,omitempty"` + + // Should OS be installed after creation + // Required: false + InstallOs bool `url:"install_os,omitempty" json:"install_os,omitempty"` + + // VNC server password + // Required: false + VncPassword string `url:"vnc_password,omitempty" json:"vnc_password,omitempty"` + + // External storage name + // Required: false + ExternalStorageName string `url:"external_storage_name,omitempty" json:"external_storage_name,omitempty"` + + // Linked vm uuid + // Required: false + LinkedVMUUID any `url:"linked_vm_uuid,omitempty" json:"linked_vm_uuid,omitempty"` + + // Creation_date + // Required: false + CreationDate time.Time `url:"creation_date,omitempty" json:"creation_date,omitempty"` + + // Imported + // Required: false + Imported bool `url:"imported,omitempty" json:"imported,omitempty"` + + // Type of virtualization + // Required: false + VirtType string `url:"virt_type,omitempty" json:"virt_type,omitempty" validation:"omitempty,virt_type"` + + // Used by dekstop + // Required: false + UsedByDesktop bool `url:"used_by_desktop,omitempty" json:"used_by_desktop,omitempty"` + + // VNC server hostname + // Required: false + VncHostname string `url:"vnc_hostname,omitempty" json:"vnc_hostname,omitempty"` + + // Ram total hotplug flag + // Required: false + RAMTotalHotplug any `url:"ram_total_hotplug,omitempty" json:"ram_total_hotplug,omitempty"` + + // Linked VM ID + // Required: false + LinkedVMID any `url:"linked_vm_id,omitempty" json:"linked_vm_id,omitempty"` + + // Ram balloon actual hotplug + // Required: false + RAMBalloonActualHotplug any `url:"ram_balloon_actual_hotplug,omitempty" json:"ram_balloon_actual_hotplug,omitempty"` + + // Smbios uuid + // Required: false + SmbiosUUID string `url:"smbios_uuid,omitempty" json:"smbios_uuid,omitempty"` + + // Parent template or VM uuid + // Required: false + UUID string `url:"uuid,omitempty" json:"uuid,omitempty"` + + // Name of external resource + // Required: false + ExternalResourceName string `url:"external_resource_name,omitempty" json:"external_resource_name,omitempty"` + + // HA Status + // Required: false + HaStatus string `url:"ha_status,omitempty" json:"ha_status,omitempty"` + + // External uuid + // Required: false + ExternalUUID any `url:"external_uuid,omitempty" json:"external_uuid,omitempty"` + + // Time of deleted + // Required: false + Deleted any `url:"deleted,omitempty" json:"deleted,omitempty"` + + // VM's System flags + // Required: false + SystemFlags string `url:"system_flags,omitempty" json:"system_flags,omitempty"` + + // VM is template; specify ``True`` to create master for linked clone VM + // Required: false + IsVzTemplate bool `url:"is_vz_template,omitempty" json:"is_vz_template,omitempty"` + + // VNC port + // Required: false //validate + VncPort int `url:"vnc_port,omitempty" json:"vnc_port,omitempty"` + + // status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // vm id + // Required: false + VMID int `url:"vm_id,omitempty" json:"vm_id,omitempty"` + + // MaxRamSize + // Required: false + MaxRAMSize int `url:"max_ram_size,omitempty" json:"max_ram_size,omitempty"` + + // Cores count + // Required: false + CoresCount int `url:"cores_count,omitempty" json:"cores_count,omitempty"` + + // is template + // Required: false + IsTemplate bool `url:"is_template,omitempty" json:"is_template,omitempty"` + + // max memory + // Required: false + Maxmemory int `url:"maxmemory,omitempty" json:"maxmemory,omitempty"` + + // External resource id + // Required: false + ExternalResourceID any `url:"external_resource_id,omitempty" json:"external_resource_id,omitempty"` + + // is linked clone + // Required: false + IsLinkedClone bool `url:"is_linked_clone,omitempty" json:"is_linked_clone,omitempty"` + + // created time + // Required: false + Created time.Time `url:"created,omitempty" json:"created,omitempty"` + + // list of mac addresses + // Required: false + MacAddresses any `url:"mac_addresses,omitempty" json:"mac_addresses,omitempty"` + + // VM guest domain name + // Required: false + Domain string `url:"domain,omitempty" json:"domain,omitempty"` + + // Count of linked clones + // Required: false + LinkedCloneCount int `url:"linked_clone_count,omitempty" json:"linked_clone_count,omitempty"` + + // Count of snapshot + // Required: false + SnapshotCount int `url:"snapshot_count,omitempty" json:"snapshot_count,omitempty"` + + // Count of device host + // Required: false + HostDeviceCount int `url:"host_device_count,omitempty" json:"host_device_count,omitempty"` + + // All md5 + // Required: false + AllMd5 bool `url:"all_md5,omitempty" json:"all_md5,omitempty"` + + // Size of disks + // Required: false + DisksSize int `url:"disks_size,omitempty" json:"disks_size,omitempty"` + + // Size of template + // Required: false + TemplateSize float64 `url:"template_size,omitempty" json:"template_size,omitempty"` + + // Full hard drive size + // Required: false + FullHardDriveSize int `url:"full_hard_drive_size,omitempty" json:"full_hard_drive_size,omitempty"` + + // Used hard drive size + // Required: false + UsedHardDriveSize float64 `url:"used_hard_drive_size,omitempty" json:"used_hard_drive_size,omitempty"` + + // Cluster ID + // Required: false + ClusterID int `url:"cluster_id,omitempty" json:"cluster_id,omitempty"` + + // External storage ID + // Required: false + ExternalStorageID int `url:"external_storage_id,omitempty" json:"external_storage_id,omitempty"` + + // VM guest OS version + // Required: false + GuestOsVersion string `url:"guest_os_version,omitempty" json:"guest_os_version,omitempty" validate:"omitempty,guest_os_version"` + + // Virtual machine startup options + // Required: false + AutoStart string `url:"auto_start,omitempty" json:"auto_start,omitempty" validate:"omitempty,auto_start"` + + // Automatic shutdown mode + // Required: false + AutoStop string `url:"auto_stop,omitempty" json:"auto_stop,omitempty" validate:"omitempty,auto_stop"` + + // VNC server mode + // Required: false + VncMode string `url:"vnc_mode,omitempty" json:"vnc_mode,omitempty" validate:"omitempty,vnc_mode"` + + // VM CPU count + // Required: true + CPUCount int `url:"cpu_count,omitempty" json:"cpu_count,omitempty" validate:"required"` + + // rates object + // Required: false + Rates struct{} `url:"rates,omitempty" json:"rates,omitempty"` + + // List of VM names to create + // Required: false + Names []string `url:"names,omitempty" json:"names,omitempty"` + + // List with devices dicts + // Required: false + Devices []Device `json:"devices,omitempty"` + + // Host devices + // Required: false + HostDevices []int `url:"host_devices,omitempty" json:"host_devices,omitempty"` + + // VM chipset + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // VM clock offset + // Required: false + ClockOffset string `url:"clock_offset,omitempty" json:"clock_offset,omitempty" validate:"omitempty,clock_offset"` +} + +// struct from request JSON no have documentation +type Device struct { + Name any `json:"name,omitempty"` + Modified time.Time `json:"modified,omitempty"` + Status string `json:"status,omitempty"` + EmulationType string `json:"emulation_type,omitempty"` + Enabled bool `json:"enabled,omitempty"` + DeviceType string `json:"device_type,omitempty"` + VolumeID any `json:"volume_id,omitempty"` + IsSharedDisk bool `json:"is_shared_disk,omitempty"` + Size int `json:"size,omitempty"` + VMID int `json:"vm_id,omitempty"` + DeviceIndex int `json:"device_index,omitempty"` + DeviceID int `json:"device_id,omitempty"` + ExternalUUID any `json:"external_uuid,omitempty"` + Deleted any `json:"deleted,omitempty"` + Connected bool `json:"connected,omitempty"` + Created time.Time `json:"created,omitempty"` + ImageID int `json:"image_id,omitempty"` + UUID any `json:"uuid,omitempty"` + ExternalResourceID any `json:"external_resource_id,omitempty"` + ImagePath string `json:"image_path,omitempty"` + EditableParams []string `json:"editable_params,omitempty"` + ImageUUID string `json:"image_uuid,omitempty"` + ImageSource string `json:"image_source,omitempty"` + ImageMd5 string `json:"image_md5,omitempty"` + DiskCaches []any `json:"disk_caches,omitempty"` + ImageName string `json:"image_name,omitempty"` + ImageHashSum string `json:"image_hashSum,omitempty"` + Editing bool `json:"editing,omitempty"` + DiskType string `json:"disk_type,omitempty"` + InterfaceType string `json:"interface_type,omitempty"` + StackIndex int `json:"stack_index,omitempty"` + ExternalStorageID int `json:"external_storage_id,omitempty"` + TierID any `json:"tier_id,omitempty"` + ImageType string `json:"image_type,omitempty"` + IPAddress []any `json:"ip_address,omitempty"` + GuestIPAddress any `json:"guest_ip_address,omitempty"` + DefaultGateway any `json:"default_gateway,omitempty"` + DNSServers []any `json:"dns_servers,omitempty"` + SearchDomains []any `json:"search_domains,omitempty"` + AutoApply bool `json:"auto_apply,omitempty"` + MacAddress any `json:"mac_address,omitempty"` + NetworkName string `json:"network_name,omitempty"` + VirtualNetworkID int `json:"virtual_network_id,omitempty"` + AdapterType string `json:"adapter_type,omitempty"` + PreventIPSpoof bool `json:"prevent_ip_spoof,omitempty"` + PreventMacSpoof bool `json:"prevent_mac_spoof,omitempty"` + PreventPromisc bool `json:"prevent_promisc,omitempty"` + Autoconnect string `json:"autoconnect,omitempty"` +} + +type BootDevice struct { + Type string `url:"type" json:"type" validate:"required"` + InUse bool `url:"in_use,omitempty" json:"in_use,omitempty"` + Index int `url:"index,omitempty" json:"index,omitempty"` +} diff --git a/pkg/vm/requests/request_get.go b/pkg/vm/requests/request_get.go new file mode 100644 index 0000000..f16d6d0 --- /dev/null +++ b/pkg/vm/requests/request_get.go @@ -0,0 +1,25 @@ +package requests + +// GetVMRequest struct to get details of a virtual machine. +type GetVMRequest struct { + // Id of source VM + // Required: true + VmID int `url:"vmId" json:"vmId" validate:"required"` + + // If true, include VM's current metrics in the metrics field. + // Required: false + WithMetrics bool `url:"with_metrics,omitempty" json:"with_metrics,omitempty"` + + // Aggregation period for metrics. Used only if 'WithMetrics' is true. + // Possible values: "latest", "hour", "day", "week". + // Required: false + MetricsPeriod string `url:"metrics_period,omitempty" json:"metrics_period,omitempty" validate:"omitempty,metrics_period"` + + // If true, include VM's hierarchy information. + // Required: false + WithParentTree bool `url:"with_parent_tree,omitempty" json:"with_parent_tree,omitempty"` + + // If true, request actual info from the agent. + // Required: false + ForceRefresh bool `url:"force_refresh,omitempty" json:"force_refresh,omitempty"` +} diff --git a/pkg/vm/requests/request_get_disks.go b/pkg/vm/requests/request_get_disks.go new file mode 100644 index 0000000..23ace50 --- /dev/null +++ b/pkg/vm/requests/request_get_disks.go @@ -0,0 +1,8 @@ +package requests + +// GetDisksRequest struct to get disks of a virtual machine. +type GetDisksRequest struct { + // Id of source VM + // Required: true + VmID int `url:"vmId" json:"vmId" validate:"required"` +} diff --git a/pkg/vm/requests/request_list.go b/pkg/vm/requests/request_list.go new file mode 100644 index 0000000..64d454f --- /dev/null +++ b/pkg/vm/requests/request_list.go @@ -0,0 +1,263 @@ +package requests + +import "time" + +// ListVMRequest struct to get list of virtual machines +type ListVMRequest struct { + // Find by partial name match. Ignored if NameExact is provided. + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by exact VM name. + // Required: false + NameExact string `url:"name_exact,omitempty" json:"name_exact,omitempty"` + + // Find by UUID. + // Required: false + UUID string `url:"uuid,omitempty" json:"uuid,omitempty"` + + // Find by virtualization type. Possible values: "VM", "CT". + // Required: false + VirtType string `url:"virt_type,omitempty" json:"virt_type,omitempty" validate:"omitempty,virt_type"` + + // Find by Node identifier. + // Required: false + NodeID string `url:"node_id,omitempty" json:"node_id,omitempty"` + + // Filter by OS installation. + // Required: false + InstallOS interface{} `url:"install_os,omitempty" json:"install_os,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs by name of node. + // Required: false + NodeName string `url:"node_name,omitempty" json:"node_name,omitempty"` + + // Filter VMs by description. + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Filter VMs by IP address. + // Required: false + IPAddress string `url:"ip_address,omitempty" json:"ip_address,omitempty"` + + // Filter VMs by MAC address. + // Required: false + MACAddress string `url:"mac_address,omitempty" json:"mac_address,omitempty"` + + // Filter VMs by system hostname. + // Required: false + Hostname string `url:"hostname,omitempty" json:"hostname,omitempty"` + + // Filter VMs by Windows domain. + // Required: false + Domain string `url:"domain,omitempty" json:"domain,omitempty"` + + // Filter VMs by status. Possible values: "RUNNING", "PAUSED", "STOPPED", "SUSPENDED", "ERROR", "DELETED", "CREATING", "FAILED_TO_CREATE", "NODE_OFFLINE". + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty" validate:"omitempty,vm_status"` + + // Filter VMs by CPU count. + // Required: false + CPUCount int `url:"cpu_count,omitempty" json:"cpu_count,omitempty"` + + // Filter VMs by CPU units (2-262144). + // Required: false + CPUUnits int `url:"cpu_units,omitempty" json:"cpu_units,omitempty" validate:"omitempty,cpu_units"` + + // Filter VMs by CPU limit (0-100) * number of cores on server. + // Required: false + CPULimit int `url:"cpu_limit,omitempty" json:"cpu_limit,omitempty" validate:"omitempty,cpu_limit"` + + // Set CPU hotplug mode for the specified VM. + // Required: false + CPUHotplug interface{} `url:"cpu_hotplug,omitempty" json:"cpu_hotplug,omitempty" validate:"omitempty,is_bool"` + + // Set CPU affinity mask for the specified VM. + // Required: false + CPUMask string `url:"cpu_mask,omitempty" json:"cpu_mask,omitempty"` + + // Set NUMA node mask for the specified VM. + // Required: false + NodeMask string `url:"node_mask,omitempty" json:"node_mask,omitempty"` + + // Filter VMs by CPU sockets. + // Required: false + CPUSockets int `url:"cpu_sockets,omitempty" json:"cpu_sockets,omitempty"` + + // Filter VMs by total CPU cores. + // Required: false + CoresCount int `url:"cores_count,omitempty" json:"cores_count,omitempty"` + + // Filter VMs by RAM size in Megabytes. + // Required: false + RAMSize int `url:"ram_size,omitempty" json:"ram_size,omitempty"` + + // Filter VMs by maximum RAM size in Megabytes. + // Required: false + MaxRAMSize int `url:"max_ram_size,omitempty" json:"max_ram_size,omitempty"` + + // Set memory hotplugging mode for the specified VM. + // Required: false + RAMHotplugEnabled bool `url:"ram_hotplug_enabled" json:"ram_hotplug_enabled"` + + // Set memory guarantee size as a percentage of VM RAM. + // Required: false + MemGuaranteeSize int `url:"mem_guarantee_size,omitempty" json:"mem_guarantee_size,omitempty" validate:"omitempty,mem_guarantee_size"` + + // Filter VMs by video RAM size in Megabytes. + // Required: false + VideoRAMSize int `url:"video_ram_size,omitempty" json:"video_ram_size,omitempty"` + + // Filter VMs by Guest OS version. + // Required: false + GuestOSVersion string `url:"guest_os_version,omitempty" json:"guest_os_version,omitempty"` + + // Filter VMs by location. + // Required: false + Location string `url:"location,omitempty" json:"location,omitempty"` + + // Filter VMs by EFI usage. + // Required: false + EFIEnabled interface{} `url:"efi_enabled,omitempty" json:"efi_enabled,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs by Cluster ID. + // Required: false + ClusterID int `url:"cluster_id,omitempty" json:"cluster_id,omitempty"` + + // Filter VMs by folder ID. + // Required: false + FolderID int `url:"folder_id,omitempty" json:"folder_id,omitempty"` + + // Filter VMs in all folder subtree. + // Required: false + // Default: true + FolderSubtree interface{} `url:"folder_subtree,omitempty" json:"folder_subtree,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs by resource pool ID. + // Required: false + ResourcePoolID int `url:"resource_pool_id,omitempty" json:"resource_pool_id,omitempty"` + + // Filter VMs by external resource ID. + // Required: false + ExternalResourceID int `url:"external_resource_id,omitempty" json:"external_resource_id,omitempty"` + + // Filter VMs by external storage ID. + // Required: false + ExternalStorageID int `url:"external_storage_id,omitempty" json:"external_storage_id,omitempty"` + + // Filter VMs by external storage name. + // Required: false + ExternalStorageName string `url:"external_storage_name,omitempty" json:"external_storage_name,omitempty"` + + // Filter VMs by external resource name. + // Required: false + ExternalResourceName string `url:"external_resource_name,omitempty" json:"external_resource_name,omitempty"` + + // Include external resource pools for filtering. + // Required: false + IncludeExternal bool `url:"include_external" json:"include_external"` + + // Filter VMs by virtual network ID. + // Required: false + VirtualNetworkID int `url:"virtual_network_id,omitempty" json:"virtual_network_id,omitempty"` + + // Filter VMs by HA status. Possible values: "ACTIVE", "INACTIVE", "ERROR", "UNKNOWN", "NO LICENSE". + // Required: false + HAStatus string `url:"ha_status,omitempty" json:"ha_status,omitempty" validate:"omitempty,ha_status"` + + // Filter VMs by imported status from Virtuozzo. + // Required: false + Imported interface{} `url:"imported,omitempty" json:"imported,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs by lock status. + // Required: false + Lock interface{} `url:"lock,omitempty" json:"lock,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs by usage in VDI desktops. + // Required: false + UsedByDesktop interface{} `url:"used_by_desktop,omitempty" json:"used_by_desktop,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs that are out of any folder. + // Required: false + NoFolder interface{} `url:"no_folder,omitempty" json:"no_folder,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs by parent UUID. + // Required: false + ParentUUID string `url:"parent_uuid,omitempty" json:"parent_uuid,omitempty"` + + // Filter VMs by linked VM ID. + // Required: false + LinkedVMID int `url:"linked_vm_id,omitempty" json:"linked_vm_id,omitempty"` + + // Filter VMs by template flag. + // Required: false + IsVZTemplate interface{} `url:"is_vz_template,omitempty" json:"is_vz_template,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs by linked clone status. + // Required: false + IsLinkedClone interface{} `url:"is_linked_clone,omitempty" json:"is_linked_clone,omitempty" validate:"omitempty,is_bool"` + + // Filter VMs by infrastructure flag. + // Required: false + IsInfrastructure interface{} `url:"is_infrastructure,omitempty" json:"is_infrastructure,omitempty" validate:"omitempty,is_bool"` + + // List of VM IDs to exclude from listing. + // Required: false + ExcludeIDs []int `url:"exclude_ids,omitempty" json:"exclude_ids,omitempty"` + + // Filter to search in all text fields. + // Required: false + FilterText string `url:"filter_text,omitempty" json:"filter_text,omitempty"` + + // Filter VMs deleted before this date. + // Required: false + DeletedBefore time.Time `url:"deleted_before,omitempty" json:"deleted_before,omitempty"` + + // Filter VMs deleted after this date. + // Required: false + DeletedAfter time.Time `url:"deleted_after,omitempty" json:"deleted_after,omitempty"` + + // Filter VMs created before this date. + // Required: false + CreatedBefore time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Filter VMs created after this date. + // Required: false + CreatedAfter time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` + + // Filter VMs created in Virtuozzo before this date. + // Required: false + CreationDateBefore time.Time `url:"creation_date_before,omitempty" json:"creation_date_before,omitempty"` + + // Filter VMs created in Virtuozzo after this date. + // Required: false + CreationDateAfter time.Time `url:"creation_date_after,omitempty" json:"creation_date_after,omitempty"` + + // List columns which will be used by FilterText. + // Required: false + FilterColumns string `url:"filter_columns,omitempty" json:"filter_columns,omitempty"` + + // Column sorting, format +|-(field). + // Required: false + Sort []string `url:"sort,omitempty" json:"sort,omitempty"` + + // Add disk size info to output. + // Required: false + WithDisksSize bool `url:"with_disks_size,omitempty" json:"with_disks_size,omitempty"` + + // Filter VMs by SMBIOS UUID. + // Required: false + SMBIOSUUID string `url:"smbios_uuid,omitempty" json:"smbios_uuid,omitempty"` + + // Number of returning page. + // Required: false + Page int `url:"page,omitempty" json:"page,omitempty"` + + // Number of items on page. + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` + + // Filter VMs by visibility. Possible values: "visible", "deleted", "all". + // Required: false + Visibility string `url:"visibility,omitempty" json:"visibility,omitempty" validate:"omitempty,visibility"` +} diff --git a/pkg/vm/requests/request_power_off.go b/pkg/vm/requests/request_power_off.go new file mode 100644 index 0000000..d17353a --- /dev/null +++ b/pkg/vm/requests/request_power_off.go @@ -0,0 +1,16 @@ +package requests + +// PowerOffRequest struct to disable VM +type PowerOffRequest struct { + // Id of source VM + // Required: true + VmID int `url:"vm" json:"vm" validate:"required"` + + // If true, attempt a graceful operation of the virtual machine. + // Required: false + Graceful bool `url:"graceful" json:"graceful,omitempty"` + + // If true, force the operation of the virtual machine. + // Required: false + Force bool `url:"force" json:"force,omitempty"` +} diff --git a/pkg/vm/requests/request_power_on.go b/pkg/vm/requests/request_power_on.go new file mode 100644 index 0000000..0ca1d84 --- /dev/null +++ b/pkg/vm/requests/request_power_on.go @@ -0,0 +1,8 @@ +package requests + +// PowerOnRequest struct to enable VM +type PowerOnRequest struct { + // Id of source VM + // Required: true + VmID int `url:"vm" json:"vm" validate:"required"` +} diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go new file mode 100644 index 0000000..3e628de --- /dev/null +++ b/pkg/vm/vm.go @@ -0,0 +1,17 @@ +package vm + +import ( + "repository.basistech.ru/BASIS/dynamix-standart-go-sdk/interfaces" +) + +// Structure for creating request to vm +type VM struct { + client interfaces.Caller +} + +// Builder for vms endpoints +func New(client interfaces.Caller) *VM { + return &VM{ + client, + } +} diff --git a/samples/config.json b/samples/config.json new file mode 100644 index 0000000..f6cf30e --- /dev/null +++ b/samples/config.json @@ -0,0 +1,9 @@ +{ + "username": "", + "password": "", + "vControlURL": "", + "retries": 5, + "timeout": "5m", + "sslSkipVerify": false, + "token": "" +} diff --git a/samples/config.yml b/samples/config.yml new file mode 100644 index 0000000..69fd784 --- /dev/null +++ b/samples/config.yml @@ -0,0 +1,7 @@ +username: +password: +vControlURL: +retries: 5 +timeout: 5m +sslSkipVerify: false +token: