diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54c2405 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +cmd/ +.idea/ +.vscode/ +.fleet/ +.DS_Store +tests/platform_upgrade/.env +tests/platform_upgrade/input.json +tests/platform_upgrade/*.txt +*.env diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e628f08 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2022 Basis LTD + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..544f131 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +all: lint test +.PHONY: all + +lint: + golangci-lint run --timeout 600s +test: + go test -v -failfast -timeout 600s ./... + +.DEFAULT_GOAL := lint + diff --git a/README.md b/README.md index e69de29..82a94d8 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,1591 @@ +# Dynamix SDK + +Dynamix SDK - это библиотека, написанная на языке GO, позволяющая взаимодействовать с API облачной платформы **Dynamix**. Библиотека содержит в себе структуры и методы, необходимые для отправки запросов. Dynamix SDK имеет встроенный http-клиент и поддерживает разные способы авторизации на платформе. Библиотека так же содержит в себе модели ответов от платформы. + +## Версии + + - Версия 9.0.х Decort-SDK соответствует 4.1.0 версии платформы + +## Оглавление + +- [Dynamix SDK](#decort-sdk) + - [Версии](#версии) + - [Оглавление](#оглавление) + - [Установка](#установка) + - [Список API](#список-api) + - [Cloudapi](#cloudapi) + - [Cloudbroker](#cloudbroker) + - [Работа с библиотекой](#работа-с-библиотекой) + - [Настройка конфигурации клиента](#настройка-конфигурации-клиента) + - [Пример конфигурации клиента](#пример-конфигурации-клиента) + - [Парсинг конфигурации из файла](#парсинг-конфигурации-из-файла) + - [Пример JSON конфигурации](#пример-json-конфигурации) + - [Пример YAML конфигурации](#пример-yaml-конфигурации) + - [Создание клиента](#создание-клиента) + - [Пример](#пример) + - [Создание структуры запроса](#создание-структуры-запроса) + - [Пример комментариев структуры](#пример-комментариев-структуры) + - [Пример создания запроса для развертывания виртуальной машины:](#пример-создания-запроса-для-развертывания-виртуальной-машины) + - [Выполнение запроса](#выполнение-запроса) + - [Пример выполнения запроса](#пример-выполнения-запроса) + - [Пример выполнения GetRaw и ListRaw запросов](#пример-выполнения-getraw-и-listraw-запросов) + - [Фильтрация](#фильтрация) + - [Использование на примере `compute.FilterByK8SID`:](#использование-на-примере-computefilterbyk8sid) + - [Сортировка](#сортировка) + - [Сериализация](#сериализация) + - [Комплексный пример](#комплексный-пример) + - [Получение списка уникальных идентификаторов ID объекта](#получение-списка-уникальных-идентификаторов-id-объекта) + - [Методы поля Result для группы tasks](#методы-поля-result-для-группы-tasks) + - [Работа с legacy клиентом](#работа-с-legacy-клиентом) + - [Настройка конфигурации legacy клиента](#настройка-конфигурации-legacy-клиента) + - [Пример конфигурации legacy клиента](#пример-конфигурации-legacy-клиента) + - [Парсинг legacy конфигурации из файла](#парсинг-legacy-конфигурации-из-файла) + - [Пример legacy JSON конфигурации](#пример-legacy-json-конфигурации) + - [Пример legacy YAML конфигурации](#пример-legacy-yaml-конфигурации) + - [Создание legacy клиента](#создание-legacy-клиента) + - [Пример создания legacy клиента](#пример-создания-legacy-клиента) + - [Пример выполнения запроса](#пример-выполнения-запроса-1) + - [Работа с BVS клиентом](#работа-с-bvs-клиентом) + - [Настройка параметров BVS в кабинете администратора](#настройка-параметров-bvs-в-кабинете-администратора) + - [Настройка конфигурации BVS клиента](#настройка-конфигурации-bvs-клиента) + - [Описание структуры token](#описание-структуры-token) + - [Пример конфигурации BVS клиента](#пример-конфигурации-bvs-клиента) + - [Парсинг BVS конфигурации из файла](#парсинг-bvs-конфигурации-из-файла) + - [Парсинг BVS токена из файла](#парсинг-bvs-токена-из-файла) + - [Пример BVS JSON конфигурации](#пример-bvs-json-конфигурации) + - [Пример BVS YAML конфигурации](#пример-bvs-yaml-конфигурации) + - [Создание BVS клиента](#создание-bvs-клиента) + - [Пример создания BVS клиента](#пример-создания-bvs-клиента) + - [Пример получения BVS токена](#пример-получения-bvs-токена) + - [Пример обновления BVS токена](#пример-обновления-bvs-токена) + - [Пример выполнения запроса](#пример-выполнения-запроса-2) + - [Пример валидации запросов, имеющих в своей структуре поле RAM (или MasterRam/WorkerRAM)](#пример-валидации-запросов-имеющих-в-своей-структуре-поле-ram-или-masterramworkerram) + - [Пример выполнения запроса](#пример-выполнения-запроса-3) + - [Работа с универсальным клиентом](#работа-с-универсальным-клиентом) + - [Настройка конфигурации универсального клиента](#настройка-конфигурации-универсального-клиента) + - [Пример конфигурации универсального клиента](#пример-конфигурации-универсального-клиента) + - [Парсинг универсальной конфигурации из файла](#парсинг-универсальной-конфигурации-из-файла) + - [Создание универсального клиента](#создание-универсального-клиента) + - [Пример создания универсального клиента](#пример-создания-универсального-клиента) + - [Пример выполнения запроса](#пример-выполнения-запроса-4) + +## Установка + +Выполните команду в терминале: + +```bash +go get -u repository.basistech.ru/BASIS/dynamix-golang-sdk +``` + +## Список API + +Библиотека поддерживает две группы API платформы: + +- `cloudapi` - пользовательская группа, которая позволяет воспользоваться всем стардартным функционалом платформы; +- `cloudbroker` - административная группа, которая позволяет воспользоваться всем стандартным функционалом платформы и расширенными возможностями, включающими в себя управление пользователями, ресурсами, платформами размещения ресурсов и т.д. + +### Cloudapi + +`Cloudapi` позволяет выполнять запросы к группе пользовательских конечных точек +Данная группа ручек позволяет выполнять следующие операции в платформе: + +- `Account` - управление аккаунтами - внутренними учетными записями платформы, которые являются владельцами вычислительных ресурсов; +- `Audit` - получение информации о событиях системы; +- `BService` - управление группами виртуальных машин (computes); +- `Compute` - управление виртуальными машинами (индивидуально); +- `Disks` - управление виртуальными дисками; +- `DPDK` - управление виртуальными сетями DPDK; +- `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ; +- `FLIPgroup` - управление группами "плавающими" ip - адресами; +- `Image` - управление образами операционных систем; +- `K8CI` - получение информации о конвейере для создания кластера; +- `K8S` - управление кластерами kubernetes; +- `KVMx86` - создание виртуальной машины x86; +- `LB` - управление балансировщиками нагрузки; +- `Locations` - получение информации о grid площадки; +- `RG` - управление ресурсными группами аккаунта; +- `Stack` - получение информации о вычислительных узлах; +- `Tasks` - получение информации о ходе выполнения асинхронных задач (например, создание кластера); +- `VFPool` - управление пулом виртуальных сетевых функций; +- `VINS` - управление виртуальными изолированными сетями. + +### Cloudbroker + +`Cloudbroker` позволяет выполнять запросы к группе пользовательских конечных точек +Данная группа ручек позволяет выполнять следующие операции в платформе: + +- `Account` - управление аккаунтами - внутренними учетными записями платформы, которые являются владельцами вычислительных ресурсов; +- `Audit` - получение информации о событиях системы; +- `APIAccess` - управление доступом к API и его объектам; +- `Backup` - управление резервным копированием; +- `Compute` - управление виртуальными машинами (индивидуально); +- `Disks` - управление виртуальными дисками; +- `DPDK` - управление виртуальными сетями DPDK; +- `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ; +- `FLIPGroup` - управление группами с «плавающими» ip адресами; +- `Grid` - управление площадками; +- `Group` - управление группами пользователей; +- `Image` - управление образами операционных систем; +- `K8CI` - управление конвейром для создания кластера; +- `K8S` - управление кластерами kubernetes; +- `KVMx86` - создание виртуальной машины x86; +- `LB` - управление балансировщиками нагрузки; +- `Node` - управление нодами платформы; +- `PCIDevice` - управление устройствами; +- `Prometheus` - получение статистики prometheus; +- `Resmon` - получение статистики resource monitoring; +- `RG` - управление ресурсными группами аккаунта; +- `SEP` - управление storage endpoint (sep); +- `Stack` - получение информации о вычислительных узлах; +- `Tasks` - получение информации о ходе выполнения асинхронных задач (например, создание кластера); +- `User` - управление пользователями (индивидуально); +- `VGPU` - управление виртуальными графическими процессорами; +- `VFPool` - управление пулом виртуальных сетевых функций; +- `VINS` - управление виртуальными изолированными сетями. + +## Работа с библиотекой + +Алгоритм работы с библиотекой выглядит следующим образом: + +1. Выполнение одного из действий: +- настройка конфигурации клиента; +- парсинг конфигурации из файла. +2. Создание клиента. +3. Создание структуры запроса. +4. Выполнение запроса. + +### Настройка конфигурации клиента + +Сначала, необходимо создать переменную конфигурации клиента. Конфигурация состоит как из обязательных, так и необязательных полей. +| Поле | Тип | Обязательный | Описание | +| --- | --- | --- | --- | +| AppID | string | Да | app_id ключа для выполнения запросов | +| AppSecret | string | Да | app_secret ключ для выполнения запроса | +| SSOURL | string | Да | URL адрес сервиса аутентификации и авторизации | +| DecortURL | string | Да | URL адрес платформы, с которой будет осуществляться взаимодействие | +| Retries | uint | Нет | Кол-во неудачных попыток выполнения запроса, по умолчанию - 5 | +| Timeout | config.Duration | Нет | Таймаут HTTP клиента, по умолчанию - без ограничений | +| SSLSkipVerify | bool | Нет | Пропуск проверки подлинности сертификата | +| Token | string | Нет | JWT токен | + +##### Пример конфигурации клиента + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" +) + +func main(){ + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + cfg.SetTimeout(5 * time.Minute) +} +``` + +#### Парсинг конфигурации из файла + +Также возможно создать переменную конфигурации из JSON или YAML файла, используя функцию `ParseConfigJSON` (или `ParseConfigYAML`) из пакета config. +
+*См. пример файлов конфигурации ниже и в директории `samples/`.* + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" +) + +func main() { + // Парсинг конфигурации из JSON-файла + cfg, _ := config.ParseConfigJSON("") +} +``` + +#### Пример JSON конфигурации + +```json +{ + "appId": "", + "appSecret": "", + "ssoUrl": "https://sso.digitalenergy.online", + "Url": "https://mr4.digitalenergy.online", + "retries": 5, + "timeout": "5m", + "sslSkipVerify": false +} +``` + +#### Пример YAML конфигурации + +```yaml +appId: +appSecret: +ssoUrl: https://sso.digitalenergy.online +Url: https://mr4.digitalenergy.online +retries: 5 +timeout: 5m +sslSkipVerify: false +``` + +### Создание клиента + +Создание клиента происходит с помощью функции-строителя `New` из основного пакета `dynamix-sdk`, для избежания проблем с именами, пакету можно присвоить алиас ``. Функция принимает конфигурацию, возвращает структуру `Client`, с помощью которой можно взаимодействовать с платформой. + +### Пример + +```go +package main + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + URL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + cfg.SetTimeout(5 * time.Minute) + + // Создание клиента + client := .New(cfg) +} +``` + +### Создание структуры запроса + +Структуры запросов определены в пакетах: + +- `pkg/cloudapi` - для `cloudapi` +- `pkg/cloudbroker` - для `cloudbroker` + +В каждом пакете находятся пакеты групп API: + +- **cloudapi**: + - `pkg/cloudapi/account` - для `Account` + - `pkg/cloudapi/audit` - для `Audit` + - `pkg/cloudapi/bservice` - для `Basic Service` + - `pkg/cloudapi/compute` - для `Compute` + - `pkg/cloudapi/disks` - для `Disks` + - `pkg/cloudapi/dpdknet` - для `DPDK` + - `pkg/cloudapi/extnet` - для `ExtNet` + - `pkg/cloudapi/flipgroup` - для `FLIPGroup` + - `pkg/cloudapi/image` - для `Image` + - `pkg/cloudapi/k8ci` - для `K8CI` + - `pkg/cloudapi/k8s` - для `K8S` + - `pkg/cloudapi/kvmx86` - для `KVMX86` + - `pkg/cloudapi/lb` - для `LB` + - `pkg/cloudapi/locations` - для `Locations` + - `pkg/cloudapi/rg` - для `RG` + - `pkg/cloudapi/stack` - для `Stack` + - `pkg/cloudapi/tasks` - для `Tasks` + - `pkg/cloudapi/vfpool` - для `VFPool` + - `pkg/cloudapi/vins` - для `VINS` +- **cloudbroker**: + - `pkg/cloudbroker/account` - для `Account` + - `pkg/cloudbroker/audit` - для `Audit` + - `pkg/cloudbroker/apiaccess` - для `APIAccess` + - `pkg/cloudbroker/backup` - для `Backup` + - `pkg/cloudbroker/compute` - для `Compute` + - `pkg/cloudbroker/disks` - для `Disks` + - `pkg/cloudbroker/dpdknet` - для `DPDK` + - `pkg/cloudbroker/extnet` - для `ExtNet` + - `pkg/cloudbroker/flipgroup` - для `FLIPGroup` + - `pkg/cloudbroker/grid` - для `Grid` + - `pkg/cloudbroker/group` - для `Group` + - `pkg/cloudbroker/image` - для `Image` + - `pkg/cloudbroker/k8ci` - для `K8CI` + - `pkg/cloudbroker/k8s` - для `K8S` + - `pkg/cloudbroker/kvmx86` - для `KVMX86` + - `pkg/cloudbroker/lb` - для `LB` + - `pkg/cloudbroker/node` - для `Node` + - `pkg/cloudbroker/pcidevice` - для `PCIDevice` + - `pkg/cloudbroker/prometheus` - для `Prometheus` + - `pkg/cloudbroker/resmon` - для `Resmon` + - `pkg/cloudbroker/rg` - для `RG` + - `pkg/cloudbroker/sep` - для `SEP` + - `pkg/cloudbroker/stack` - для `Stack` + - `pkg/cloudbroker/tasks` - для `Tasks` + - `pkg/cloudbroker/user` - для `User` + - `pkg/cloudbroker/vgpu` - для `VGPU` + - `pkg/cloudbroker/vfpool` - для `VFPool` + - `pkg/cloudbroker/vins` - для `VINS` + +Все поля структуры имеют описание, в которых содержится: + +- Их назначение; +- Обязательный или нет - поле required в комментариях; +- Доп. информация (допустимые значения, значения по умолчанию). + +#### Пример комментариев структуры + +```go +type CreateRequest struct { + // ID of the resource group, which will own this VM + // Required: true + RGID uint64 `url:"rgId" json:"rgId"` + + // Name of this VM. + // Must be unique among all VMs (including those in DELETED state) in target resource group + // Required: true + Name string `url:"name" json:"name"` + + // Number CPUs to allocate to this VM + // Required: true + CPU uint64 `url:"cpu" json:"cpu"` + + // Volume of RAM in MB to allocate to this VM + // Required: true + RAM uint64 `url:"ram" json:"ram"` + + // ID of the OS image to base this VM on; + // Could be boot disk image or CD-ROM image + // Required: true + ImageID uint64 `url:"imageId" json:"imageId"` + + // Size of the boot disk in GB + // Required: false + BootDisk uint64 `url:"bootDisk,omitempty" json:"bootDisk,omitempty"` + + // ID of SEP to create boot disk on. + // Uses images SEP ID if not set + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool to use if SEP ID is set, can be also empty if needed to be chosen by system + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Network type + // Should be one of: + // - VINS + // - EXTNET + // - NONE + // Required: false + NetType string `url:"netType,omitempty" json:"netType,omitempty"` + + // Network ID for connect to, + // for EXTNET - external network ID, + // for VINS - VINS ID, + // when network type is not "NONE" + // Required: false + NetID uint64 `url:"netId,omitempty" json:"netId,omitempty"` + + // IP address to assign to this VM when connecting to the specified network + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // Input data for cloud-init facility + // Required: false + Userdata string `url:"userdata,omitempty" json:"userdata,omitempty"` + + // Text description of this VM + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Start VM upon success + // Required: false + Start bool `url:"start,omitempty" json:"start,omitempty"` + + // Stack ID + // Required: false + StackID uint64 `url:"stackId,omitempty" json:"stackId,omitempty"` + + // System name + // Required: false + IS string `url:"IS,omitempty" json:"IS,omitempty"` + + // Compute purpose + // Required: false + IPAType string `url:"ipaType,omitempty" json:"ipaType,omitempty"` + + +} +``` + +#### Пример создания запроса для развертывания виртуальной машины: + +```go +package main + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/pkg/cloudapi/kvmx86" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + URL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + // Создание клиента + client := decort.New(cfg) + + // Создание структуры запроса + // CreateRequest - реквест на создание виртуальной машины + req := kvmx86.CreateRequest{ + RGID: 123, + Name: "compute", + CPU: 4, + RAM: 4096, + ImageID: 321, + } +} +``` + +### Выполнение запроса + +Чтобы выполнить запрос, необходимо: + +1. Вызвать у клиента метод, отвечаеющий за определение группы API для взаимодействия, это может быть `.CloudAPI()`, либо `.CloudBroker()`. Данные методы возвращаеют соответствующие структуры, с помощью которых можно совершать запросы. +2. Вызвать у возвращенной структуры метод, определяющий группу ручек для взаимодействия. + + Доступные методы для `.CloudAPI()`: + + - `.Account()` - для работы с `Account` + - `.Audit()` - для работы с `Audit` + - `.BService()` - для работы с `BService` + - `.Compute()` - для работы с `Compute` + - `.Disks()` - для работы с `Disks` + - `.DPDKNet()` - для работы с `DPDK` + - `.ExtNet()` - для работы с `ExtNet` + - `.FLIPgroup()` - для работы с `FLIPGroup` + - `.Image()` - для работы с `Image` + - `.K8CI()` - для работы с `K8CI` + - `.K8S()` - для работы с `K8S` + - `.KVMx86()` - для работы с `KVMX86` + - `.LB()` - для работы с `LB` + - `.Locations()` - для работы с `Locations` + - `.RG()` - для работы с `RG` + - `.Resmon()` - для работы с `Resmon` + - `.Stack()` - для работы с `Stack` + - `.Tasks()` - для работы с `Tasks` + - `.VFPool()` - для работы с `VFPool` + - `.VINS()` - для работы с `VINS` + + Доступные методы для `.CloudBroker()`: + + - `.Account()` - для работы с `Account` + - `.Audit()` - для работы с `Audit` + - `.APIAccess()` - для работы с `APIAccess` + - `.Backup()` - для работы с `Backup` + - `.Compute()` - для работы с `Compute` + - `.Disks()` - для работы с `Disks` + - `.DPDKNet()` - для работы с `DPDK` + - `.ExtNet()` - для работы с `ExtNet` + - `.FLIPGroup()` - для работы с `FLIPGroup` + - `.Grid()` - для работы с `Grid` + - `.Group()` - для работы с `Group` + - `.Image()` - для работы с `Image` + - `.K8CI()` - для работы с `K8CI` + - `.K8S()` - для работы с `K8S` + - `.KVMx86()` - для работы с `KVMX86` + - `.LB()` - для работы с `LB` + - `.Node()` - для работы с `Node` + - `.PCIDevice()` - для работы с `PCIDevice` + - `.Prometheus()` - для работы с `Prometheus` + - `.Resmon()` - для работы с `Resmon` + - `.RG()` - для работы с `RG` + - `.SEP()` - для работы с `SEP` + - `.Stack()` - для работы с `Stack` + - `.Tasks()` - для работы с `Tasks` + - `.User()` - для работы с `User` + - `.VGPU()` - для работы с `VGPU` + - `.VFPool()` - для работы с `VFPool` + - `.VINS()` - для работы с `VINS` + +3. Вызвать метод, отвечающий за выполнение запроса и передать в него: + +- контекст; +- структуру запроса. + У каждой группы ручек API имеются свои доступные методы, которые определяются платформой. + +4. Обработать результат и ошибки. + +Т.к. все вызовы методов идут последовательно, можно их объеденить в конвейер: +Общий вид конвейера будет выглядеть так: + +```go + client..<группа>.<метод> +``` + +#### Пример выполнения запроса + +```go +package main + +import ( + "log" + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/pkg/cloudapi/kvmx86" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + // Создание клиента + client := decort.New(cfg) + + // Создание структуры запроса + // CreateRequest - реквест на создание виртуальной машины + req := kvmx86.CreateRequest{ + RGID: 123, + Name: "compute", + CPU: 4, + RAM: 4096, + ImageID: 321, + } + + //Выполнение запроса с помощью конвейера + res, err := client.CloudAPI().KVMX86().Create(context.Background(), req) + if err != nil { + log.Fatal(err) + } + + fmt.Println(res) +} +``` + +Для запросов Get и List реализованы запросы GetRaw и ListRaw, которые возвращают ответ не в виде соответствующей структуры, а в виде массива байт (JSON). +Выполнение таких запросов происходит аналогично. + +#### Пример выполнения GetRaw и ListRaw запросов + +```go +package main + +import ( + "log" + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/pkg/cloudapi/account" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + // Создание клиента + client := decort.New(cfg) + + // 1. Создание структуры запроса GetRequest на получение информации об аккаунте и выполнение GetRaw запроса с помощью конвейера + req1 := account.GetRequest{ + AccountID: 123, + } + res1, err := client.CloudAPI().Account().GetRaw(context.Background(), req1) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(res1)) + + // 2. Создание структуры запроса ListRequest на получение информации об аккаунтах и выполнение ListRaw запроса с помощью конвейера + req2 := account.ListRequest{} + res2, err := client.CloudAPI().Account().ListRaw(context.Background(), req2) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(res2)) +} +``` + +### Фильтрация + +Для каждого `ListRequest` в SDK есть группа функций для фильтрации ответа платформы. Для того чтобы произвести фильтрацию по заданным полям, достаточно описать анонимную функцию (предикат) в `.FilterFunc()`, например: + +```go +// Создание запроса account/list +req := account.ListRequest{} + +resp, err := client.CloudAPI().Account().List(context.Background(), req) +if err != nil { + log.Fatal(err) +} + +// Тип filtered - ListAccount, тот же, что и у ответа платформы. +filtered := resp. + FilterFunc(func(ia account.ItemAccount) bool { + // ItemAccount открывает доступ к полям модели ответа платформы. + for _, itemAcl := range ia.ACL { + if itemAcl.UgroupID == "" { // Фильтр по ACL/UgroupID + return true + } + } + return false + }) +``` + +Для удобства пользователей, особенно важные фильтры вынесены в отдельные функции, что облегчает фильтрацию сразу по нескольким полям: + +```go +// Создание запроса account/list +req := account.ListRequest{} + +resp, err := client.CloudAPI().Account().List(context.Background(), req) +if err != nil { + log.Fatal(err) +} + +// Несколько фильтров объединены в конвейер +filtered := resp. + FilterByName(""). + FilterByStatus("") + // .... +``` + +Также у `compute`, `disks`, и `lb` присутствуют специфические функции фильтрации, отправляющие дополнительные запросы. В качестве параметров принимают: +- context.Context - контекст для доп. запроса +- id (или другое поле, по которому производится фильтрация) +- interfaces.Caller - DECORT-клиент для запроса + +Так как эти функции возвращают не только результирующий слайс, но и возможную ошибку - конвейер придется прервать для обработки ошибки. + + +#### Использование на примере `compute.FilterByK8SID`: + +```go +func main() { + // Чтение конфигурации из файла + cfg, _ := config.ParseConfigJSON("") + + // Создание клиента + client := decort.New(cfg) + + // Создание структуры запроса compute/list + req := compute.ListRequest{} + + // Запрос + resp, err := client.CloudAPI().Compute().List(context.Background(), req) + if err != nil { + log.Fatal(err) + } + + // Фильтрация по id кластера. + // Первый аргумент - контекст + // Второй - ID кластера + // Третий - DECORT-клиент + filtered, err := resp.FilterByK8SID(context.Background(), , client) + if err != nil { + log.Fatal(err) // Возможная ошибка запроса + } + + // Доп. фильтрация и сортировка результата - worker ноды кластера + workers := filtered.FilterByK8SWorkers().SortByCreatedTime(false) + + // Доп. фильтрация и сортировка результата - master ноды кластера + masters := filtered.FilterByK8SMasters().SortByCreatedTime(true) + + // .... +} +``` + +### Сортировка + +Функции сортировки так же могут быть объединены в конвейер: + +```go +// Создание запроса compute/list +req := compute.ListRequest{} + +resp, err := client.CloudAPI().Compute().List(context.Background(), req) +if err != nil { + log.Fatal(err) +} + +// Функции сортировки имеют параметр inverse (bool): +// При значении false -> сортировка по возрастанию +// При true -> сортировка по убыванию +sorted := resp. + SortByCPU(false). + SortByRAM(false). + SortByCreatedTime(true) + // .... +``` + +### Сериализация + +Результат преобразований легко сериализовать в JSON при помощи функции `.Serialize()`. Она принимает в себя 2 необязательных аргумента: префикс (prefix) и отступ (Indent). + +В случае если функция вызывается без аргументов, то маршализация пройдет успешно, но без отступов и префиксов. + +```go +// Сериализация данных с префиксом "" и отступом "\t" +serialized, err := filtered.Serialize("", "\t") +if err != nil { + log.Fatal(err) +} + +// Запись сериализованных данных в файл +err = serialized.WriteToFile("") +if err != nil { + log.Fatal(err) +} +``` + +#### Комплексный пример + +```go +package main + +import ( + "context" + "log" + + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/pkg/cloudbroker/compute" +) + +func main() { + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "", + DecortURL: "", + Retries: 5, + } + + cfg.SetTimeout(5 * time.Minute) + + // Создание клиента + client := decort.New(cfg) + + // Создание запроса compute/list + req := compute.ListRequest{} + + resp, err := client.CloudBroker().Compute().List(context.Background(), req) + if err != nil { + log.Fatal(err) + } + + // Цепь преобразований. + filtered, _ := resp. + FilterFunc(func(ic compute.ItemCompute) bool { + return ic.GUID == 123 // Фильтр по GUID + }). + FilterByName(""). // Фильтр по имени + SortByCPU(false). // Сортировка по кол-ву ядер CPU по возрастанию + SortByCreatedTime(true). // Сортировка по времени создания по убыванию + Serialize("", "\t") // Сериализация с префиксом "" и отступом "\t" + // Serialize - терминальная функция, возвращает Serialized (обертку над []byte) + + // Запись данных в файл + err = filtered.WriteToFile("") + if err != nil { + log.Fatal(err) + } +} + +``` + +### Получение списка уникальных идентификаторов ID объекта + +Для всех структур, имеющих поля со списками объектов с уникальными числовыми идентификаторами (ID), добавлены методы IDs(), возвращающие массивы уникальных идентификаторов объектов в этих списках. + +```go +package main + +import ( + "log" + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/pkg/cloudapi/bservice" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + // Создание клиента + client := decort.New(cfg) + + // Создание структуры запроса GetRequest на получение информации о сервисе и выполнение запроса с помощью конвейера + req := bservice.GetRequest{ + ServiceID: 123, + } + res, err := client.CloudAPI().BService().Get(context.Background(), req) + if err != nil { + log.Fatal(err) + } + + // Получение списков ID ComputeIDs и GroupIDs экземпляра res типа RecordBasicService по полям Computes и Groups, соответственно + computeIDs := res.Computes.IDs() + groupIDs := res.Groups.IDs() + fmt.Println(computeIDs) + fmt.Println(groupIDs) +} +``` + +### Методы поля Result для группы tasks + +Поле Result внутри структур группы tasks имеет тип интерфейс и может содержать: +- строку о результате выполнения задачи, например `true` +- массив, содержащий ID и имя созданного ресурса, например `[12345, "resource_name"]` +- массив, содержащий информацию о восстновленных дисках, например `[{"computeId": 123, "diskId": 456}, {"computeId": 789, "diskId": 10}]` + +Соответственно, для получения информации из поля Result доступны следующие методы: +- ToString(): строковое представление результата выполнения задачи +- ID() и Name(): получение ID и имени созданного в результате выполнения задачи ресурса +- ToMaps(): получение списка карт, содержащих информацию о дисках, восстановленных в результате выполнения задачи. +Все методы оборудованы возвратом ошибок. Непустая ошибка означает, что из поля Result нельзя получить информацию, которую предоставляет метод. + +```go +package main + +import ( + "log" + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/pkg/cloudapi/tasks" + tasks_cb "repository.basistech.ru/BASIS/dynamix-golang-sdk/pkg/cloudbroker/tasks" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + // Создание клиента + client := decort.New(cfg) + + // Создание структуры запроса GetRequest на получение информации о конкретной задаче и выполнение запроса с помощью конвейера + getReq := tasks.GetRequest{ + AuditID: "b6316", + } + task, err := client.CloudAPI().Tasks().Get(context.Background(), getReq) + if err != nil { + log.Fatal(err) + } + + // Получение списка карт с информацией о восстановленных дисках + maps, err := task.Result.ToMaps() + if err != nil { + log.Fatal(err) + } + fmt.Println(maps) + + // Получение строкового результата выполнения задачи task + res, _ := task.Result.ToString() + fmt.Println(res) + + // Создание структуры запроса ListRequest на получение информации о всех задачах и выполнение запроса с помощью конвейера + listReq := tasks_cb.ListRequest{} + tasks, err := client.CloudBroker().Tasks().List(context.Background(), listReq) + if err != nil { + log.Fatal(err) + } + + for _, t := range tasks { + // Получение id ресурса, созданного в результате выполнения задачи t + id, err := task.Result.ID() + if err != nil { + log.Fatal(err) + } + fmt.Println(id) + + // Получение имени ресурса, созданного в результате выполнения задачи t + name, _ := task.Result.Name() + fmt.Println(name) + } +} +``` + +## Работа с legacy клиентом + +Работа с legacy клиентом применяется для пользователей, которые не используют для авторизации decs3o. + +### Настройка конфигурации legacy клиента + +Сначала, необходимо создать переменную конфигурации клиента. Конфигурация состоит как из обязательных, так и необязательных полей. + +| Поле | Тип | Обязательный | Описание | +| ------------- | ------ | ------------ | ------------------------------------------------------------------ | +| Username | string | Да | username legacy пользователя | +| Password | string | Да | пароль legacy пользователя | +| DecortURL | string | Да | URL адрес платформы, с которой будет осуществляться взаимодействие | +| Retries | uint | Нет | Кол-во неудачных попыток выполнения запроса, по умолчанию - 5 | +| Timeout | config.Duration | Нет | Таймаут HTTP клиента, по умолчанию - без ограничений | +| SSLSkipVerify | bool | Нет | Пропуск проверки подлинности сертификата | +| Token | string | Нет | JWT токен | + +#### Пример конфигурации legacy клиента + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" +) + +func main(){ + // Настройка конфигурации + legacyCfg := config.LegacyConfig{ + Username: "", + Password: "", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + legacyCfg.SetTimeout(5 * time.Minute) +} +``` + +#### Парсинг legacy конфигурации из файла + +Также возможно создать переменную конфигурации из JSON или YAML файла, используя функцию `ParseLegacyConfigJSON` (или `ParseLegacyConfigYAML`) из пакета config. +
+*См. пример файлов конфигурации ниже и в директории `samples/`.* + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" +) + +func main() { + // Парсинг конфигурации из YAML-файла + legacyCfg, _ := config.ParseLegacyConfigYAML("") +} +``` + +#### Пример legacy JSON конфигурации + +```json +{ + "username": "", + "password": "", + "decortUrl": "https://mr4.digitalenergy.online", + "retries": 5, + "timeout": "5m", + "sslSkipVerify": true +} +``` + +#### Пример legacy YAML конфигурации +```yaml +username: +password: +decortUrl: https://mr4.digitalenergy.online +retries: 5 +timeout: 5m +sslSkipVerify: true +``` +### Создание legacy клиента + +Создание клиента происходит с помощью функции-строителя `NewLegacy` из основного пакета `decort-sdk`, для избежания проблем с именами, пакету можно присвоить алиас `decort`. Функция принимает конфигурацию, возвращает структуру `DecortClient`, с помощью которой можно взаимодействовать с платформой. + +#### Пример создания legacy клиента + +```go +package main + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + legacyCfg := config.LegacyConfig{ + Username: "", + Password: "", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + legacyCfg.SetTimeout(5 * time.Minute) + + // Создание клиента + legacyClient := decort.NewLegacy(legacyCfg) +} +``` + +#### Пример выполнения запроса + +```go +package main + +import ( + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + legacyCfg := config.LegacyConfig{ + Username: "", + Password: "", + Domain: "dynamix", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + // Создание клиента + legacyClient := decort.NewLegacy(legacyCfg) + + // Создание структуры запроса + // CreateRequest - реквест на создание виртуальной машины + req := kvmx86.CreateRequest{ + RGID: 123, + Name: "compute", + CPU: 4, + RAM: 4096, + ImageID: 321, + } + + // Выполнение запроса + res, err := legacyClient.CloudAPI().KVMX86().Create(context.Background(), req) + if err != nil { + log.Fatal(err) + } + + fmt.Println(res) +} +``` + +## Работа с BVS клиентом + +Работа с BVS клиентом применяется для пользователей, которые используют для авторизации BVS. + +### Настройка параметров BVS в кабинете администратора + +Для корректной работы функции обновления токена необходимо соблюдать следующие условия: + +на странице администратора по следующему пути: домены-<имя вашего домена>-(символ i)-токены +- параметр "Время жизни токена доступа" - устанавливается для всего домена. Не применяется есть по следующему пути: безопасность-клиентские_системы-(символ i)-токены-"Время жизни токена доступа" не выставлено иное время, которое имеет больший приоритет для конкретной клиентской системы +- параметр "Время простоя сессии" - время жизни токена обновления. В случае указания количества минут меньше, чем время жизни токена, то обновление токена будет работать некорректно. Редомендуется указывать время или равное или больше времени жизни токена +- параметр "Максимальное время жизни сессии" - время в течение которого возможно производить обновление токена. Если данный параметр будет равен времени жизни токена, то обновление токена будет работать некорректно. Редомендуется указывать время больше времени жизни токена + +### Настройка конфигурации BVS клиента + +Сначала, необходимо создать переменную конфигурации клиента. Конфигурация состоит как из обязательных, так и необязательных полей. + +| Поле | Тип | Обязательный | Описание | +| ------------- | --------------------------------------------- | ------------ | ------------------------------------------------------------------ | +| Username | string | Да | username пользователя | +| Password | string | Да | пароль пользователя | +| AppID | string | Да | app_id ключа для выполнения запросов | +| AppSecret | string | Да | app_secret ключ для выполнения запроса | +| DecortURL | string | Да | URL адрес платформы, с которой будет осуществляться взаимодействие | +| SSOURL | string | Да | URL адрес сервиса аутентификации и авторизации | +| Domain | string | Да | Имя домена | +| Retries | uint | Нет | Кол-во неудачных попыток выполнения запроса, по умолчанию - 5 | +| Timeout | config.Duration | Нет | Таймаут HTTP клиента, по умолчанию - без ограничений | +| SSLSkipVerify | bool | Нет | Пропуск проверки подлинности сертификата | +| Token | struct{} [см. ниже](#описание-структуры-token)| Нет | JWT токен | +| PathCfg | string | Нет | Путь записи конфигурации в файл | +| PathToken | string | Нет | Путь записи токена в файл | +| TimeToRefresh | uint | Нет | Количество минут, за сколько до истечения срока действия токена выполнится его обновление, по умолчанию - 1 минута | + +### Описание структуры token +| Параметр | Тип | Описание | +| ------------ | ------ | ------------------------------- | +| AccessToken | string | Токен | +| TokenType | string | Тип токена | +| RefreshToken | string | Токен для запроса на обновление | +| Expiry | time | Время жизни токена | + +#### Пример конфигурации BVS клиента + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" +) + +func main(){ + // Настройка конфигурации + BvsCfg := config.BVSConfig{ + AppID: "", + AppSecret: "", + Username: "", + Password: "", + SSOURL: "https://bvs-delta.qa.loc:8443", + DecortURL: "https://delta.qa.loc", + Domain: "dynamix", + Retries: 5, + } + + BvsCfg.SetTimeout(5 * time.Minute) +} +``` + +#### Парсинг BVS конфигурации из файла + +Также возможно создать переменную конфигурации из JSON или YAML файла, используя функцию `ParseConfigBVSJSON` (или `ParseConfigBVSYAML`) из пакета config. +
+*См. пример файлов конфигурации ниже и в директории `samples/`.* + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" +) + +func main() { + // Парсинг конфигурации из YAML-файла + BVSCfg, _ := config.ParseConfigBVSYAML("") +} +``` + +#### Парсинг BVS токена из файла + +Также возможно создать переменную токена из JSON или YAML файла, используя функцию `ParseTokenBVSJSON` (или `ParseTokenBVSYAML`) из пакета config. +
+*См. пример файлов конфигурации ниже и в директории `samples/`.* + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" +) + +func main() { + // Парсинг токена из json-файла + BVSToken, _ := config.ParseTokenBVSJSON("") +} +``` + +#### Пример BVS JSON конфигурации + +```json +{ + "username": "", + "password": "", + "appId": "", + "appSecret": "", + "ssoUrl": "https://bvs-delta.qa.loc:8443", + "decortUrl": "https://delta.qa.loc", + "domain": "dynamix", + "token": { + "access_token": "string_token", + "token_type": "bearer", + "refresh_token": "string_refresh_token", + "expiry": "2023-11-24T12:40:27.954150524+03:00" + }, + "retries": 5, + "sslSkipVerify": true, + "timeout": "5m", + "path_cfg": "config", + "path_token": "token", + "timeToRefresh": 5 +} +``` + +#### Пример BVS YAML конфигурации +```yaml + username: + password: + appId: + appSecret: + ssoUrl: https://bvs-delta.qa.loc:8443 + decortUrl: https://delta.qa.loc + domain: dynamix + token": + access_token: string_token + token_type: bearer + refresh_token: string_refresh_token + expiry: 2023-11-24T12:40:27.954150524+03:00 + retries: 5 + sslSkipVerify: true + timeout: 5m + path_cfg: config + path_token: token + timeToRefresh: 5 +``` +### Создание BVS клиента + +Создание клиента происходит с помощью функции-строителя `NewBVS` из основного пакета `decort-sdk`, для избежания проблем с именами, пакету можно присвоить алиас `decort`. Функция принимает конфигурацию, возвращает структуру `DecortClient`, с помощью которой можно взаимодействовать с платформой. + +#### Пример создания BVS клиента + +```go +package main + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + BVSCfg := config.BVSConfig{ + Username: "", + Password: "", + AppID: "", + AppSecret: "", + SSOURL: "https://bvs-delta.qa.loc:8443", + DecortURL: "https://mr4.digitalenergy.online", + Domain: "dynamix", + Retries: 5, + } + + BVSCfg.SetTimeout(5 * time.Minute) + + // Создание клиента + BVSClient := decort.NewBVS(BVSCfg) +} +``` + +#### Пример получения BVS токена + +В случае указания значения в переменной конфигурации `PathCfg` токен и конфигурация будут записаны в файл в формате `json`, переменная `PathToken` служит для записи токена в файл в формате `json` + +```go +package main + +import ( + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + BVSCfg := config.BVSConfig{ + Username: "", + Password: "", + AppID: "", + AppSecret: "", + SSOURL: "https://bvs-delta.qa.loc:8443", + DecortURL: "https://mr4.digitalenergy.online", + Domain: "dynamix", + PathCfg: "config", + Retries: 5, + } + + // Создание клиента + BVSClient := decort.NewBVS(BVSCfg) + + // Выполнение запроса на получение токена + token, err := BVSClient.GetToken(context.Background()) + if err != nil { + log.Fatal(err) + } + + fmt.Println(token) +} +``` + +#### Пример обновления BVS токена + +В случае указания значения в переменной конфигурации `PathCfg` обновленный токен и конфигурация будут записаны в файл в формате `json`, переменная `PathToken` служит для записи токена в файл в формате `json` + +```go +package main + +import ( + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + BVSCfg := config.BVSConfig{ + Username: "", + Password: "", + AppID: "", + AppSecret: "", + SSOURL: "https://bvs-delta.qa.loc:8443", + DecortURL: "https://mr4.digitalenergy.online", + Domain: "dynamix", + PathToken: "token", + Retries: 5, + } + + // Создание клиента + BVSClient := decort.NewBVS(BVSCfg) + + // Выполнение запроса на обновление токена + token, err := BVSClient.RefreshToken(context.Background()) + if err != nil { + log.Fatal(err) + } + + fmt.Println(token) +} +``` + +#### Пример выполнения запроса + +```go +package main + +import ( + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + BVSCfg := config.BVSConfig{ + Username: "", + Password: "", + AppID: "", + AppSecret: "", + SSOURL: "https://bvs-delta.qa.loc:8443", + DecortURL: "https://mr4.digitalenergy.online", + Domain: "dynamix", + Retries: 5, + } + + // Создание клиента + BVSClient := decort.NewBVS(BVSCfg) + + // Создание структуры запроса + // CreateRequest - реквест на создание виртуальной машины + req := kvmx86.CreateRequest{ + RGID: 123, + Name: "compute", + CPU: 4, + RAM: 4096, + ImageID: 321, + } + + // Выполнение запроса + res, err := BVSClient.CloudAPI().KVMX86().Create(context.Background(), req) + if err != nil { + log.Fatal(err) + } + + fmt.Println(res) +} +``` +#### Пример валидации запросов, имеющих в своей структуре поле RAM (или MasterRam/WorkerRAM) + +Если структура запроса содержит поле RAM (или MasterRam/WorkerRAM), то он может быть проверен на валидность. Для этого запрос должен быть передан в функцию ValidateRAM. Вторым аргументом ValidateRAM ожидает число uint64. Рекомендуется использовать константу constants.RAM_DIVISIBILITY. Функция проверит кратно ли значение поля RAM (или MasterRam/WorkerRAM) этому числу. + +#### Пример выполнения запроса + +```go +package main + +import ( + "context" + "fmt" + "log" + "os" + + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/internal/validators" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/pkg/cloudbroker/kvmx86" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso-delta.qa.loc:8443", + DecortURL: "https://delta.qa.loc", + Retries: 5, + SSLSkipVerify: true, + } + + // Создание клиента + client := decort.New(cfg) + + // Создание структуры запроса + // CreateRequest - реквест на создание виртуальной машины + req := kvmx86.CreateRequest{ + Name: "kvmx86", + RGID: 907, + CPU: 2048, + RAM: 1024, + ImageID: 161, + } + + // Валидация запроса + err := validators.ValidateRAM(req, constants.RAM_DIVISIBILITY) + if err != nil { + log.Fatalf("unable to validate request: %v", err) + } + //Выполнение запроса с помощью конвейера + res, err := client.CloudBroker().KVMX86().Create(context.Background(), req) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + log.Println(res) + +} +``` + +## Работа с универсальным клиентом + +Работа с универсальным клиентом позволяет использовать любой тип авторизации + +### Настройка конфигурации универсального клиента + +| Параметр | Тип | Обязательный | Описание | +| --- | --- | --- | --- | +| Decs3oConfig | *Config | Нет | Конфигурация Decs3o | +| BVSConfig | *BVSConfig | Нет | Конфигурация BVS | +| LegacyConfig | string | Нет | Конфигурация Legacy | + +В универсальном клиенте можно использовать только один тип конфигурации одновременно. + +#### Пример конфигурации универсального клиента + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" +) + +func main(){ + // Настройка конфигурации + BVSConfig := config.BVSConfig{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + DecortURL: "https://mr4.digitalenergy.online", + Username: "", + Password: "", + Retries: 5, + } + + BVSConfig.SetTimeout(5 * time.Minute) + + cfg := config.UniversalConfig{ + BVSConfig: &BVScfg, + } +} +``` + +#### Создание универсального клиента + +Создание клиента происходит с помощью функции-строителя `NewUniversal` из основного пакета `decort-sdk`, для избежания проблем с именами, пакету можно присвоить алиас `decort`. Функция принимает конфигурацию, возвращает структуру `ClientInterface`, с помощью которой можно взаимодействовать с платформой. + +#### Пример создания универсального клиента + +```go +package main + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + legacyCfg := config.LegacyConfig{ + Username: "", + Password: "", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + legacyCfg.SetTimeout(5 * time.Minute) + + cfg := config.UniversalConfig{ + LegacyConfig: &legacyCfg, + } + + // Создание клиента + universalClient := decort.NewUniversal(cfg) +} +``` + +#### Пример выполнения запроса + +```go +package main + +import ( + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk" +) + +func main() { + // Настройка конфигурации + legacyCfg := config.LegacyConfig{ + Username: "", + Password: "", + Domain: "dynamix", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + legacyCfg.SetTimeout(5 * time.Minute) + + cfg := config.UniversalConfig{ + LegacyConfig: &legacyCfg, + } + + // Создание клиента + universalClient := decort.NewUniversal(cfg) + + // Создание структуры запроса + // CreateRequest - реквест на создание виртуальной машины + req := kvmx86.CreateRequest{ + RGID: 123, + Name: "compute", + CPU: 4, + RAM: 4096, + ImageID: 321, + } + + // Выполнение запроса + res, err := universalClient.CloudAPI().KVMX86().Create(context.Background(), req) + if err != nil { + log.Fatal(err) + } + + fmt.Println(res) +} +``` \ No newline at end of file diff --git a/client.go b/client.go new file mode 100644 index 0000000..94a0f79 --- /dev/null +++ b/client.go @@ -0,0 +1,377 @@ +package decortsdk + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "mime/multipart" + "net/http" + "reflect" + "strconv" + "strings" + "sync" + "time" + + "github.com/google/go-querystring/query" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker" +) + +// DecortClient is HTTP-client for platform +type DecortClient struct { + decortURL string + client *http.Client + cfg config.Config + expiryTime time.Time + mutex *sync.Mutex +} + +// Сlient builder +func New(cfg config.Config) *DecortClient { + if cfg.Retries == 0 { + cfg.Retries = 5 + } + + var expiryTime time.Time + + if cfg.Token != "" { + expiryTime = time.Now().AddDate(0, 0, 1) + } + + return &DecortClient{ + decortURL: cfg.DecortURL, + client: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + //nolint:gosec + InsecureSkipVerify: cfg.SSLSkipVerify, + }, + }, + }, + cfg: trimConfig(&cfg), + expiryTime: expiryTime, + mutex: &sync.Mutex{}, + } +} + +// CloudAPI builder +func (dc *DecortClient) CloudAPI() *cloudapi.CloudAPI { + return cloudapi.New(dc) +} + +// CloudBroker builder +func (dc *DecortClient) CloudBroker() *cloudbroker.CloudBroker { + return cloudbroker.New(dc) +} + +// DecortApiCall method for sending requests to the platform +func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { + + var body *bytes.Buffer + var ctype string + + byteSlice, ok := params.([]byte) + if ok { + body = bytes.NewBuffer(byteSlice) + // ctype = "application/x-iso9660-image" + ctype = "application/octet-stream" + } else { + values, err := query.Values(params) + if err != nil { + return nil, err + } + body = bytes.NewBufferString(values.Encode()) + } + + req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+constants.RESTMACHINE+url, body) + if err != nil { + return nil, err + } + + // get token + if err = dc.getToken(ctx); err != nil { + return nil, err + } + // perform request + respBytes, err := dc.do(req, ctype) + if err != nil { + return nil, err + } + + return respBytes, err +} + +// DecortApiCallMP method for sending requests to the platform +func (dc *DecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) { + body, ctype, err := multiPartReq(params) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+constants.RESTMACHINE+url, body) + if err != nil { + return nil, err + } + + // get token + if err = dc.getToken(ctx); err != nil { + return nil, err + } + + // perform request + respBytes, err := dc.do(req, ctype) + if err != nil { + return nil, err + } + + return respBytes, err +} + +func (dc *DecortClient) getToken(ctx context.Context) error { + dc.mutex.Lock() + defer dc.mutex.Unlock() + + // new token is not needed + if dc.cfg.Token != "" && !time.Now().After(dc.expiryTime) { + return nil + } + + // set up request headers and body + body := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&response_type=id_token", dc.cfg.AppID, dc.cfg.AppSecret) + bodyReader := strings.NewReader(body) + + dc.cfg.SSOURL = strings.TrimSuffix(dc.cfg.SSOURL, "/") + + req, _ := http.NewRequestWithContext(ctx, "POST", dc.cfg.SSOURL+"/v1/oauth/access_token", bodyReader) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + // request token + resp, err := dc.client.Do(req) + if err != nil || resp == nil { + return fmt.Errorf("cannot get token: %w", err) + } + defer resp.Body.Close() + + var tokenBytes []byte + tokenBytes, 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", tokenBytes) + } + + // save token in config + token := string(tokenBytes) + dc.cfg.Token = token + dc.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 (dc *DecortClient) do(req *http.Request, ctype string) ([]byte, error) { + // set up request headers and body + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + if ctype != "" { + req.Header.Set("Content-Type", ctype) + } + + req.Header.Add("Authorization", "bearer "+dc.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 := dc.client.Do(req) + if resp != nil { + defer resp.Body.Close() + } + + // retries logic GOES HERE + // get http response + //var resp *http.Response + //for i := uint64(0); i < dc.cfg.Retries; i++ { + // req := req.Clone(req.Context()) + // req.Body = io.NopCloser(bytes.NewBuffer(buf)) + // + // if i > 0 { + // time.Sleep(5 * time.Second) // no time sleep for the first request + // } + // + // resp, err = dc.client.Do(req) + // + // // stop retries on success and close response body + // if resp != nil { + // defer resp.Body.Close() + // } + // if err == nil { + // break + // } + // + // // retries in case of connection errors with time sleep + // if isConnectionError(err) { + // continue + // } + // + // // return error in case of non-connection error + // return nil, err + //} + + // 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) +} + +// isConnectionError checks if given error falls within specific and associated connection errors +//func isConnectionError(err error) bool { +// if strings.Contains(err.Error(), "connection reset by peer") { +// return true +// } +// if errors.Is(err, io.EOF) { +// return true +// } +// +// return false +//} + +// multiPartReq writes the request structure to the request body, and also returns string of the content-type +func multiPartReq(params interface{}) (*bytes.Buffer, string, error) { + reqBody := &bytes.Buffer{} + writer := multipart.NewWriter(reqBody) + values := reflect.ValueOf(params) + types := values.Type() + defer writer.Close() + for i := 0; i < values.NumField(); i++ { + if !values.Field(i).IsValid() { + continue + } + + if values.Field(i).IsZero() { + continue + } + + if file, ok := constants.FileName[types.Field(i).Name]; ok { + part, err := writer.CreateFormFile(trimString(types.Field(i)), file) + if err != nil { + return &bytes.Buffer{}, "", err + } + _, err = io.Copy(part, strings.NewReader(valueToString(values.Field(i).Interface()))) + if err != nil { + return &bytes.Buffer{}, "", err + } + continue + } + + if values.Field(i).Type().Kind() == reflect.Slice { + switch slice := values.Field(i).Interface().(type) { + case []string: + if validators.IsInSlice(trimString(types.Field(i)), constants.K8sValues) { + code, err := json.Marshal(slice) + if err != nil { + return &bytes.Buffer{}, "", err + } + err = writer.WriteField(trimString(types.Field(i)), string(code)) + if err != nil { + return &bytes.Buffer{}, "", err + } + } else { + for _, val := range slice { + err := writer.WriteField(trimString(types.Field(i)), val) + if err != nil { + return &bytes.Buffer{}, "", err + } + } + } + case []uint: + for _, val := range slice { + err := writer.WriteField(trimString(types.Field(i)), strconv.FormatUint(uint64(val), 10)) + if err != nil { + return &bytes.Buffer{}, "", err + } + } + case []uint64: + for _, val := range slice { + err := writer.WriteField(trimString(types.Field(i)), strconv.FormatUint(val, 10)) + if err != nil { + return &bytes.Buffer{}, "", err + } + } + case []map[string]interface{}: + for _, val := range slice { + encodeStr, err := json.Marshal(val) + if err != nil { + return &bytes.Buffer{}, "", err + } + err = writer.WriteField(trimString(types.Field(i)), string(encodeStr)) + if err != nil { + return &bytes.Buffer{}, "", err + } + } + default: + return &bytes.Buffer{}, "", fmt.Errorf("unsupported slice type:%T", slice) + } + continue + } + + err := writer.WriteField(trimString(types.Field(i)), valueToString(values.Field(i).Interface())) + if err != nil { + return &bytes.Buffer{}, "", err + } + } + ct := writer.FormDataContentType() + return reqBody, ct, nil +} + +func valueToString(a any) string { + switch str := a.(type) { + case string: + return str + case uint: + return strconv.FormatUint(uint64(str), 10) + case uint64: + return strconv.FormatUint(str, 10) + case bool: + return strconv.FormatBool(str) + default: + return "" + } +} + +func trimString(el reflect.StructField) string { + return strings.TrimSuffix(el.Tag.Get("url"), ",omitempty") +} + +func trimConfig(cfg *config.Config) config.Config { + cfg.SSOURL = strings.TrimSuffix(cfg.SSOURL, "/") + cfg.DecortURL = strings.TrimSuffix(cfg.DecortURL, "/") + return *cfg +} diff --git a/client_bvs.go b/client_bvs.go new file mode 100644 index 0000000..b5ebfcf --- /dev/null +++ b/client_bvs.go @@ -0,0 +1,395 @@ +package decortsdk + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + "sync" + "time" + + "github.com/google/go-querystring/query" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker" +) + +// BVSDecortClient is HTTP-client for platform +type BVSDecortClient struct { + client *http.Client + cfg config.BVSConfig + mutex *sync.Mutex + decortURL string +} + +type tokenJSON struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + RefreshToken string `json:"refresh_token"` + ExpiresIn uint64 `json:"expires_in"` +} + +// Сlient builder +func NewBVS(cfg config.BVSConfig) *BVSDecortClient { + if cfg.Retries == 0 { + cfg.Retries = 5 + } + if cfg.TimeToRefresh == 0 { + cfg.TimeToRefresh = 1 + } + + return &BVSDecortClient{ + decortURL: cfg.DecortURL, + client: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + //nolint:gosec + InsecureSkipVerify: cfg.SSLSkipVerify, + }, + }, + }, + cfg: trimBVSConfig(&cfg), + mutex: &sync.Mutex{}, + } +} + +// CloudAPI builder +func (bdc *BVSDecortClient) CloudAPI() *cloudapi.CloudAPI { + return cloudapi.New(bdc) +} + +// CloudBroker builder +func (bdc *BVSDecortClient) CloudBroker() *cloudbroker.CloudBroker { + return cloudbroker.New(bdc) +} + +// DecortApiCall method for sending requests to the platform +func (bdc *BVSDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { + var body *bytes.Buffer + var ctype string + + byteSlice, ok := params.([]byte) + if ok { + body = bytes.NewBuffer(byteSlice) + // ctype = "application/x-iso9660-image" + ctype = "application/octet-stream" + } else { + values, err := query.Values(params) + if err != nil { + return nil, err + } + body = bytes.NewBufferString(values.Encode()) + } + + req, err := http.NewRequestWithContext(ctx, method, bdc.decortURL+constants.RESTMACHINE+url, body) + if err != nil { + return nil, err + } + + // get token + if bdc.cfg.Token.AccessToken == "" { + if _, err = bdc.GetToken(ctx); err != nil { + return nil, err + } + } + + // refresh token + if bdc.cfg.Token.RefreshToken != "" && bdc.cfg.Token.Expiry.Add(-time.Duration(bdc.cfg.TimeToRefresh)*time.Minute).Before(time.Now()) { + if _, err := bdc.RefreshToken(ctx); err != nil { + if _, err = bdc.GetToken(ctx); err != nil { + return nil, err + } + } + } + + // perform request + reqCopy := req.Clone(ctx) + respBytes, err := bdc.do(req, ctype) + if err == nil { + return respBytes, nil + } + + // get token and retry in case of access denied + if err.Error() == "access is denied" { + _, err = bdc.GetToken(ctx) + if err != nil { + return nil, err + } + + respBytes, err = bdc.do(reqCopy, "") + if err != nil { + return nil, err + } + } + + return respBytes, err +} + +func (bdc *BVSDecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) { + body, ctype, err := multiPartReq(params) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, method, bdc.decortURL+constants.RESTMACHINE+url, body) + if err != nil { + return nil, err + } + + // get token + if bdc.cfg.Token.AccessToken == "" { + if _, err = bdc.GetToken(ctx); err != nil { + return nil, err + } + } + + // refresh token + if bdc.cfg.Token.RefreshToken != "" && bdc.cfg.Token.Expiry.Add(-time.Duration(bdc.cfg.TimeToRefresh)*time.Minute).Before(time.Now()) { + if _, err := bdc.RefreshToken(ctx); err != nil { + if _, err = bdc.GetToken(ctx); err != nil { + return nil, err + } + } + } + + // perform request + reqCopy := req.Clone(ctx) + respBytes, err := bdc.do(req, ctype) + if err == nil { + return respBytes, nil + } + + // get token and retry in case of access denied + if err.Error() == "access is denied" { + _, err = bdc.GetToken(ctx) + if err != nil { + return nil, err + } + + respBytes, err = bdc.do(reqCopy, ctype) + if err != nil { + return nil, err + } + } + return respBytes, err +} + +// GetToken allows you to get a token and returns the token structure. When specifying the PathCfg variable, +// the token and configuration will be written to a file. +// When specifying the PathToken variable, the token will be written to a file. +func (bdc *BVSDecortClient) GetToken(ctx context.Context) (config.Token, error) { + bdc.mutex.Lock() + defer bdc.mutex.Unlock() + + // set up request headers and body + body := fmt.Sprintf("grant_type=password&client_id=%s&client_secret=%s&username=%s&password=%s&response_type=token&scope=openid", bdc.cfg.AppID, bdc.cfg.AppSecret, bdc.cfg.Username, bdc.cfg.Password) + bodyReader := strings.NewReader(body) + + bdc.cfg.SSOURL = strings.TrimSuffix(bdc.cfg.SSOURL, "/") + + req, _ := http.NewRequestWithContext(ctx, "POST", bdc.cfg.SSOURL+"/realms/"+bdc.cfg.Domain+"/protocol/openid-connect/token", bodyReader) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + // request token + resp, err := bdc.client.Do(req) + if err != nil || resp == nil { + return config.Token{}, fmt.Errorf("cannot get token: %w", err) + } + defer resp.Body.Close() + + var tokenBytes []byte + tokenBytes, err = io.ReadAll(resp.Body) + if err != nil { + return config.Token{}, fmt.Errorf("cannot get token: %w", err) + } + + if resp.StatusCode != 200 { + return config.Token{}, fmt.Errorf("cannot get token: %s", tokenBytes) + } + + // save token in config + var tj tokenJSON + if err = json.Unmarshal(tokenBytes, &tj); err != nil { + return config.Token{}, fmt.Errorf("cannot unmarshal token: %w", err) + } + + bdc.cfg.Token = config.Token{ + AccessToken: tj.AccessToken, + TokenType: tj.TokenType, + RefreshToken: tj.RefreshToken, + Expiry: tj.expiry(), + } + + if bdc.cfg.PathCfg != "" { + ser, _ := bdc.cfg.Serialize("", " ") + _ = ser.WriteToFile(bdc.cfg.PathCfg) + } + + if bdc.cfg.PathToken != "" { + ser, _ := bdc.cfg.Token.Serialize("", " ") + _ = ser.WriteToFile(bdc.cfg.PathToken) + } + + return bdc.cfg.Token, nil +} + +// RefreshToken allows you to refresh a token and returns the token structure. When specifying the PathCfg variable, +// the token and configuration will be written to a file. +// When specifying the PathToken variable, the token will be written to a file +func (bdc *BVSDecortClient) RefreshToken(ctx context.Context) (config.Token, error) { + bdc.mutex.Lock() + defer bdc.mutex.Unlock() + + // set up request headers and body + body := fmt.Sprintf("grant_type=refresh_token&client_id=%s&client_secret=%s&refresh_token=%s&scope=openid", bdc.cfg.AppID, bdc.cfg.AppSecret, bdc.cfg.Token.RefreshToken) + bodyReader := strings.NewReader(body) + + bdc.cfg.SSOURL = strings.TrimSuffix(bdc.cfg.SSOURL, "/") + + req, _ := http.NewRequestWithContext(ctx, "POST", bdc.cfg.SSOURL+"/realms/"+bdc.cfg.Domain+"/protocol/openid-connect/token", bodyReader) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + // refresh token + resp, err := bdc.client.Do(req) + if err != nil || resp == nil { + return config.Token{}, fmt.Errorf("cannot refresh token: %w", err) + } + defer resp.Body.Close() + + var tokenBytes []byte + tokenBytes, err = io.ReadAll(resp.Body) + if err != nil { + return config.Token{}, fmt.Errorf("cannot refresh token: %w", err) + } + + if resp.StatusCode != 200 { + return config.Token{}, fmt.Errorf("cannot refresh token: %s", tokenBytes) + } + + // save token in config + var tj tokenJSON + if err = json.Unmarshal(tokenBytes, &tj); err != nil { + return config.Token{}, fmt.Errorf("cannot unmarshal after refresh token: %w", err) + } + + bdc.cfg.Token = config.Token{ + AccessToken: tj.AccessToken, + TokenType: tj.TokenType, + RefreshToken: tj.RefreshToken, + Expiry: tj.expiry(), + } + + if bdc.cfg.PathCfg != "" { + ser, _ := bdc.cfg.Serialize("", " ") + _ = ser.WriteToFile(bdc.cfg.PathCfg) + } + + if bdc.cfg.PathToken != "" { + ser, _ := bdc.cfg.Token.Serialize("", " ") + _ = ser.WriteToFile(bdc.cfg.PathToken) + } + + return bdc.cfg.Token, nil +} + +func (e *tokenJSON) expiry() (t time.Time) { + if v := e.ExpiresIn; v != 0 { + return time.Now().Add(time.Duration(v) * time.Second) + } + return +} + +// 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 (bdc *BVSDecortClient) do(req *http.Request, ctype string) ([]byte, error) { + // set up request headers and body + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + if ctype != "" { + req.Header.Set("Content-Type", ctype) + } + + req.Header.Add("Authorization", "bearer "+bdc.cfg.Token.AccessToken) + 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 := bdc.client.Do(req) + if resp != nil { + defer resp.Body.Close() + } + + // retries logic GOES HERE + // get http response + //var resp *http.Response + //for i := uint64(0); i < bdc.cfg.Retries; i++ { + // req := req.Clone(req.Context()) + // req.Body = io.NopCloser(bytes.NewBuffer(buf)) + // + // if i > 0 { + // time.Sleep(5 * time.Second) // no time sleep for the first request + // } + // + // resp, err = bdc.client.Do(req) + // + // // stop retries on success and close response body + // if resp != nil { + // defer resp.Body.Close() + // } + // if err == nil { + // break + // } + // + // // retries in case of connection errors with time sleep + // if isConnectionError(err) { + // continue + // } + // + // // return error in case of non-connection error + // return nil, err + //} + + // handle http request errors + if err != nil { + return nil, err + } + if resp == nil { + return nil, fmt.Errorf("got empty response without error") + } + + var respBytes []byte + respBytes, err = io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // handle access denied and successful request + if resp.StatusCode == 401 { + return respBytes, errors.New("access is denied") + } + if resp.StatusCode == 200 { + return respBytes, nil + } + + // handle errors with other status codes + err = fmt.Errorf("%s", respBytes) + return nil, fmt.Errorf("could not execute request: %w", err) +} + +func trimBVSConfig(cfg *config.BVSConfig) config.BVSConfig { + cfg.SSOURL = strings.TrimSuffix(cfg.SSOURL, "/") + cfg.DecortURL = strings.TrimSuffix(cfg.DecortURL, "/") + return *cfg +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..f51d1bd --- /dev/null +++ b/config/config.go @@ -0,0 +1,101 @@ +package config + +import ( + "encoding/json" + "os" + "time" + + "gopkg.in/yaml.v3" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// Configuration for creating request to platform +type Config struct { + // JWT platform token + // Required: false + // Example: "qwqwdfwv68979we0q9bfv7e9sbvd89798qrwv97ff" + Token string `json:"token" yaml:"token"` + + // Application (client) identifier for authorization + // in the cloud platform controller in oauth2 mode. + // Required: true + // Example: "ewqfrvea7s890avw804389qwguf234h0otfi3w4eiu" + AppID string `json:"appId" yaml:"appId" validate:"required"` + + // Application (client) secret code for authorization + // in the cloud platform controller in oauth2 mode. + // Example: "frvet09rvesfis0c9erv9fsov0vsdfi09ovds0f" + AppSecret string `json:"appSecret" yaml:"appSecret" validate:"required"` + + // Platform authentication service address + // Required: true + // Example: "https://sso.digitalenergy.online" + SSOURL string `json:"ssoUrl" yaml:"ssoUrl" validate:"url"` + + // The address of the platform on which the actions are planned + // Required: true + // Example: "https://mr4.digitalenergy.online" + DecortURL string `json:"decortUrl" yaml:"decortUrl" 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/config_bvs.go b/config/config_bvs.go new file mode 100644 index 0000000..482d3c3 --- /dev/null +++ b/config/config_bvs.go @@ -0,0 +1,216 @@ +package config + +import ( + "encoding/json" + "os" + "time" + + "gopkg.in/yaml.v3" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type BVSConfig struct { + // ServiceAccount username + // Required: true + // Example : "osh_mikoev" + Username string `json:"username" yaml:"username" validate:"required"` + + // ServiceAccount password + // Required: true + // Example: "[1o>hYkjnJr)HI78q7t&#%8Lm" + Password string `json:"password" yaml:"password" validate:"required"` + + // Domain name + // Required: true + // Example: "dynamix" + Domain string `json:"domain" yaml:"domain" validate:"required"` + + // Application (client) identifier for authorization + // in the cloud platform controller in oauth2 mode. + // Required: true + // Example: "ewqfrvea7s890avw804389qwguf234h0otfi3w4eiu" + AppID string `json:"appId" yaml:"appId" validate:"required"` + + // Application (client) secret code for authorization + // in the cloud platform controller in oauth2 mode. + // Example: "frvet09rvesfis0c9erv9fsov0vsdfi09ovds0f" + AppSecret string `json:"appSecret" yaml:"appSecret" validate:"required"` + + // Platform authentication service address + // Required: true + // Example: "https://sso.digitalenergy.online" + SSOURL string `json:"ssoUrl" yaml:"ssoUrl" validate:"url"` + + // The address of the platform on which the actions are planned + // Required: true + // Example: "https://mr4.digitalenergy.online" + DecortURL string `json:"decortUrl" yaml:"decortUrl" validate:"url"` + + // JWT platform token + // Required: false + // Example: "qwqwdfwv68979we0q9bfv7e9sbvd89798qrwv97ff" + Token Token `json:"token" yaml:"token"` + + // 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"` + + // The path of the configuration file entry + // Required: false + PathCfg string `json:"path_cfg" yaml:"path_cfg"` + + // The path of the token file entry + // Required: false + PathToken string `json:"path_token" yaml:"path_token"` + + // The number of minutes before the expiration of the token, a refresh will be made + // Required: false + TimeToRefresh int64 `json:"timeToRefresh" yaml:"timeToRefresh"` +} + +type Token struct { + // AccessToken is the token that authorizes and authenticates + // the requests. + // Required: false + AccessToken string `json:"access_token" yaml:"access_token"` + + // TokenType is the type of token. + // The Type method returns either this or "Bearer", the default. + // Required: false + TokenType string `json:"token_type" yaml:"token_type"` + + // RefreshToken is a token that's used by the application + // (as opposed to the user) to refresh the access token + // if it expires. + // Required: false + RefreshToken string `json:"refresh_token" yaml:"refresh_token"` + + // Expiry is the optional expiration time of the access token. + // Required: false + Expiry time.Time `json:"expiry" yaml:"expiry"` +} + +// SetTimeout is used to set HTTP client timeout. +func (c *BVSConfig) SetTimeout(dur time.Duration) { + c.Timeout = Duration(dur) +} + +// ParseConfigJSON parses Config from specified JSON-formatted file. +func ParseConfigBVSJSON(path string) (BVSConfig, error) { + file, err := os.ReadFile(path) + if err != nil { + return BVSConfig{}, err + } + + var config BVSConfig + + err = json.Unmarshal(file, &config) + if err != nil { + return BVSConfig{}, err + } + + err = validators.ValidateConfig(config) + if err != nil { + return BVSConfig{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return config, nil +} + +// ParseConfigJSON parses Token from specified JSON-formatted file. +func ParseTokenBVSJSON(path string) (Token, error) { + file, err := os.ReadFile(path) + if err != nil { + return Token{}, err + } + + var token Token + + err = json.Unmarshal(file, &token) + if err != nil { + return Token{}, err + } + + err = validators.ValidateConfig(token) + if err != nil { + return Token{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return token, nil +} + +// ParseTokenBVSYAML parses Token from specified YAML-formatted file. +func ParseTokenBVSYAML(path string) (Token, error) { + file, err := os.ReadFile(path) + if err != nil { + return Token{}, err + } + + var token Token + + err = yaml.Unmarshal(file, &token) + if err != nil { + return Token{}, err + } + + err = validators.ValidateConfig(token) + if err != nil { + return Token{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return token, nil +} + +// ParseConfigYAML parses Config from specified YAML-formatted file. +func ParseConfigBVSYAML(path string) (BVSConfig, error) { + file, err := os.ReadFile(path) + if err != nil { + return BVSConfig{}, err + } + + var config BVSConfig + + err = yaml.Unmarshal(file, &config) + if err != nil { + return BVSConfig{}, err + } + + err = validators.ValidateConfig(config) + if err != nil { + return BVSConfig{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return config, nil +} + +func (t Token) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(t, prefix, indent) + } + + return json.Marshal(t) +} + +func (c BVSConfig) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(c, prefix, indent) + } + + return json.Marshal(c) +} diff --git a/config/legacy-config.go b/config/legacy-config.go new file mode 100644 index 0000000..64eb9d6 --- /dev/null +++ b/config/legacy-config.go @@ -0,0 +1,95 @@ +package config + +import ( + "encoding/json" + "os" + "time" + + "gopkg.in/yaml.v3" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// Legacy client configuration +type LegacyConfig struct { + // ServiceAccount username + // Required: true + // Example : "osh_mikoev" + Username string `json:"username" yaml:"username" validate:"required"` + + // ServiceAccount password + // Required: true + // 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://mr4.digitalenergy.online" + DecortURL string `json:"decortUrl" yaml:"decortUrl" 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 *LegacyConfig) SetTimeout(dur time.Duration) { + c.Timeout = Duration(dur) +} + +// ParseLegacyConfigJSON parses LegacyConfig from specified JSON-formatted file. +func ParseLegacyConfigJSON(path string) (LegacyConfig, error) { + file, err := os.ReadFile(path) + if err != nil { + return LegacyConfig{}, err + } + + var config LegacyConfig + + err = json.Unmarshal(file, &config) + if err != nil { + return LegacyConfig{}, err + } + + err = validators.ValidateConfig(config) + if err != nil { + return LegacyConfig{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return config, nil +} + +// ParseLegacyConfigYAML parses LegacyConfig from specified YAML-formatted file. +func ParseLegacyConfigYAML(path string) (LegacyConfig, error) { + file, err := os.ReadFile(path) + if err != nil { + return LegacyConfig{}, err + } + + var config LegacyConfig + + err = yaml.Unmarshal(file, &config) + if err != nil { + return LegacyConfig{}, err + } + + err = validators.ValidateConfig(config) + if err != nil { + return LegacyConfig{}, 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/config/universal-config.go b/config/universal-config.go new file mode 100644 index 0000000..7dc0dfc --- /dev/null +++ b/config/universal-config.go @@ -0,0 +1,8 @@ +package config + +// UniversalConfig combines configurations for different types of clients +type UniversalConfig struct { + Decs3oConfig *Config `json:"decs3oConfig,omitempty" yaml:"decs3oConfig,omitempty"` + BVSConfig *BVSConfig `json:"bvsConfig,omitempty" yaml:"bvsConfig,omitempty"` + LegacyConfig *LegacyConfig `json:"legacyConfig,omitempty" yaml:"legacyConfig,omitempty"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..40fd282 --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module repository.basistech.ru/BASIS/dynamix-golang-sdk/v9 + +go 1.20 + +require ( + github.com/go-playground/validator/v10 v10.11.2 + github.com/google/go-querystring v1.1.0 + github.com/joho/godotenv v1.5.1 + gopkg.in/yaml.v3 v3.0.1 +) + +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..a63c3b7 --- /dev/null +++ b/go.sum @@ -0,0 +1,40 @@ +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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/interfaces/caller.go b/interfaces/caller.go new file mode 100644 index 0000000..c40b0ba --- /dev/null +++ b/interfaces/caller.go @@ -0,0 +1,12 @@ +package interfaces + +import "context" + +// Interface for sending requests to platform +type Caller interface { + // DecortApiCall method for sending requests to the platform + DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) + + // DecortApiCallMP method for sending requests to the platform + DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) +} diff --git a/interfaces/request.go b/interfaces/request.go new file mode 100644 index 0000000..6d1066b --- /dev/null +++ b/interfaces/request.go @@ -0,0 +1,7 @@ +package interfaces + +// Interface to valiate RAM values +type RequestWithRAM interface { + // GetRAM returns RAM values + GetRAM() map[string]uint64 +} diff --git a/internal/constants/constants.go b/internal/constants/constants.go new file mode 100644 index 0000000..3ce48de --- /dev/null +++ b/internal/constants/constants.go @@ -0,0 +1,14 @@ +package constants + +const ( + RESTMACHINE = "/restmachine" + + // RAM_DIVISIBILITY sets divisibility of RAM value + RAM_DIVISIBILITY uint64 = 128 +) + +var FileName = map[string]string{ + "OidcCertificate": "ca.crt", +} + +var K8sValues = []string{"labels", "taints", "annotations, additionalSANs"} 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/serialization/serialize.go b/internal/serialization/serialize.go new file mode 100644 index 0000000..9e3cc65 --- /dev/null +++ b/internal/serialization/serialize.go @@ -0,0 +1,18 @@ +package serialization + +import ( + "os" +) + +type Writable interface { + WriteToFile(string) error +} + +type Serialized []byte + +// WriteToFile writes serialized data to specified file. +// +// Make sure to use .json extension for best compatibility. +func (s Serialized) WriteToFile(path string) error { + return os.WriteFile(path, s, 0600) +} diff --git a/internal/validators/custom.go b/internal/validators/custom.go new file mode 100644 index 0000000..f3a3992 --- /dev/null +++ b/internal/validators/custom.go @@ -0,0 +1,402 @@ +package validators + +import ( + "errors" + "fmt" + "net/url" + "reflect" + "regexp" + "strings" + + "github.com/go-playground/validator/v10" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/multierror" +) + +// computeDriverValidator is used to validate Driver field in kvmx86 create. +func computeDriverValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computeDriverValues) +} + +// protoValidator is used to validate Proto fields. +func protoValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, protoValues) +} + +// accessTypeValidator is used to validate AccessType fields. +func accessTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, accessTypeValues) +} + +// resTypesValidator is used to validate ResTypes fields. +func resTypesValidator(fe validator.FieldLevel) bool { + fieldSlice, ok := fe.Field().Interface().([]string) + if !ok { + return false + } + + for _, value := range fieldSlice { + if !IsInSlice(value, resTypesValues) { + return false + } + } + + return true +} + +// driverValidator is used to validate Driver fields. +func driverValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, driverValues) +} + +// accountCUTypeValidator is used to validate CUType field. +func accountCUTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, accountCUTypeValues) +} + +// bserviceModeValidator is used to validate Mode field. +func bserviceModeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, bserviceModeValues) +} + +// computeTopologyValidator is used to validate Topology field. +func computeTopologyValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computeTopologyValues) +} + +// computePolicyValidator is used to validate Policy field. +func computePolicyValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computePolicyValues) +} + +// computeModeValidator is used to validate Mode field. +func computeModeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computeModeValues) +} + +// computeDiskTypeValidator is used to validate DiskType field. +func computeDiskTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computeDiskTypeValues) +} + +// computeNetTypeValidator is used to validate NetType field. +func computeNetTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computeNetTypeValues) +} + +// computex86NetTypeValidator is used to validate NetType field. +func computex86NetTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computex86NetTypeValues) +} + +// computeOrderValidator is used to validate Order field. +func computeOrderValidator(fe validator.FieldLevel) bool { + fieldSlice, ok := fe.Field().Interface().([]string) + if !ok { + return false + } + + for _, value := range fieldSlice { + if !IsInSlice(value, computeOrderValues) { + return false + } + } + + return true +} + +// computeDataDisksValidator is used to validate DataDisks field. +func computeDataDisksValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, computeDataDisksValues) +} + +// diskTypeValidator is used to validate Type field. +func diskTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, diskTypeValues) +} + +// flipgroupClientTypeValidator is used to validate ClientType field. +func flipgroupClientTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, flipgroupClientTypeValues) +} + +// kvmNetTypeValidator is used to validate NetType field. +func kvmNetTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, kvmNetTypeValues) +} + +// lbAlgorithmValidator is used to validate Algorithm field. +func lbAlgorithmValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, lbAlgorithmValues) +} + +// rgDefNetValidator is used to validate DefNet field. +func rgDefNetValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, rgDefNetValues) +} + +// rgNetTypeValidator is used to validate NetType field. +func rgNetTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, rgNetTypeValues) +} + +// vinsTypeValidator is used to validate Type field. +func vinsTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, vinsTypeValues) +} + +// imageBootTypeValidator is used to validate BootType field. +func imageBootTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, imageBootTypeValues) +} + +// imageTypeValidator is used to validate ImageType field. +func imageTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, imageTypeValues) +} + +// imageDriversValidator is used to validate Drivers field. +func imageDriversValidator(fe validator.FieldLevel) bool { + fieldSlice, ok := fe.Field().Interface().([]string) + if !ok { + return false + } + + for _, item := range fieldSlice { + if !IsInSlice(item, imageDriversValues) { + return false + } + } + + return true +} + +// imageArchitectureValidator is used to validate Architecture field. +func imageArchitectureValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, imageArchitectureValues) +} + +// sepFieldTypeValidator is used to validate FieldType field. +func sepFieldTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, sepFieldTypeValues) +} + +// hwPathValidator is used to validate HWPath field. +func hwPathValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + ok, _ := regexp.MatchString(`^\b[0-9a-f]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.\d{1}$`, fieldValue) + + return ok +} + +// networkPluginValidator is used to validate NetworkPlugin field +func networkPluginValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + fieldValue = strings.ToLower(fieldValue) + + return IsInSlice(fieldValue, networkPluginValues) +} + +// networkPluginsValidator is used to validate NetworkPlugins field +func networkPluginsValidator(fe validator.FieldLevel) bool { + fieldSlice, ok := fe.Field().Interface().([]string) + if !ok { + return false + } + + for _, item := range fieldSlice { + item = strings.ToLower(item) + + if !IsInSlice(item, networkPluginValues) { + return false + } + } + + return true +} + +func interfaceStateValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + fieldValue = strings.ToLower(fieldValue) + + return IsInSlice(fieldValue, interfaceStateValues) +} + +func interfaceTXModelValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + fieldValue = strings.ToLower(fieldValue) + + return IsInSlice(fieldValue, txModelValues) +} + +func interfaceIOEventFDValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + fieldValue = strings.ToLower(fieldValue) + + return IsInSlice(fieldValue, ioEventFDValues) +} + +func interfaceEventIDxValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + fieldValue = strings.ToLower(fieldValue) + + return IsInSlice(fieldValue, eventIDxValues) +} + +func strictLooseValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + fieldValue = strings.ToLower(fieldValue) + + return IsInSlice(fieldValue, strictLooseValues) +} + +// name workerGroup must be more 3 symbol +func workerGroupNameValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + fieldValue = strings.Trim(fieldValue, " ") + + return len(fieldValue) >= 3 +} + +func sortByValidator(fe validator.FieldLevel) bool { + + sortByRegexp := regexp.MustCompile(`^[+-][a-zA-Z_]+`) + + return sortByRegexp.MatchString(fe.Field().String()) +} + +func actionValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, actionValues) +} + +func vmActionValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, vmActionValues) +} + +func mtuValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().Uint() + + return fieldValue >= uint64(mtuMin) && fieldValue <= uint64(mtuMax) +} + +func computeFeaturesValidator(fe validator.FieldLevel) bool { + field := fe.Field() + slice, ok := field.Interface().([]string) + if !ok { + return false + } + + return IsSubSlice(slice, computeFeaturesValues) +} + +func networkInterfaceNamingValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, networkInterfaceNamingValues) +} + +func numaAffinityValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, numaAffinityValues) +} + +// kvmx86NetTypeValidator is used to validate NetType field for x86 compute. +func kvmx86NetTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, kvmx86NetTypeValues) +} + +func isBoolTypeValidator(fe validator.FieldLevel) bool { + return fe.Field().CanConvert(reflect.TypeOf(true)) +} + +func urlValidartor(fl validator.FieldLevel) bool { + fieldValues := fl.Field().String() + + _, err := url.ParseRequestURI(fieldValues) + return err == nil +} + +func chipsetValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + fieldValue = strings.ToLower(fieldValue) + + return IsInSlice(fieldValue, chipsetValues) +} + +// ValidateRAM checks if request contains RAM value that is positive integer divisible by divisibility passed. +// It is recommended to pass constants.RAM_DIVISIBILITY as divisility arguement +func ValidateRAM(r interfaces.RequestWithRAM, divisibility uint64) error { + + if divisibility == 0 { + + return errors.New("second argument of ValidateRAM should be greater than 0") + } + mapRAM := r.GetRAM() + + errs := make([]error, 0, len(mapRAM)) + + for k, v := range mapRAM { + + if v%divisibility != 0 { + + errs = append(errs, fmt.Errorf("expected value of %s: \"%d\" should be divisible by %d", k, v, divisibility)) + } + } + return multierror.Join(errs...) +} diff --git a/internal/validators/helper.go b/internal/validators/helper.go new file mode 100644 index 0000000..9b948e5 --- /dev/null +++ b/internal/validators/helper.go @@ -0,0 +1,54 @@ +package validators + +import ( + "errors" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/multierror" + + "github.com/go-playground/validator/v10" +) + +func ValidateRequest(req interface{}) error { + validate := getDecortValidator() + return validate.Struct(req) +} + +func ValidateConfig(cfg interface{}) error { + validate := getDecortValidator() + 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...) +} + +//nolint:errorlint +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 IsSubSlice(source []string, target []string) bool { + for _, s := range source { + if !IsInSlice(s, target) { + return false + } + } + return true +} diff --git a/internal/validators/messages.go b/internal/validators/messages.go new file mode 100644 index 0000000..72b4100 --- /dev/null +++ b/internal/validators/messages.go @@ -0,0 +1,315 @@ +package validators + +import ( + "fmt" + "strings" + + "github.com/go-playground/validator/v10" +) + +func errorMessage(fe validator.FieldError) string { + prefix := "validation-error:" + + switch fe.Tag() { + + // Common Validators + case "required": + return fmt.Sprintf("%s %s is required", prefix, fe.Field()) + case "gt": + return fmt.Sprintf("%s %s can't be less or equal to zero", prefix, fe.Field()) + case "min": + return fmt.Sprintf("%s %s: not enough elements", prefix, fe.Field()) + case "max": + return fmt.Sprintf("%s %s: too many elements", prefix, fe.Field()) + case "url": + return fmt.Sprintf("%s %s: unexpected URL format", prefix, fe.Field()) + case "email": + return fmt.Sprintf("%s %s: unexpected E-Mail format", prefix, fe.Field()) + case "isBool": + return fmt.Sprintf("%s %s: must be bool type", prefix, fe.Field()) + + case "driver": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(driverValues)) + + case "accessType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(accessTypeValues)) + + case "resTypes": + return fmt.Sprintf("%s %s can contain only the following values: %s", + prefix, + fe.Field(), + joinValues(resTypesValues)) + + case "proto": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(protoValues)) + + // Account Validators + case "accountCUType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(accountCUTypeValues)) + + // BService Validators + case "bserviceMode": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(bserviceModeValues)) + + // Compute Validators + case "computeTopology": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computeTopologyValues)) + + case "computePolicy": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computePolicyValues)) + + case "computeMode": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computeModeValues)) + + case "computeDiskType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computeDiskTypeValues)) + + case "mtu": + return fmt.Sprint(prefix, fe.Field(), "must be ", mtuMin, "-", mtuMax) + + case "computex86NetType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computex86NetTypeValues)) + + case "computeNetType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computeNetTypeValues)) + + case "computeOrder": + return fmt.Sprintf("%s %s can contain only the following values: %s", + prefix, + fe.Field(), + joinValues(computeOrderValues)) + + case "computeDataDisks": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computeDataDisksValues)) + + case "computeDriver": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computeDriverValues)) + + // Disk Validators + case "diskType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(diskTypeValues)) + + // Flipgroup Validators + case "flipgroupClientType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(flipgroupClientTypeValues)) + + // k8s Validators + case "workerGroupName": + return fmt.Sprintf("%s %s must be more 3 symbol", + prefix, + fe.Field()) + + // KVM_X86 Validators + case "kvmNetType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(kvmNetTypeValues)) + + // LB Validators + case "lbAlgorithm": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(lbAlgorithmValues)) + + // RG Validators + case "rgDefNet": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(rgDefNetValues)) + + case "rgNetType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(rgNetTypeValues)) + + // ViNS Validators + case "vinsType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(vinsTypeValues)) + + // Image Validators + case "imageBootType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(imageBootTypeValues)) + + case "imageType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(imageTypeValues)) + + case "imageDrivers": + return fmt.Sprintf("%s %s must contain only the following: %s", + prefix, + fe.Field(), + joinValues(imageDriversValues)) + + case "imageArchitecture": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(imageArchitectureValues)) + + // SEP Validators + case "sepFieldType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(sepFieldTypeValues)) + + // HWPath Validators + case "hwPath": + return fmt.Sprintf("%s %s must be in format 0000:1f:2b.0", + prefix, + fe.Field()) + + // Network plugin Validators + case "networkPlugin": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(networkPluginValues)) + + case "networkPlugins": + return fmt.Sprintf("%s %s must contain only the following: %s", + prefix, + fe.Field(), + joinValues(networkPluginValues)) + + case "strict_loose": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(strictLooseValues)) + + case "interfaceState": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(interfaceStateValues)) + + case "interfaceTXModel": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(txModelValues)) + + case "interfaceIOEventFD": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(ioEventFDValues)) + + case "interfaceEventIDx": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(eventIDxValues)) + + case "sortBy": + return fmt.Sprintf("%s %s must be in format +|-(field)", + prefix, + fe.Field()) + + case "action": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(actionValues)) + + case "vmaction": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(vmActionValues)) + + case "computeFeatures": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(computeFeaturesValues)) + + case "networkInterfaceNaming": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(networkInterfaceNamingValues)) + + case "numaAffinity": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(numaAffinityValues)) + + case "kvmx86NetType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(kvmx86NetTypeValues)) + + case "chipset": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(chipsetValues)) + } + + return fe.Error() +} + +func joinValues(values []string) string { + return strings.Join(values, ", ") +} diff --git a/internal/validators/validator.go b/internal/validators/validator.go new file mode 100644 index 0000000..829eb95 --- /dev/null +++ b/internal/validators/validator.go @@ -0,0 +1,265 @@ +package validators + +import ( + "sync" + + "github.com/go-playground/validator/v10" +) + +var ( + once sync.Once + decortValidator = validator.New() +) + +// getDecortValidator returns singleton instance of DecortValidator. +func getDecortValidator() *validator.Validate { + once.Do(func() { + err := registerAllValidators(decortValidator) + if err != nil { + panic(err) + } + }) + + return decortValidator +} + +// registerAllValidators registers all custom validators in DecortValidator. +func registerAllValidators(validate *validator.Validate) error { + + err := validate.RegisterValidation("proto", protoValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computeDriver", computeDriverValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("accessType", accessTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("resTypes", resTypesValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("driver", driverValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("imageBootType", imageBootTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("imageType", imageTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("imageDrivers", imageDriversValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("imageArchitecture", imageArchitectureValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("accountCUType", accountCUTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("bserviceMode", bserviceModeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computeTopology", computeTopologyValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computePolicy", computePolicyValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computeMode", computeModeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computeDiskType", computeDiskTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computeNetType", computeNetTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computex86NetType", computex86NetTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computeOrder", computeOrderValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computeDataDisks", computeDataDisksValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("diskType", diskTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("flipgroupClientType", flipgroupClientTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("kvmNetType", kvmNetTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("lbAlgorithm", lbAlgorithmValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("rgDefNet", rgDefNetValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("rgNetType", rgNetTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("vinsType", vinsTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("sepFieldType", sepFieldTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("hwPath", hwPathValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("networkPlugin", networkPluginValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("networkPlugins", networkPluginsValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("strict_loose", strictLooseValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("interfaceState", interfaceStateValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("interfaceTXModel", interfaceTXModelValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("interfaceIOEventFD", interfaceIOEventFDValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("interfaceEventIDx", interfaceEventIDxValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("workerGroupName", workerGroupNameValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("sortBy", sortByValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("action", actionValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("vmaction", vmActionValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("mtu", mtuValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("computeFeatures", computeFeaturesValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("networkInterfaceNaming", networkInterfaceNamingValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("numaAffinity", numaAffinityValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("kvmx86NetType", kvmx86NetTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("isBool", isBoolTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("url", urlValidartor) + if err != nil { + return err + } + + err = validate.RegisterValidation("chipset", chipsetValidator) + 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..219fd52 --- /dev/null +++ b/internal/validators/values.go @@ -0,0 +1,73 @@ +package validators + +var ( + driverValues = []string{"KVM_X86"} + accessTypeValues = []string{"R", "RCX", "ARCXDU"} + resTypesValues = []string{"compute", "vins", "k8s", "openshift", "lb", "flipgroup"} + protoValues = []string{"tcp", "udp"} + + accountCUTypeValues = []string{"CU_M", "CU_C", "CU_D", "CU_DM", "CU_S", "CU_A", "CU_NO", "CU_I", "CU_NP"} + + bserviceModeValues = []string{"ABSOLUTE", "RELATIVE"} + + computeTopologyValues = []string{"compute", "node"} + computePolicyValues = []string{"RECOMMENDED", "REQUIRED"} + computeModeValues = []string{"EQ", "EN", "ANY"} + computeDiskTypeValues = []string{"D", "B"} + computeNetTypeValues = []string{"EXTNET", "VINS"} + computex86NetTypeValues = []string{"EXTNET", "VINS", "VFNIC", "DPDK"} + computeOrderValues = []string{"cdrom", "network", "hd"} + computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"} + computeDriverValues = []string{"KVM_X86", "SVA_KVM_X86"} + + diskTypeValues = []string{"B", "T", "D"} + + flipgroupClientTypeValues = []string{"compute", "vins"} + + kvmNetTypeValues = []string{"EXTNET", "VINS", "NONE"} + kvmx86NetTypeValues = []string{"EXTNET", "VINS", "NONE", "VFNIC", "DPDK"} + + lbAlgorithmValues = []string{"roundrobin", "static-rr", "leastconn"} + + rgDefNetValues = []string{"PRIVATE", "PUBLIC", "NONE"} + rgNetTypeValues = []string{"PUBLIC", "PRIVATE"} + + vinsTypeValues = []string{"DHCP", "VIP", "EXCLUDED"} + + imageBootTypeValues = []string{"uefi", "bios"} + imageTypeValues = []string{"windows", "linux", "other"} + imageDriversValues = []string{"KVM_X86"} + imageArchitectureValues = []string{"X86_64"} + + sepFieldTypeValues = []string{"int", "str", "bool", "list", "dict"} + + networkPluginValues = []string{"flannel", "weavenet", "calico"} + + strictLooseValues = []string{"strict", "loose"} + + interfaceStateValues = []string{"on", "off"} + + actionValues = []string{"power_on", "shutdown", "force_shutdown", "reboot"} + + vmActionValues = []string{"stop", "move"} + + computeFeaturesValues = []string{"hugepages", "numa", "cpupin", "vfnic"} + + networkInterfaceNamingValues = []string{"eth", "ens"} + + numaAffinityValues = []string{"none", "strict", "loose"} + + txModelValues = []string{"iothread", "timer", "selected by hypervisor"} + + ioEventFDValues = []string{"on", "off", "selected by hypervisor"} + + eventIDxValues = []string{"on", "off", "selected by hypervisor"} + + chipsetValues = []string{"i440fx", "Q35"} +) + +const ( + mtuMin = 1 + + mtuMax = 9216 +) diff --git a/legacy-client.go b/legacy-client.go new file mode 100644 index 0000000..3ba3dd9 --- /dev/null +++ b/legacy-client.go @@ -0,0 +1,248 @@ +package decortsdk + +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-golang-sdk/v9/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker" +) + +// LegacyDecortClient is Legacy HTTP-client for platform +type LegacyDecortClient struct { + decortURL string + client *http.Client + cfg config.LegacyConfig + expiryTime time.Time + mutex *sync.Mutex +} + +// Legacy client builder +func NewLegacy(cfg config.LegacyConfig) *LegacyDecortClient { + if cfg.Retries == 0 { + cfg.Retries = 5 + } + + var expiryTime time.Time + + if cfg.Token != "" { + expiryTime = time.Now().AddDate(0, 0, 1) + } + + return &LegacyDecortClient{ + decortURL: cfg.DecortURL, + client: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + //nolint:gosec + InsecureSkipVerify: cfg.SSLSkipVerify, + }, + }, + }, + cfg: trimLegacyConfig(&cfg), + expiryTime: expiryTime, + mutex: &sync.Mutex{}, + } +} + +// CloudAPI builder +func (ldc *LegacyDecortClient) CloudAPI() *cloudapi.CloudAPI { + return cloudapi.New(ldc) +} + +// CloudBroker builder +func (ldc *LegacyDecortClient) CloudBroker() *cloudbroker.CloudBroker { + return cloudbroker.New(ldc) +} + +// DecortApiCall method for sending requests to the platform +func (ldc *LegacyDecortClient) DecortApiCall(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 + var ctype string + + byteSlice, ok := params.([]byte) + if ok { + body = bytes.NewBuffer(byteSlice) + ctype = "application/octet-stream" + } else { + values, err := query.Values(params) + if err != nil { + return nil, err + } + body = bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token)) + } + + req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+constants.RESTMACHINE+url, body) + if err != nil { + return nil, err + } + + // perform request + respBytes, err := ldc.do(req, ctype) + if err != nil { + return nil, err + } + + return respBytes, err +} + +func (ldc *LegacyDecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) { + body, ctype, err := multiPartReq(params) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+constants.RESTMACHINE+url, body) + if err != nil { + return nil, err + } + + // get token + if err = ldc.getToken(ctx); err != nil { + return nil, err + } + + // perform request + respBytes, err := ldc.do(req, ctype) + if err != nil { + return nil, err + } + + return respBytes, err +} + +func (ldc *LegacyDecortClient) 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("username=%s&password=%s", url.QueryEscape(ldc.cfg.Username), url.QueryEscape(ldc.cfg.Password)) + bodyReader := strings.NewReader(body) + + req, _ := http.NewRequestWithContext(ctx, "POST", ldc.cfg.DecortURL+constants.RESTMACHINE+"/cloudapi/user/authenticate", bodyReader) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + // request token + resp, err := ldc.client.Do(req) + if err != nil || resp == nil { + return fmt.Errorf("cannot get token: %w", err) + } + defer resp.Body.Close() + + var tokenBytes []byte + tokenBytes, 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", tokenBytes) + } + + // save token in config + token := string(tokenBytes) + 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 *LegacyDecortClient) do(req *http.Request, ctype string) ([]byte, error) { + // set up request headers and body + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + if ctype != "" { + req.Header.Set("Content-Type", ctype) + } + + 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() + } + + // retries logic GOES HERE + // get http response + //var resp *http.Response + //for i := uint64(0); i < ldc.cfg.Retries; i++ { + // req := req.Clone(req.Context()) + // req.Body = io.NopCloser(bytes.NewBuffer(buf)) + // + // if i > 0 { + // time.Sleep(5 * time.Second) // no time sleep for the first request + // } + // + // resp, err = ldc.client.Do(req) + // + // // stop retries on success and close response body + // if resp != nil { + // defer resp.Body.Close() + // } + // if err == nil { + // break + // } + // + // // retries in case of connection errors with time sleep + // if isConnectionError(err) { + // continue + // } + // + // // return error in case of non-connection error + // return nil, err + //} + + // 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 trimLegacyConfig(cfg *config.LegacyConfig) config.LegacyConfig { + cfg.DecortURL = strings.TrimSuffix(cfg.DecortURL, "/") + return *cfg +} diff --git a/pkg/cloudapi/account.go b/pkg/cloudapi/account.go new file mode 100644 index 0000000..5b5a459 --- /dev/null +++ b/pkg/cloudapi/account.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/account" +) + +// Accessing the Account method group +func (ca *CloudAPI) Account() *account.Account { + return account.New(ca.client) +} diff --git a/pkg/cloudapi/account/account.go b/pkg/cloudapi/account/account.go new file mode 100644 index 0000000..0b54279 --- /dev/null +++ b/pkg/cloudapi/account/account.go @@ -0,0 +1,18 @@ +// API Actor API for managing account +package account + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to account +type Account struct { + client interfaces.Caller +} + +// Builder for account endpoints +func New(client interfaces.Caller) *Account { + return &Account{ + client, + } +} diff --git a/pkg/cloudapi/account/add_user.go b/pkg/cloudapi/account/add_user.go new file mode 100644 index 0000000..8c27da0 --- /dev/null +++ b/pkg/cloudapi/account/add_user.go @@ -0,0 +1,49 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AddUserRequest struct to add permission to access account for a user +type AddUserRequest struct { + // ID of account to add to + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Name of the user to be given rights + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` + + // Account permission types: + // - 'R' for read only access + // - 'RCX' for Write + // - 'ARCXDU' for Admin + // Required: true + AccessType string `url:"accesstype" json:"accesstype" validate:"required,accessType"` +} + +// AddUser gives a user access rights. +func (a Account) AddUser(ctx context.Context, req AddUserRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/addUser" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/account/audits.go b/pkg/cloudapi/account/audits.go new file mode 100644 index 0000000..dd53d20 --- /dev/null +++ b/pkg/cloudapi/account/audits.go @@ -0,0 +1,40 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuditsRequest struct to give list of account audits +type AuditsRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Audits gets audit records for the specified account object +func (a Account) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/audits" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/account/create.go b/pkg/cloudapi/account/create.go new file mode 100644 index 0000000..9d74d1c --- /dev/null +++ b/pkg/cloudapi/account/create.go @@ -0,0 +1,75 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct for creating account +type CreateRequest struct { + // Display name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Name of the account + // Required: true + Username string `url:"username" json:"username" validate:"required"` + + // Email + // Required: false + EmailAddress string `url:"emailaddress,omitempty" json:"emailaddress,omitempty" validate:"omitempty,email"` + + // Max size of memory in MB + // Required: false + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + + // Max size of aggregated vdisks in GB + // Required: false + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + + // Max number of CPU cores + // Required: false + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + + // Max sent/received network transfer peering + // Required: false + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + + // Max number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + + // If true send emails when a user is granted access to resources + // Required: false + SendAccessEmails bool `url:"sendAccessEmails" json:"sendAccessEmails"` + + // Limit (positive) or disable (0) GPU resources + // Required: false + GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` +} + +// Create creates account +// Setting a cloud unit maximum to -1 or empty will not put any restrictions on the resource +func (a Account) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/create" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/account/delete.go b/pkg/cloudapi/account/delete.go new file mode 100644 index 0000000..7af2fe1 --- /dev/null +++ b/pkg/cloudapi/account/delete.go @@ -0,0 +1,36 @@ +package account + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete account +type DeleteRequest struct { + // ID of account to delete + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Whether to completely delete the account + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete completes delete an account from the system Returns true if account is deleted or was already deleted or never existed +func (a Account) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/delete" + + _, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudapi/account/delete_user.go b/pkg/cloudapi/account/delete_user.go new file mode 100644 index 0000000..7121720 --- /dev/null +++ b/pkg/cloudapi/account/delete_user.go @@ -0,0 +1,42 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteUserRequest struct to revoke access to account +type DeleteUserRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // ID or emailaddress of the user to remove + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` +} + +// DeleteUser revokes user access from the account +func (a Account) DeleteUser(ctx context.Context, req DeleteUserRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/deleteUser" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/account/disable_enable.go b/pkg/cloudapi/account/disable_enable.go new file mode 100644 index 0000000..24c9fd9 --- /dev/null +++ b/pkg/cloudapi/account/disable_enable.go @@ -0,0 +1,60 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableEnableRequest struct to change status of account +type DisableEnableRequest struct { + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Disable disables an account +func (a Account) Disable(ctx context.Context, req DisableEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/disable" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// Enable enables an account +func (a Account) Enable(ctx context.Context, req DisableEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/enable" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/account/filter.go b/pkg/cloudapi/account/filter.go new file mode 100644 index 0000000..81bb1ef --- /dev/null +++ b/pkg/cloudapi/account/filter.go @@ -0,0 +1,70 @@ +package account + +// FilterByID returns ListAccounts with specified ID. +func (la ListAccounts) FilterByID(id uint64) ListAccounts { + predicate := func(ia ItemAccount) bool { + return ia.ID == id + } + + return la.FilterFunc(predicate) +} + +// FilterByName returns ListAccounts with specified Name. +func (la ListAccounts) FilterByName(name string) ListAccounts { + predicate := func(ia ItemAccount) bool { + return ia.Name == name + } + + return la.FilterFunc(predicate) +} + +// FilterByStatus returns ListAccounts with specified Status. +func (la ListAccounts) FilterByStatus(status string) ListAccounts { + predicate := func(ia ItemAccount) bool { + return ia.Status == status + } + + return la.FilterFunc(predicate) +} + +// FilterByUserGroupID returns ListAccounts with specified UserGroupID. +func (la ListAccounts) FilterByUserGroupID(userGroupID string) ListAccounts { + predicate := func(ia ItemAccount) bool { + acl := ia.ACL + + for _, item := range acl { + if item.UgroupID == userGroupID { + return true + } + } + + return false + } + + return la.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListAccounts based on a user-specified predicate. +func (la ListAccounts) FilterFunc(predicate func(ItemAccount) bool) ListAccounts { + var result ListAccounts + + for _, acc := range la.Data { + if predicate(acc) { + result.Data = append(result.Data, acc) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemAccount. +// If none was found, returns an empty struct. +func (la ListAccounts) FindOne() ItemAccount { + if len(la.Data) == 0 { + return ItemAccount{} + } + + return la.Data[0] +} diff --git a/pkg/cloudapi/account/filter_test.go b/pkg/cloudapi/account/filter_test.go new file mode 100644 index 0000000..b1e7f9f --- /dev/null +++ b/pkg/cloudapi/account/filter_test.go @@ -0,0 +1,149 @@ +package account + +import ( + "testing" +) + +var accounts = ListAccounts{ + Data: []ItemAccount{ + { + ACL: []RecordACL{ + { + IsExplicit: true, + GUID: "", + Rights: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UgroupID: "timofey_tkachev_1@decs3o", + }, + }, + CreatedTime: 1676645275, + DeletedTime: 0, + ID: 132846, + Name: "std", + Status: "CONFIRMED", + UpdatedTime: 1676645275, + }, + { + ACL: []RecordACL{ + { + IsExplicit: true, + GUID: "", + Rights: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UgroupID: "not_really_timofey_tkachev_1@decs3o", + }, + }, + CreatedTime: 1676878820, + DeletedTime: 0, + ID: 132847, + Name: "std_2", + Status: "CONFIRMED", + UpdatedTime: 1676645275, + }, + { + ACL: []RecordACL{ + { + IsExplicit: true, + GUID: "", + Rights: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UgroupID: "timofey_tkachev_1@decs3o", + }, + { + IsExplicit: true, + GUID: "", + Rights: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UgroupID: "second_account@decs3o", + }, + }, + CreatedTime: 1676883850, + DeletedTime: 1676883899, + ID: 132848, + Name: "std_broker", + Status: "DELETED", + UpdatedTime: 1676878820, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := accounts.FilterByID(132846).FindOne() + + if actual.ID != 132846 { + t.Fatal("actual: ", actual.ID, " > expected: 132846") + } +} + +func TestFilterByUserGroupId(t *testing.T) { + actual := accounts.FilterByUserGroupID("second_account@decs3o").FindOne() + + for _, item := range actual.ACL { + if item.UgroupID == "second_account@decs3o" { + return + } + } + + t.Fatal("second_account@decs3o has not been found. expected 1 found") +} + +func TestFilterByName(t *testing.T) { + actual := accounts.FilterByName("std_broker").FindOne() + + if actual.Name != "std_broker" { + t.Fatal("actual: ", actual.Name, " >> expected: std_broker") + } +} + +func TestFilterByStatus(t *testing.T) { + actual := accounts.FilterByStatus("CONFIRMED") + + if len(actual.Data) != 2 { + t.Fatal("Expected 2 elements in slice, found: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "CONFIRMED" { + t.Fatal("expected CONFIRMED, found: ", item.Status) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := accounts.FilterFunc(func(ia ItemAccount) bool { + return ia.DeletedTime == 0 + }) + + for _, item := range actual.Data { + if item.DeletedTime != 0 { + t.Fatal("Expected DeletedTime = 0, found: ", item.DeletedTime) + } + } +} + +func TestSortingByCreatedTime(t *testing.T) { + actual := accounts.SortByCreatedTime(false) + + if actual.Data[0].Name != "std" { + t.Fatal("Expected account std as earliest, found: ", actual.Data[0].Name) + } + + actual = accounts.SortByCreatedTime(true) + + if actual.Data[0].Name != "std_broker" { + t.Fatal("Expected account std_broker as latest, found: ", actual.Data[0].Name) + } +} + +func TestFilterEmpty(t *testing.T) { + actual := accounts.FilterByID(0) + + if len(actual.Data) != 0 { + t.Fatal("Expected 0 found, actual: ", len(actual.Data)) + } +} diff --git a/pkg/cloudapi/account/get.go b/pkg/cloudapi/account/get.go new file mode 100644 index 0000000..682fdb4 --- /dev/null +++ b/pkg/cloudapi/account/get.go @@ -0,0 +1,47 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about account +type GetRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Get gets account details as a RecordAccount struct +func (a Account) Get(ctx context.Context, req GetRequest) (*RecordAccount, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordAccount{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets account details as an array of bytes +func (a Account) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/account/get_consumed_account_units.go b/pkg/cloudapi/account/get_consumed_account_units.go new file mode 100644 index 0000000..08093a1 --- /dev/null +++ b/pkg/cloudapi/account/get_consumed_account_units.go @@ -0,0 +1,46 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetConsumedAccountUnitsRequest struct to calculate the currently consumed units for all cloudspaces and resource groups in the account +type GetConsumedAccountUnitsRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// GetConsumedAccountUnits calculates the currently consumed units for all cloudspaces and resource groups in the account. +// Calculated cloud units are returned in a dict which includes: +// - CU_M: consumed memory in MB +// - CU_C: number of cpu cores +// - CU_D: consumed vdisk storage in GB +// - CU_DM: consumed max vdisk storage in GB +// - CU_I: number of public IPs +func (a Account) GetConsumedAccountUnits(ctx context.Context, req GetConsumedAccountUnitsRequest) (*ResourceLimits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/getConsumedAccountUnits" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := ResourceLimits{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/account/get_consumed_cloud_units_by_type.go b/pkg/cloudapi/account/get_consumed_cloud_units_by_type.go new file mode 100644 index 0000000..18e63b1 --- /dev/null +++ b/pkg/cloudapi/account/get_consumed_cloud_units_by_type.go @@ -0,0 +1,54 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetConsumedCloudUnitsByTypeRequest struct to calculate the currently consumed cloud units of the specified type for all cloudspaces and resource groups in the account +type GetConsumedCloudUnitsByTypeRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Cloud unit resource type + // Required: true + CUType string `url:"cutype" json:"cutype" validate:"required,accountCUType"` +} + +// GetConsumedCloudUnitsByType calculates the currently consumed cloud units of the specified type for all cloudspaces +// and resource groups in the account. +// Possible types of cloud units are include: +// +// - CU_M: returns consumed memory in MB +// - CU_C: returns number of virtual cpu cores +// - CU_D: returns consumed virtual disk storage in GB +// - CU_DM: returns consumed max virtual disk storage in GB +// - CU_S: returns consumed primary storage (NAS) in TB +// - CU_A: returns consumed secondary storage (Archive) in TB +// - CU_NO: returns sent/received network transfer in operator in GB +// - CU_NP: returns sent/received network transfer peering in GB +// - CU_I: returns number of public IPs +func (a Account) GetConsumedCloudUnitsByType(ctx context.Context, req GetConsumedCloudUnitsByTypeRequest) (float64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/getConsumedCloudUnitsByType" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseFloat(string(res), 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/account/get_reserved_account_units.go b/pkg/cloudapi/account/get_reserved_account_units.go new file mode 100644 index 0000000..4630f2a --- /dev/null +++ b/pkg/cloudapi/account/get_reserved_account_units.go @@ -0,0 +1,47 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetReservedAccountUnitsRequest struct to calculate the reserved units for all cloudspaces and resource groups in the account +type GetReservedAccountUnitsRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// GetReservedAccountUnits calculates the reserved units for all cloudspaces and resource groups in the account. +// Calculated cloud units are returned in a dict which includes: +// +// - CU_M: reserved memory in MB +// - CU_C: number of cpu cores +// - CU_D: reserved vdisk storage in GB +// - CU_DM: reserved max vdisk storage in GB +// - CU_I: number of public IPs +func (a Account) GetReservedAccountUnits(ctx context.Context, req GetReservedAccountUnitsRequest) (*ResourceLimits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/getReservedAccountUnits" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := ResourceLimits{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/account/get_resource_consumption.go b/pkg/cloudapi/account/get_resource_consumption.go new file mode 100644 index 0000000..8f47266 --- /dev/null +++ b/pkg/cloudapi/account/get_resource_consumption.go @@ -0,0 +1,40 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetResourceConsumptionRequest struct to get resource consumption +type GetResourceConsumptionRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// GetResourceConsumption show amount of consumed and reserved resources (cpu, ram, disk) by specific account +func (a Account) GetResourceConsumption(ctx context.Context, req GetResourceConsumptionRequest) (*RecordResourceConsumption, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/getResourceConsumption" + + info := RecordResourceConsumption{} + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/account/ids.go b/pkg/cloudapi/account/ids.go new file mode 100644 index 0000000..a190de5 --- /dev/null +++ b/pkg/cloudapi/account/ids.go @@ -0,0 +1,73 @@ +package account + +// IDs gets array of AccountIDs from ListAccounts struct +func (la ListAccounts) IDs() []uint64 { + res := make([]uint64, 0, len(la.Data)) + for _, acc := range la.Data { + res = append(res, acc.ID) + } + return res +} + +// IDs gets array of ComputeIDs from ListComputes struct +func (lc ListComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lc.Data)) + for _, c := range lc.Data { + res = append(res, c.ComputeID) + } + return res +} + +// IDs gets array of DiskIDs from ListDisks struct +func (ld ListDisks) IDs() []uint64 { + res := make([]uint64, 0, len(ld.Data)) + for _, d := range ld.Data { + res = append(res, d.ID) + } + return res +} + +// IDs gets array of FLIPGroupIDs from ListFLIPGroups struct +func (fg ListFLIPGroups) IDs() []uint64 { + res := make([]uint64, 0, len(fg.Data)) + for _, g := range fg.Data { + res = append(res, g.ID) + } + return res +} + +// IDs gets array of AccountIDs from ListResourceConsumption struct +func (rc ListResourceConsumption) IDs() []uint64 { + res := make([]uint64, 0, len(rc.Data)) + for _, r := range rc.Data { + res = append(res, r.AccountID) + } + return res +} + +// IDs gets array of RGIDs from ListRG struct +func (rg ListRG) IDs() []uint64 { + res := make([]uint64, 0, len(rg.Data)) + for _, g := range rg.Data { + res = append(res, g.RGID) + } + return res +} + +// IDs gets array of TemplateIDs from ListTemplates struct +func (lt ListTemplates) IDs() []uint64 { + res := make([]uint64, 0, len(lt.Data)) + for _, t := range lt.Data { + res = append(res, t.ID) + } + return res +} + +// IDs gets array of VINSIDs from ListVINS struct +func (lv ListVINS) IDs() []uint64 { + res := make([]uint64, 0, len(lv.Data)) + for _, v := range lv.Data { + res = append(res, v.ID) + } + return res +} diff --git a/pkg/cloudapi/account/list.go b/pkg/cloudapi/account/list.go new file mode 100644 index 0000000..f894dff --- /dev/null +++ b/pkg/cloudapi/account/list.go @@ -0,0 +1,71 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of accounts +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by access control list + // Required: false + ACL string `url:"acl,omitempty" json:"acl,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets a list of all accounts the user has access to a ListAccounts struct +func (a Account) List(ctx context.Context, req ListRequest) (*ListAccounts, error) { + + res, err := a.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListAccounts{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets a list of all accounts the user has access to as an array of bytes +func (a Account) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/account/list_computes.go b/pkg/cloudapi/account/list_computes.go new file mode 100644 index 0000000..f5c41f6 --- /dev/null +++ b/pkg/cloudapi/account/list_computes.go @@ -0,0 +1,85 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListComputesRequest struct to get a list of compute instances +type ListComputesRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by compute id + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by resource group name + // Required: false + RGName string `url:"rgName,omitempty" json:"rgName,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by ip address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by external network name + // Required: false + ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"` + + // Find by external network id + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListComputes gets list all compute instances under specified account, accessible by the user +func (a Account) ListComputes(ctx context.Context, req ListComputesRequest) (*ListComputes, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/listComputes" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/account/list_deleted.go b/pkg/cloudapi/account/list_deleted.go new file mode 100644 index 0000000..b461d20 --- /dev/null +++ b/pkg/cloudapi/account/list_deleted.go @@ -0,0 +1,61 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get a list of deleted accounts +type ListDeletedRequest struct { + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by access control list + // Required: false + ACL string `url:"acl,omitempty" json:"acl,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` +} + +// ListDeleted gets list of all deleted accounts the user has access to +func (a Account) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListAccounts, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/listDeleted" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAccounts{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/account/list_disks.go b/pkg/cloudapi/account/list_disks.go new file mode 100644 index 0000000..4c9d268 --- /dev/null +++ b/pkg/cloudapi/account/list_disks.go @@ -0,0 +1,69 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDisksRequest struct to get a list of deleted disks +type ListDisksRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by disk id + // Required: false + DiskID uint64 `url:"diskId,omitempty" json:"diskId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by max size disk + // Required: false + DiskMaxSize uint64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"` + + // Type of the disks + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDisks gets list all currently unattached disks under specified account +func (a Account) ListDisks(ctx context.Context, req ListDisksRequest) (*ListDisks, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/listDisks" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/account/list_flipgroups.go b/pkg/cloudapi/account/list_flipgroups.go new file mode 100644 index 0000000..6c0afb3 --- /dev/null +++ b/pkg/cloudapi/account/list_flipgroups.go @@ -0,0 +1,77 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListFLIPGroupsRequest struct to get a list of FLIPGroups +type ListFLIPGroupsRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by vinsId + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Find by VINS name + // Required: false + VINSName string `url:"vinsName,omitempty" json:"vinsName,omitempty"` + + // Find by external network id + // Required: false + ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"` + + // Find by IP + // Required: false + ByIP string `url:"byIp,omitempty" json:"byIp,omitempty"` + + // Find by flipGroup Id + // Required: false + FLIPGroupID uint64 `url:"flipGroupId,omitempty" json:"flipGroupId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListFLIPGroups gets list all FLIPGroups under specified account, accessible by the user +func (a Account) ListFLIPGroups(ctx context.Context, req ListFLIPGroupsRequest) (*ListFLIPGroups, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/listFlipGroups" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListFLIPGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/account/list_resource_consumption.go b/pkg/cloudapi/account/list_resource_consumption.go new file mode 100644 index 0000000..be7df42 --- /dev/null +++ b/pkg/cloudapi/account/list_resource_consumption.go @@ -0,0 +1,26 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" +) + +// ListResourceConsumption show data list amount of consumed and reserved resources (cpu, ram, disk) by specific accounts +func (a Account) ListResourceConsumption(ctx context.Context) (*ListResourceConsumption, error) { + url := "/cloudapi/account/listResourceConsumption" + + info := ListResourceConsumption{} + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/account/list_rg.go b/pkg/cloudapi/account/list_rg.go new file mode 100644 index 0000000..70545c3 --- /dev/null +++ b/pkg/cloudapi/account/list_rg.go @@ -0,0 +1,73 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRGRequest struct to get a list of resource groups +type ListRGRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by vinsId + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Find by VM ID + // Required: false + VMID uint64 `url:"vmId,omitempty" json:"vmId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` +} + +// ListRG gets list of all resource groups under specified account, accessible by the user +func (a Account) ListRG(ctx context.Context, req ListRGRequest) (*ListRG, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/listRG" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListRG{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/account/list_templates.go b/pkg/cloudapi/account/list_templates.go new file mode 100644 index 0000000..cf84487 --- /dev/null +++ b/pkg/cloudapi/account/list_templates.go @@ -0,0 +1,69 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListTemplatesRequest struct to get a list of templates +type ListTemplatesRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Include deleted images + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Find by image id + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListTemplates gets list of templates which can be managed by this account +func (a Account) ListTemplates(ctx context.Context, req ListTemplatesRequest) (*ListTemplates, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/listTemplates" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListTemplates{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/account/list_vins.go b/pkg/cloudapi/account/list_vins.go new file mode 100644 index 0000000..3f81842 --- /dev/null +++ b/pkg/cloudapi/account/list_vins.go @@ -0,0 +1,69 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListVINSRequest struct to get a list of VINS +type ListVINSRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by VINS ID + // Required: false + VINSID uint64 `url:"vins,omitempty" json:"vinsId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by external network ip + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListVINS gets list of all ViNSes under specified account, accessible by the user +func (a Account) ListVINS(ctx context.Context, req ListVINSRequest) (*ListVINS, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/listVins" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/account/models.go b/pkg/cloudapi/account/models.go new file mode 100644 index 0000000..f68b7e9 --- /dev/null +++ b/pkg/cloudapi/account/models.go @@ -0,0 +1,661 @@ +package account + +// Access Control List +type RecordACL struct { + // Whether access is explicitly specified + IsExplicit bool `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Access rights + Rights string `json:"right"` + + // Status + Status string `json:"status"` + + // Account Type + Type string `json:"type"` + + // Account owner ID + UgroupID string `json:"userGroupId"` + + // Is it possible to remove + CanBeDeleted bool `json:"canBeDeleted"` +} + +// Resource limits +type ResourceLimits struct { + // Number of cores + CUC float64 `json:"CU_C"` + + // Disk size, GB + CUD float64 `json:"CU_D"` + + // Max disk size, GB + CUDM float64 `json:"CU_DM"` + + // Number of public IP addresses + CUI float64 `json:"CU_I"` + + // RAM size, MB + CUM float64 `json:"CU_M"` + + // Traffic volume, GB + CUNP float64 `json:"CU_NP"` + + // Number of graphics cores + GPUUnits float64 `json:"gpu_units"` +} + +// Main information in one of if the list of accounts +type ItemAccount struct { + // Access Control List + ACL []RecordACL `json:"acl"` + + // Compute Features + ComputeFeatures []string `json:"computeFeatures"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// List of accounts +type ListAccounts struct { + Data []ItemAccount `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// Resources used +type Resource struct { + // Number of cores + CPU int64 `json:"cpu"` + + // Disk size + DiskSize float64 `json:"disksize"` + + // Max disk size + DiskSizeMax float64 `json:"disksizemax"` + + // Number of External IPs + ExtIPs int64 `json:"extips"` + + // External traffic + ExtTraffic int64 `json:"exttraffic"` + + // Number of grafic cores + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +// Disk usage +type DiskUsage struct { + // Disk size + DiskSize float64 `json:"disksize"` + + // Disk size max + DiskSizeMax float64 `json:"disksizemax"` +} + +// Information about resource consumption +type RecordResourceConsumption struct { + ItemResourceConsumption + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` +} + +// Information about resources +type ItemResourceConsumption struct { + // Current information about resources + Consumed Resource `json:"consumed"` + + // Reserved information about resources + Reserved Resource `json:"reserved"` + + // Account ID + AccountID uint64 `json:"id"` +} + +type ListResourceConsumption struct { + Data []ItemResourceConsumption `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// Information about computes +type Computes struct { + // Number of started computes + Started uint64 `json:"started"` + + // Number of stopped computes + Stopped uint64 `json:"stopped"` +} + +// Information about machines +type Machines struct { + // Number of running machines + Running uint64 `json:"running"` + + // Number of halted machines + Halted uint64 `json:"halted"` +} + +// Main information about account +type RecordAccount struct { + // DCLocation + DCLocation string `json:"DCLocation"` + + // CKey + CKey string `json:"_ckey"` + + // Access control list + ACL []RecordACL `json:"acl"` + + // Company + Company string `json:"company"` + + // Company URL + CompanyURL string `json:"companyurl"` + + // Compute Features + ComputeFeatures []string `json:"computeFeatures"` + + // Computes + Computes Computes `json:"computes"` + + // CPU allocation parameter + CPUAllocationParameter string `json:"cpu_allocation_parameter"` + + // CPU allocation ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deactivation time + DeactivationTime float64 `json:"deactivationTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Display name + DisplayName string `json:"displayname"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Machines + Machines Machines `json:"machines"` + + // Name + Name string `json:"name"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // Resource types + ResTypes []string `json:"resourceTypes"` + + // Send access emails + SendAccessEmails bool `json:"sendAccessEmails"` + + // Status + Status string `json:"status"` + + // UniqPools + UniqPools []interface{} `json:"uniqPools"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Version + Version uint64 `json:"version"` + + // VINS + VINS []uint64 `json:"vins"` + + // VINSes + VINSes uint64 `json:"vinses"` +} + +// Main information about compute +type ItemCompute struct { + // ID an account + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Number of CPU + CPUs uint64 `json:"cpus"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // ID compute + ComputeID uint64 `json:"id"` + + // Compute name + ComputeName string `json:"name"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Registered or not + Registered bool `json:"registered"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group Name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Total disks size + TotalDisksSize uint64 `json:"totalDisksSize"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User controlled or not + UserManaged bool `json:"userManaged"` + + // Number of connected VINS + VINSConnected uint64 `json:"vinsConnected"` +} + +// List of computes +type ListComputes struct { + // Data + Data []ItemCompute `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about disk +type ItemDisk struct { + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Pool + Pool string `json:"pool"` + + // ID SEP + SEPID uint64 `json:"sepId"` + + // Shareable + Shareable bool `json:"shareable"` + + // Max size + SizeMax uint64 `json:"sizeMax"` + + // Disk type + Type string `json:"type"` +} + +// List of disks +type ListDisks struct { + // Data + Data []ItemDisk `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about VINS +type ItemVINS struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Name of account + AccountName string `json:"accountName"` + + // Number of computes + Computes uint64 `json:"computes"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // External IP + ExternalIP string `json:"externalIP"` + + // Extnet ID + ExtnetId uint64 `json:"extnetId"` + + // Free IPs + FreeIPs int64 `json:"freeIPs"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // NNFDev ID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// List of VINS +type ListVINS struct { + // Data + Data []ItemVINS `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main info about audit +type ItemAudit struct { + // Call + Call string `json:"call"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List of audits +type ListAudits []ItemAudit + +// Information compute in resource group +type RGComputes struct { + // Number of started computes + Started uint64 `json:"Started"` + + // Number of stopped computes + Stopped uint64 `json:"Stopped"` +} + +// Resources of Resource group +type RGResources struct { + // Consumed + Consumed Resource `json:"Consumed"` + + // Limits + Limits LimitsRG `json:"Limits"` + + // Reserved + Reserved Resource `json:"Reserved"` +} + +// Resources used +type LimitsRG struct { + // Number of cores + CPU int64 `json:"cpu"` + + // Disk size + DiskSize int64 `json:"disksize"` + + // Max disk size + DiskSizeMax int64 `json:"disksizemax"` + + // Number of External IPs + ExtIPs int64 `json:"extips"` + + // External traffic + ExtTraffic int64 `json:"exttraffic"` + + // Number of grafic cores + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs + SEPs uint64 `json:"seps"` +} + +// Main information about resource group +type ItemRG struct { + // Computes + Computes RGComputes `json:"Computes"` + + // Resources + Resources RGResources `json:"Resources"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Resource group ID + RGID uint64 `json:"id"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Resource group name + RGName string `json:"name"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Number of VINS + VINSes uint64 `json:"vinses"` +} + +// List of Resource groups +type ListRG struct { + // Data + Data []ItemRG `json:"data"` + + // Enrtry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about template +type ItemTemplate struct { + // UNCPath + UNCPath string `json:"UNCPath"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Description + Description string `json:"desc"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Public or not + Public bool `json:"public"` + + // Size + Size uint64 `json:"size"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // Username + Username string `json:"username"` +} + +// List of templates +type ListTemplates struct { + // Data + Data []ItemTemplate `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about FLIPGroup +type ItemFLIPGroup struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Client type + ClientType string `json:"clientType"` + + // Connection type + ConnType string `json:"connType"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IP + IP string `json:"ip"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network type + NetType string `json:"netType"` + + // Network mask + NetMask uint64 `json:"netmask"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// List of FLIPGroups +type ListFLIPGroups struct { + // Data + Data []ItemFLIPGroup `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/account/restore.go b/pkg/cloudapi/account/restore.go new file mode 100644 index 0000000..696e53f --- /dev/null +++ b/pkg/cloudapi/account/restore.go @@ -0,0 +1,32 @@ +package account + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore a deleted account +type RestoreRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Restore restores a deleted account +func (a Account) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/restore" + + _, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudapi/account/serialize.go b/pkg/cloudapi/account/serialize.go new file mode 100644 index 0000000..45b57dd --- /dev/null +++ b/pkg/cloudapi/account/serialize.go @@ -0,0 +1,43 @@ +package account + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (la ListAccounts) Serialize(params ...string) (serialization.Serialized, error) { + if len(la.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(la, prefix, indent) + } + + return json.Marshal(la) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ia ItemAccount) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ia, prefix, indent) + } + + return json.Marshal(ia) +} diff --git a/pkg/cloudapi/account/sorting.go b/pkg/cloudapi/account/sorting.go new file mode 100644 index 0000000..ce243df --- /dev/null +++ b/pkg/cloudapi/account/sorting.go @@ -0,0 +1,60 @@ +package account + +import "sort" + +// SortByCreatedTime sorts ListAccounts by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (la ListAccounts) SortByCreatedTime(inverse bool) ListAccounts { + if len(la.Data) < 2 { + return la + } + + sort.Slice(la.Data, func(i, j int) bool { + if inverse { + return la.Data[i].CreatedTime > la.Data[j].CreatedTime + } + + return la.Data[i].CreatedTime < la.Data[j].CreatedTime + }) + + return la +} + +// SortByUpdatedTime sorts ListAccounts by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (la ListAccounts) SortByUpdatedTime(inverse bool) ListAccounts { + if len(la.Data) < 2 { + return la + } + + sort.Slice(la.Data, func(i, j int) bool { + if inverse { + return la.Data[i].UpdatedTime > la.Data[j].UpdatedTime + } + + return la.Data[i].UpdatedTime < la.Data[j].UpdatedTime + }) + + return la +} + +// SortByDeletedTime sorts ListAccounts by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (la ListAccounts) SortByDeletedTime(inverse bool) ListAccounts { + if len(la.Data) < 2 { + return la + } + + sort.Slice(la.Data, func(i, j int) bool { + if inverse { + return la.Data[i].DeletedTime > la.Data[j].DeletedTime + } + + return la.Data[i].DeletedTime < la.Data[j].DeletedTime + }) + + return la +} diff --git a/pkg/cloudapi/account/update.go b/pkg/cloudapi/account/update.go new file mode 100644 index 0000000..74fbfb2 --- /dev/null +++ b/pkg/cloudapi/account/update.go @@ -0,0 +1,75 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update account +type UpdateRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Name of the account + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Max size of memory in MB + // Required: false + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + + // Max size of aggregated vdisks in GB + // Required: false + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + + // Max number of CPU cores + // Required: false + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + + // Max sent/received network transfer peering + // Required: false + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + + // Max number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + + // If true send emails when a user is granted access to resources + // Required: false + SendAccessEmails bool `url:"sendAccessEmails" json:"sendAccessEmails"` + + // Limit (positive) or disable (0) GPU resources + // Required: false + GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` + + // List of strings with pools + // i.e.: ["sep1_poolName1", "sep2_poolName2", etc] + // Required: false + UniqPools []string `url:"uniqPools,omitempty" json:"uniqPools,omitempty"` +} + +// Update updates an account name and resource types and limits +func (a Account) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/update" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/account/update_user.go b/pkg/cloudapi/account/update_user.go new file mode 100644 index 0000000..8592832 --- /dev/null +++ b/pkg/cloudapi/account/update_user.go @@ -0,0 +1,49 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateUserRequest struct to update user access rights +type UpdateUserRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Userid/Email for registered users or emailaddress for unregistered users + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` + + // Account permission types: + // - 'R' for read only access + // - 'RCX' for Write + // - 'ARCXDU' for Admin + // Required: true + AccessType string `url:"accesstype" json:"accesstype" validate:"required,accessType"` +} + +// UpdateUser updates user access rights +func (a Account) UpdateUser(ctx context.Context, req UpdateUserRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/updateUser" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/audit.go b/pkg/cloudapi/audit.go new file mode 100644 index 0000000..19b437a --- /dev/null +++ b/pkg/cloudapi/audit.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/audit" +) + +// Accessing the Stack method group +func (ca *CloudAPI) Audit() *audit.Audit { + return audit.New(ca.client) +} diff --git a/pkg/cloudapi/audit/audit.go b/pkg/cloudapi/audit/audit.go new file mode 100644 index 0000000..81e0263 --- /dev/null +++ b/pkg/cloudapi/audit/audit.go @@ -0,0 +1,15 @@ +package audit + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to audit +type Audit struct { + client interfaces.Caller +} + +// Builder for audit endpoint +func New(client interfaces.Caller) *Audit { + return &Audit{ + client: client, + } +} diff --git a/pkg/cloudapi/audit/get.go b/pkg/cloudapi/audit/get.go new file mode 100644 index 0000000..0e5487b --- /dev/null +++ b/pkg/cloudapi/audit/get.go @@ -0,0 +1,46 @@ +package audit + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about account +type GetRequest struct { + // Audit GUID + // Required: true + AuditGuid string `url:"auditGuid" json:"auditGuid" validate:"required"` +} + +// Get gets information about audit as a RecordAudit struct +func (a Audit) Get(ctx context.Context, req GetRequest) (*RecordAudit, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordAudit{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about audit as an array of bytes +func (a Audit) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/audit/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/audit/models.go b/pkg/cloudapi/audit/models.go new file mode 100644 index 0000000..8bb0ef0 --- /dev/null +++ b/pkg/cloudapi/audit/models.go @@ -0,0 +1,41 @@ +package audit + +// Main info about audit +type RecordAudit struct { + + // Arguments + Arguments string `json:"args"` + + // Call + Call string `json:"call"` + + // GUID + GUID string `json:"guid"` + + // Kwargs + Kwargs string `json:"kwargs"` + + // RemoteAddr + RemoteAddr string `json:"remote_addr"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Result + Result string `json:"result"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Tags + Tags string `json:"tags"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // TimestampEnd + TimestampEnd float64 `json:"timestampEnd"` + + // User + User string `json:"user"` +} diff --git a/pkg/cloudapi/bservice.go b/pkg/cloudapi/bservice.go new file mode 100644 index 0000000..2f15c7d --- /dev/null +++ b/pkg/cloudapi/bservice.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/bservice" + +// Accessing the BService method group +func (ca *CloudAPI) BService() *bservice.BService { + return bservice.New(ca.client) +} diff --git a/pkg/cloudapi/bservice/bservice.go b/pkg/cloudapi/bservice/bservice.go new file mode 100644 index 0000000..19e769d --- /dev/null +++ b/pkg/cloudapi/bservice/bservice.go @@ -0,0 +1,16 @@ +// API Actor for managing Compute Group. This actor is a final API for endusers to manage Compute Group +package bservice + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to bservice +type BService struct { + client interfaces.Caller +} + +// Builder for bservice endpoints +func New(client interfaces.Caller) *BService { + return &BService{ + client, + } +} diff --git a/pkg/cloudapi/bservice/create.go b/pkg/cloudapi/bservice/create.go new file mode 100644 index 0000000..a9bc6b5 --- /dev/null +++ b/pkg/cloudapi/bservice/create.go @@ -0,0 +1,50 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct for BasicService +type CreateRequest struct { + // Name of the service + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of the Resource Group where this service will be placed + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of the user to deploy SSH key for. Pass empty string if no SSH key deployment is required + // Required: false + SSHUser string `url:"sshUser,omitempty" json:"sshUser,omitempty"` + + // SSH key to deploy for the specified user. Same key will be deployed to all computes of the service + // Required: false + SSHKey string `url:"sshKey,omitempty" json:"sshKey,omitempty"` +} + +// Create creates blank BasicService instance +func (b BService) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/create" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/delete.go b/pkg/cloudapi/bservice/delete.go new file mode 100644 index 0000000..4e5f8ea --- /dev/null +++ b/pkg/cloudapi/bservice/delete.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete basic service +type DeleteRequest struct { + // ID of the BasicService to be delete + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // If set to False, Basic service will be deleted to recycle bin. Otherwise destroyed immediately + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes BasicService instance +func (b BService) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/delete" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/disable.go b/pkg/cloudapi/bservice/disable.go new file mode 100644 index 0000000..c5b381b --- /dev/null +++ b/pkg/cloudapi/bservice/disable.go @@ -0,0 +1,40 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct for disable service +type DisableRequest struct { + // ID of the service to disable + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` +} + +// Disable disables service. +// Disabling a service technically means setting model status +// of all computes and service itself to DISABLED and stopping all computes. +func (b BService) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/disable" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/enable.go b/pkg/cloudapi/bservice/enable.go new file mode 100644 index 0000000..4ec6146 --- /dev/null +++ b/pkg/cloudapi/bservice/enable.go @@ -0,0 +1,41 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to disable service +type EnableRequest struct { + // ID of the service to enable + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` +} + +// Enable enables service. +// Enabling a service technically means setting model status of +// all computes and service itself to ENABLED. +// It does not start computes. +func (b BService) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/enable" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/filter.go b/pkg/cloudapi/bservice/filter.go new file mode 100644 index 0000000..f503212 --- /dev/null +++ b/pkg/cloudapi/bservice/filter.go @@ -0,0 +1,71 @@ +package bservice + +// FilterByID returns ListBasicServices with specified ID. +func (lbs ListBasicServices) FilterByID(id uint64) ListBasicServices { + predicate := func(ibs ItemBasicService) bool { + return ibs.ID == id + } + + return lbs.FilterFunc(predicate) +} + +// FilterByName returns ListBasicServices with specified Name. +func (lbs ListBasicServices) FilterByName(name string) ListBasicServices { + predicate := func(ibs ItemBasicService) bool { + return ibs.Name == name + } + + return lbs.FilterFunc(predicate) +} + +// FilterByRGID returns ListBasicServices with specified RGID. +func (lbs ListBasicServices) FilterByRGID(rgID uint64) ListBasicServices { + predicate := func(ibs ItemBasicService) bool { + return ibs.RGID == rgID + } + + return lbs.FilterFunc(predicate) +} + +// FilterByStatus returns ListBasicServices with specified Status. +func (lbs ListBasicServices) FilterByStatus(status string) ListBasicServices { + predicate := func(ibs ItemBasicService) bool { + return ibs.Status == status + } + + return lbs.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListBasicServices with specified TechStatus. +func (lbs ListBasicServices) FilterByTechStatus(techStatus string) ListBasicServices { + predicate := func(ibs ItemBasicService) bool { + return ibs.TechStatus == techStatus + } + + return lbs.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListResourceGroups based on a user-specified predicate. +func (lbs ListBasicServices) FilterFunc(predicate func(ItemBasicService) bool) ListBasicServices { + var result ListBasicServices + + for _, item := range lbs.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(lbs.Data)) + + return result +} + +// FindOne returns first found ItemBasicService +// If none was found, returns an empty struct. +func (lbs ListBasicServices) FindOne() ItemBasicService { + if lbs.EntryCount == 0 { + return ItemBasicService{} + } + + return lbs.Data[0] +} diff --git a/pkg/cloudapi/bservice/filter_test.go b/pkg/cloudapi/bservice/filter_test.go new file mode 100644 index 0000000..70e2949 --- /dev/null +++ b/pkg/cloudapi/bservice/filter_test.go @@ -0,0 +1,155 @@ +package bservice + +import "testing" + +var bservices = ListBasicServices{ + Data: []ItemBasicService{ + { + AccountID: 1, + AccountName: "std_1", + BaseDomain: "", + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1677743675, + DeletedBy: "", + DeletedTime: 0, + GID: 212, + Groups: []uint64{}, + GUID: 1, + ID: 1, + Name: "bservice_1", + ParentSrvID: 0, + ParentSrvType: "", + RGID: 7971, + RGName: "rg_1", + SSHUser: "", + Status: "CREATED", + TechStatus: "STOPPED", + UpdatedBy: "", + UpdatedTime: 0, + UserManaged: true, + }, + { + AccountID: 2, + AccountName: "std_2", + BaseDomain: "", + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1677743736, + DeletedBy: "", + DeletedTime: 0, + GID: 212, + Groups: []uint64{}, + GUID: 2, + ID: 2, + Name: "bservice_2", + ParentSrvID: 0, + ParentSrvType: "", + RGID: 7972, + RGName: "rg_2", + SSHUser: "", + Status: "CREATED", + TechStatus: "STOPPED", + UpdatedBy: "", + UpdatedTime: 0, + UserManaged: true, + }, + { + AccountID: 3, + AccountName: "std_3", + BaseDomain: "", + CreatedBy: "sample_user_2@decs3o", + CreatedTime: 1677743830, + DeletedBy: "", + DeletedTime: 0, + GID: 212, + Groups: []uint64{}, + GUID: 3, + ID: 3, + Name: "bservice_3", + ParentSrvID: 0, + ParentSrvType: "", + RGID: 7973, + RGName: "rg_3", + SSHUser: "", + Status: "ENABLED", + TechStatus: "STARTED", + UpdatedBy: "", + UpdatedTime: 0, + UserManaged: true, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := bservices.FilterByID(1).FindOne() + + if actual.ID != 1 { + t.Fatal("expected ID 1, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := bservices.FilterByName("bservice_3").FindOne() + + if actual.Name != "bservice_3" { + t.Fatal("expected Name 'bservice_3', found: ", actual.Name) + } +} + +func TestFilterByRGID(t *testing.T) { + actual := bservices.FilterByRGID(7971).FindOne() + + if actual.RGID != 7971 { + t.Fatal("expected RGID 7971, found: ", actual.RGID) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := bservices.FilterByStatus("CREATED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "CREATED" { + t.Fatal("expected Status 'CREATED', found: ", item.Status) + } + } +} + +func TestFilterByTechStatus(t *testing.T) { + actual := bservices.FilterByTechStatus("STOPPED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.TechStatus != "STOPPED" { + t.Fatal("expected TechStatus 'STOPPED', found: ", item.TechStatus) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := bservices.FilterFunc(func(ibs ItemBasicService) bool { + return ibs.CreatedBy == "sample_user_2@decs3o" + }) + + if len(actual.Data) > 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + if actual.FindOne().CreatedBy != "sample_user_2@decs3o" { + t.Fatal("expected 'sample_user_2@decs3o', found: ", actual.FindOne().CreatedBy) + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := bservices.SortByCreatedTime(true) + + if actual.Data[0].CreatedTime != 1677743830 || actual.Data[2].CreatedTime != 1677743675 { + t.Fatal("expected descending order, found ascending") + } +} diff --git a/pkg/cloudapi/bservice/get.go b/pkg/cloudapi/bservice/get.go new file mode 100644 index 0000000..b339a99 --- /dev/null +++ b/pkg/cloudapi/bservice/get.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about service +type GetRequest struct { + // ID of the service to query information + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` +} + +// Get gets detailed specifications for the BasicService as a RecordBasicService struct +func (b BService) Get(ctx context.Context, req GetRequest) (*RecordBasicService, error) { + res, err := b.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordBasicService{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed specifications for the BasicService as an array of bytes +func (b BService) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/get" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/bservice/group_add.go b/pkg/cloudapi/bservice/group_add.go new file mode 100644 index 0000000..84d92e6 --- /dev/null +++ b/pkg/cloudapi/bservice/group_add.go @@ -0,0 +1,108 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupAddRequest struct to create new compute group within BasicService +type GroupAddRequest struct { + // ID of the Basic Service to add a group to + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // Name of the Compute Group to add + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Computes number. Defines how many computes must be there in the group + // Required: true + Count uint64 `url:"count" json:"count" validate:"required"` + + // Compute CPU number. All computes in the group have the same CPU count + // Required: true + CPU uint64 `url:"cpu" json:"cpu" validate:"required"` + + // Compute RAM volume in MB. All computes in the group have the same RAM volume + // Required: true + RAM uint64 `url:"ram" json:"ram" validate:"required"` + + // Compute boot disk size in GB + // Required: true + Disk uint64 `url:"disk" json:"disk" validate:"required"` + + // OS image ID to create computes from + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // Compute driver + // should be one of: + // - KVM_X86 + // Required: true + Driver string `url:"driver" json:"driver" validate:"driver"` + + // Storage endpoint provider ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool to use if sepId is set, can be also empty if needed to be chosen by system + // Required: false + SEPPool string `url:"sepPool,omitempty" json:"sepPool,omitempty"` + + // Group role tag. Can be empty string, does not have to be unique + // Required: false + Role string `url:"role,omitempty" json:"role,omitempty"` + + // List of ViNSes to connect computes to + // Required: false + VINSes []uint64 `url:"vinses,omitempty" json:"vinses,omitempty"` + + // List of external networks to connect computes to + // Required: false + ExtNets []uint64 `url:"extnets,omitempty" json:"extnets,omitempty"` + + // Time of Compute Group readiness + // Required: false + TimeoutStart uint64 `url:"timeoutStart,omitempty" json:"timeoutStart,omitempty"` + + // Meta data for working group computes, format YAML "user_data": 1111 + // Required: false + UserData string `url:"userData,omitempty" json:"userData,omitempty"` +} + +// GetRAM returns RAM field values +func (r GroupAddRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +// GroupAdd creates new Compute Group within BasicService. +// Compute Group is NOT started automatically, +// so you need to explicitly start it +func (b BService) GroupAdd(ctx context.Context, req GroupAddRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupAdd" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_compute_remove.go b/pkg/cloudapi/bservice/group_compute_remove.go new file mode 100644 index 0000000..cfd492e --- /dev/null +++ b/pkg/cloudapi/bservice/group_compute_remove.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupComputeRemoveRequest struct to remove group compute +type GroupComputeRemoveRequest struct { + // ID of the Basic Service + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute GROUP + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` + + // ID of the Compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// GroupComputeRemove makes group compute remove of the Basic Service +func (b BService) GroupComputeRemove(ctx context.Context, req GroupComputeRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupComputeRemove" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_get.go b/pkg/cloudapi/bservice/group_get.go new file mode 100644 index 0000000..d5d140a --- /dev/null +++ b/pkg/cloudapi/bservice/group_get.go @@ -0,0 +1,44 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupGetRequest struct to get detailed information about Compute Group +type GroupGetRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` +} + +// GroupGet gets detailed specifications for the Compute Group +func (b BService) GroupGet(ctx context.Context, req GroupGetRequest) (*RecordGroup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupGet" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/bservice/group_parent_add.go b/pkg/cloudapi/bservice/group_parent_add.go new file mode 100644 index 0000000..e701667 --- /dev/null +++ b/pkg/cloudapi/bservice/group_parent_add.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupParentAddRequest struct to add parent Compute Group relation to the specified Compute Group +type GroupParentAddRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` + + // ID of the parent Compute Group to register with the current Compute Group + // Required: true + ParentID uint64 `url:"parentId" json:"parentId" validate:"required"` +} + +// GroupParentAdd add parent Compute Group relation to the specified Compute Group +func (b BService) GroupParentAdd(ctx context.Context, req GroupParentAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupParentAdd" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_parent_remove.go b/pkg/cloudapi/bservice/group_parent_remove.go new file mode 100644 index 0000000..92ef1c3 --- /dev/null +++ b/pkg/cloudapi/bservice/group_parent_remove.go @@ -0,0 +1,48 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupParentRemoveRequest struct to remove parent Compute Group +// relation from the specified Compute Group +type GroupParentRemoveRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` + + // ID of the parent Compute Group + // to remove from the current Compute Group + // Required: true + ParentID uint64 `url:"parentId" json:"parentId" validate:"required"` +} + +// GroupParentRemove removes parent Compute Group relation to the specified Compute Group +func (b BService) GroupParentRemove(ctx context.Context, req GroupParentRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupParentRemove" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_remove.go b/pkg/cloudapi/bservice/group_remove.go new file mode 100644 index 0000000..9349be4 --- /dev/null +++ b/pkg/cloudapi/bservice/group_remove.go @@ -0,0 +1,43 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupRemoveRequest struct for destroy the specified Compute Group +type GroupRemoveRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` +} + +// GroupRemove destroy the specified Compute Group +func (b BService) GroupRemove(ctx context.Context, req GroupRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupRemove" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_resize.go b/pkg/cloudapi/bservice/group_resize.go new file mode 100644 index 0000000..dd5839e --- /dev/null +++ b/pkg/cloudapi/bservice/group_resize.go @@ -0,0 +1,53 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupResizeRequest struct to resize the group +type GroupResizeRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group to resize + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` + + // Either delta or absolute value of computes + // Required: true + Count int64 `url:"count" json:"count" validate:"required"` + + // Either delta or absolute value of computes + // Should be one of: + // - ABSOLUTE + // - RELATIVE + // Required: true + Mode string `url:"mode" json:"mode" validate:"bserviceMode"` +} + +// GroupResize resize the group by changing the number of computes +func (b BService) GroupResize(ctx context.Context, req GroupResizeRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupResize" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_start.go b/pkg/cloudapi/bservice/group_start.go new file mode 100644 index 0000000..7005fea --- /dev/null +++ b/pkg/cloudapi/bservice/group_start.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupStartRequest struct to start the specified Compute Group +type GroupStartRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group to start + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` +} + +// GroupStart starts the specified Compute Group within BasicService +func (b BService) GroupStart(ctx context.Context, req GroupStartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupStart" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_stop.go b/pkg/cloudapi/bservice/group_stop.go new file mode 100644 index 0000000..e4eaebb --- /dev/null +++ b/pkg/cloudapi/bservice/group_stop.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupStopRequest struct to stop the specified Compute Group +type GroupStopRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group to stop + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` + + // Force stop Compute Group + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// GroupStop stops the specified Compute Group within BasicService +func (b BService) GroupStop(ctx context.Context, req GroupStopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupStop" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_update.go b/pkg/cloudapi/bservice/group_update.go new file mode 100644 index 0000000..db7809f --- /dev/null +++ b/pkg/cloudapi/bservice/group_update.go @@ -0,0 +1,76 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupUpdateRequest struct to update existing Compute group +type GroupUpdateRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` + + // Specify non-empty string to update Compute Group name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Specify non-empty string to update group role + // Required: false + Role string `url:"role,omitempty" json:"role,omitempty"` + + // Specify positive value to set new compute CPU count + // Required: false + CPU uint64 `url:"cpu,omitempty" json:"cpu,omitempty"` + + // Specify positive value to set new compute RAM volume in MB + // Required: false + RAM uint64 `url:"ram,omitempty" json:"ram,omitempty"` + + // Specify new compute boot disk size in GB + // Required: false + Disk uint64 `url:"disk,omitempty" json:"disk,omitempty"` + + // Force resize Compute Group + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// GetRAM returns RAM field values +func (r GroupUpdateRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +// GroupUpdate updates existing Compute group within Basic Service and apply new settings to its computes as necessary +func (b BService) GroupUpdate(ctx context.Context, req GroupUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupUpdate" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_update_extnet.go b/pkg/cloudapi/bservice/group_update_extnet.go new file mode 100644 index 0000000..c241847 --- /dev/null +++ b/pkg/cloudapi/bservice/group_update_extnet.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupUpdateExtNetRequest struct to update External Network settings +type GroupUpdateExtNetRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` + + // List of Extnets to connect computes + // Required: false + ExtNets []uint64 `url:"extnets,omitempty" json:"extnets,omitempty"` +} + +// GroupUpdateExtNet updates External Network settings for the group according to the new list +func (b BService) GroupUpdateExtNet(ctx context.Context, req GroupUpdateExtNetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupUpdateExtnet" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/group_update_vins.go b/pkg/cloudapi/bservice/group_update_vins.go new file mode 100644 index 0000000..b329191 --- /dev/null +++ b/pkg/cloudapi/bservice/group_update_vins.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GroupUpdateVINSRequest struct to update VINS settings +type GroupUpdateVINSRequest struct { + // ID of the Basic Service of Compute Group + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the Compute Group + // Required: true + CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` + + // List of ViNSes to connect computes + // Required: false + VINSes []uint64 `url:"vinses,omitempty" json:"vinses,omitempty"` +} + +// GroupUpdateVINS update ViNS settings for the group according to the new list +func (b BService) GroupUpdateVINS(ctx context.Context, req GroupUpdateVINSRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/groupUpdateVins" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/ids.go b/pkg/cloudapi/bservice/ids.go new file mode 100644 index 0000000..a204ed6 --- /dev/null +++ b/pkg/cloudapi/bservice/ids.go @@ -0,0 +1,37 @@ +package bservice + +// IDs gets array of BasicServiceIDs from ListBasicServices struct +func (lbs ListBasicServices) IDs() []uint64 { + res := make([]uint64, 0, len(lbs.Data)) + for _, bs := range lbs.Data { + res = append(res, bs.ID) + } + return res +} + +// IDs gets array of ComputeIDs from ListComputes struct +func (lc ListComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lc)) + for _, c := range lc { + res = append(res, c.ID) + } + return res +} + +// IDs gets array of GroupIDs from ListGroups struct +func (lg ListGroups) IDs() []uint64 { + res := make([]uint64, 0, len(lg)) + for _, g := range lg { + res = append(res, g.ID) + } + return res +} + +// IDs gets array of GroupComputeIDs from ListGroupComputes struct +func (lgc ListGroupComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lgc)) + for _, gc := range lgc { + res = append(res, gc.ID) + } + return res +} diff --git a/pkg/cloudapi/bservice/list.go b/pkg/cloudapi/bservice/list.go new file mode 100644 index 0000000..97e61a6 --- /dev/null +++ b/pkg/cloudapi/bservice/list.go @@ -0,0 +1,87 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of BasicService instances +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // ID of the account to query for BasicService instances + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group name + // Required: false + RGName string `url:"rgName,omitempty" json:"rgName,omitempty"` + + // ID of the resource group to query for BasicService instances + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by account name + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of BasicService instances associated with the specified Resource Group as a ListBasicServices struct +func (b BService) List(ctx context.Context, req ListRequest) (*ListBasicServices, error) { + + res, err := b.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListBasicServices{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of BasicService instances associated with the specified Resource Group as an array of bytes +func (b BService) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/list" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/bservice/list_deleted.go b/pkg/cloudapi/bservice/list_deleted.go new file mode 100644 index 0000000..d0ad3a9 --- /dev/null +++ b/pkg/cloudapi/bservice/list_deleted.go @@ -0,0 +1,56 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted BasicService instances +type ListDeletedRequest struct { + // ID of the account to query for BasicService instances + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // ID of the resource group to query for BasicService instances + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list of deleted BasicService instances associated with the specified Resource Group +func (b BService) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListBasicServices, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/listDeleted" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListBasicServices{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/bservice/models.go b/pkg/cloudapi/bservice/models.go new file mode 100644 index 0000000..ab50f75 --- /dev/null +++ b/pkg/cloudapi/bservice/models.go @@ -0,0 +1,389 @@ +package bservice + +// Detailed info about BasicService +type RecordBasicService struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Base domain + BaseDomain string `json:"baseDomain"` + + // List Computes + Computes ListComputes `json:"computes"` + + // Number of cores + CPUTotal uint64 `json:"cpuTotal"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Amount of disk space used, GB + DiskTotal uint64 `json:"diskTotal"` + + // Grid ID + GID uint64 `json:"gid"` + + // List of Service Compute Groups + Groups ListGroups `json:"groups"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Parent service ID + ParentSrvID uint64 `json:"parentSrvId"` + + // Parent service type + ParentSrvType string `json:"parentSrvType"` + + // Total amount of RAM, MB + RAMTotal uint64 `json:"ramTotal"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // List of snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // SSH key for connection + SSHKey string `json:"sshKey"` + + // Username for SSH connection + SSHUser string `json:"sshUser"` + + // status + Status string `json:"status"` + + // TechStatus + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Whether user controlled + UserManaged bool `json:"userManaged"` +} + +// Main information about Compute +type ItemCompute struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Architecture + Architecture string `json:"arch"` + + // Compute group ID + CompGroupID uint64 `json:"compgroupId"` + + // Compute group name + CompGroupName string `json:"compgroupName"` + + // compute group role + CompGroupRole string `json:"compgroupRole"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // StackID + StackID uint64 `json:"stackId"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` +} + +// List of Computes +type ListComputes []ItemCompute + +// Main information about Group +type ItemGroup struct { + // Amount of computes + Computes uint64 `json:"computes"` + + // Consistency + Consistency bool `json:"consistency"` + + // Group ID + ID uint64 `json:"id"` + + // Group name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // TechStatus + TechStatus string `json:"techStatus"` +} + +// List of Groups +type ListGroups []ItemGroup + +// Main information about Snapshot +type ItemSnapshot struct { + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Timestamp + Timestamp uint64 `json:"timestamp"` + + // Valid or not + Valid bool `json:"valid"` +} + +// List of Snapshot +type ListSnapshots []ItemSnapshot + +// List of Snapshots +type ListInfoSnapshots struct { + // Data + Data ListSnapshots `json:"data"` + + // EntryCount + EntryCount uint64 `json:"entryCount"` +} + +// Main information about Group +type RecordGroup struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account Name + AccountName string `json:"accountName"` + + // List of Computes + Computes ListGroupComputes `json:"computes"` + + // Consistency or not + Consistency bool `json:"consistency"` + + // Number of CPU + CPU uint64 `json:"cpu"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Amount of disk + Disk uint64 `json:"disk"` + + // Driver + Driver string `json:"driver"` + + // list of External Network IDs + ExtNets []uint64 `json:"extnets"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // List of Parent IDs + Parents []uint64 `json:"parents"` + + // Pool name + PoolName string `json:"poolName"` + + // Number of RAM, MB + RAM uint64 `json:"ram"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Role + Role string `json:"role"` + + // SEPID + SEPID uint64 `json:"sepId"` + + // Sequence number + SeqNo uint64 `json:"seqNo"` + + // Service ID + ServiceID uint64 `json:"serviceId"` + + // Status + Status string `json:"status"` + + // TechStatus + TechStatus string `json:"techStatus"` + + // Timeout Start + TimeoutStart uint64 `json:"timeoutStart"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // List of VINS IDs + VINSes []uint64 `json:"vinses"` +} + +// Main information about Group Compute +type ItemGroupCompute struct { + // ID + ID uint64 `json:"id"` + + // IP Addresses + IPAddresses []string `json:"ipAddresses"` + + // Name + Name string `json:"name"` + + // List of information about OS Users + OSUsers ListOSUsers `json:"osUsers"` +} + +// List of Group Computes +type ListGroupComputes []ItemGroupCompute + +// Main information about OS User +type ItemOSUser struct { + // Login + Login string `json:"login"` + + // Password + Password string `json:"password"` +} + +// List of information about OS Users +type ListOSUsers []ItemOSUser + +// Main information about BasicService +type ItemBasicService struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Base domain + BaseDomain string `json:"baseDomain"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Grid ID + GID uint64 `json:"gid"` + + // List of group IDs + Groups []uint64 `json:"groups"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Parent service ID + ParentSrvID uint64 `json:"parentSrvId"` + + // Parent service type + ParentSrvType string `json:"parentSrvType"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // SSH user + SSHUser string `json:"sshUser"` + + // Status + Status string `json:"status"` + + // TechStatus + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User Managed or not + UserManaged bool `json:"userManaged"` +} + +// List of BasicServices +type ListBasicServices struct { + Data []ItemBasicService `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/bservice/restore.go b/pkg/cloudapi/bservice/restore.go new file mode 100644 index 0000000..fe350e3 --- /dev/null +++ b/pkg/cloudapi/bservice/restore.go @@ -0,0 +1,38 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore BasicService instance +type RestoreRequest struct { + // ID of the BasicService to be restored + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` +} + +// Restore restores BasicService instance +func (b BService) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/restore" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/serialize.go b/pkg/cloudapi/bservice/serialize.go new file mode 100644 index 0000000..840249d --- /dev/null +++ b/pkg/cloudapi/bservice/serialize.go @@ -0,0 +1,43 @@ +package bservice + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lbs ListBasicServices) Serialize(params ...string) (serialization.Serialized, error) { + if lbs.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lbs, prefix, indent) + } + + return json.Marshal(lbs) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ibs ItemBasicService) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ibs, prefix, indent) + } + + return json.Marshal(ibs) +} diff --git a/pkg/cloudapi/bservice/snapshot_create.go b/pkg/cloudapi/bservice/snapshot_create.go new file mode 100644 index 0000000..2c867c5 --- /dev/null +++ b/pkg/cloudapi/bservice/snapshot_create.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotCreateRequest struct to create snapshot +type SnapshotCreateRequest struct { + // ID of the Basic Service + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // Label of the snapshot + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotCreate create snapshot of the Basic Service +func (b BService) SnapshotCreate(ctx context.Context, req SnapshotCreateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/snapshotCreate" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/snapshot_delete.go b/pkg/cloudapi/bservice/snapshot_delete.go new file mode 100644 index 0000000..5c3dcca --- /dev/null +++ b/pkg/cloudapi/bservice/snapshot_delete.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotDeleteRequest struct to delete snapshot +type SnapshotDeleteRequest struct { + // ID of the Basic Service + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // Label of the snapshot + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotDelete delete snapshot of the Basic Service +func (b BService) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/snapshotDelete" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/snapshot_list.go b/pkg/cloudapi/bservice/snapshot_list.go new file mode 100644 index 0000000..86ea387 --- /dev/null +++ b/pkg/cloudapi/bservice/snapshot_list.go @@ -0,0 +1,40 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotListRequest struct to get list of existing snapshots +type SnapshotListRequest struct { + // ID of the Basic Service + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` +} + +// SnapshotList gets list existing snapshots of the Basic Service +func (b BService) SnapshotList(ctx context.Context, req SnapshotListRequest) (*ListInfoSnapshots, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/snapshotList" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListInfoSnapshots{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/bservice/snapshot_rollback.go b/pkg/cloudapi/bservice/snapshot_rollback.go new file mode 100644 index 0000000..85a6299 --- /dev/null +++ b/pkg/cloudapi/bservice/snapshot_rollback.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotRollbackRequest struct to rollback snapshot +type SnapshotRollbackRequest struct { + // ID of the Basic Service + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // Label of the snapshot + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotRollback rollback snapshot of the Basic Service +func (b BService) SnapshotRollback(ctx context.Context, req SnapshotRollbackRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/snapshotRollback" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/sorting.go b/pkg/cloudapi/bservice/sorting.go new file mode 100644 index 0000000..3fdaed1 --- /dev/null +++ b/pkg/cloudapi/bservice/sorting.go @@ -0,0 +1,60 @@ +package bservice + +import "sort" + +// SortByCreatedTime sorts ListBasicServices by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lbs ListBasicServices) SortByCreatedTime(inverse bool) ListBasicServices { + if lbs.EntryCount < 2 { + return lbs + } + + sort.Slice(lbs.Data, func(i, j int) bool { + if inverse { + return lbs.Data[i].CreatedTime > lbs.Data[j].CreatedTime + } + + return lbs.Data[i].CreatedTime < lbs.Data[j].CreatedTime + }) + + return lbs +} + +// SortByUpdatedTime sorts ListBasicServices by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lbs ListBasicServices) SortByUpdatedTime(inverse bool) ListBasicServices { + if lbs.EntryCount < 2 { + return lbs + } + + sort.Slice(lbs.Data, func(i, j int) bool { + if inverse { + return lbs.Data[i].UpdatedTime > lbs.Data[j].UpdatedTime + } + + return lbs.Data[i].UpdatedTime < lbs.Data[j].UpdatedTime + }) + + return lbs +} + +// SortByDeletedTime sorts ListBasicServices by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lbs ListBasicServices) SortByDeletedTime(inverse bool) ListBasicServices { + if lbs.EntryCount < 2 { + return lbs + } + + sort.Slice(lbs.Data, func(i, j int) bool { + if inverse { + return lbs.Data[i].DeletedTime > lbs.Data[j].DeletedTime + } + + return lbs.Data[i].DeletedTime < lbs.Data[j].DeletedTime + }) + + return lbs +} diff --git a/pkg/cloudapi/bservice/start.go b/pkg/cloudapi/bservice/start.go new file mode 100644 index 0000000..ed64952 --- /dev/null +++ b/pkg/cloudapi/bservice/start.go @@ -0,0 +1,40 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StartRequest struct to start service +type StartRequest struct { + // ID of the service to start + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` +} + +// Start starts service. +// Starting a service technically means starting computes from all +// service groups according to group relations +func (b BService) Start(ctx context.Context, req StartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/start" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/bservice/stop.go b/pkg/cloudapi/bservice/stop.go new file mode 100644 index 0000000..ee41865 --- /dev/null +++ b/pkg/cloudapi/bservice/stop.go @@ -0,0 +1,40 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StopRequest struct to stop service +type StopRequest struct { + // ID of the service to stop + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` +} + +// Stop stops service. +// Stopping a service technically means stopping computes from +// all service groups +func (b BService) Stop(ctx context.Context, req StopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/stop" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/cloudapi.go b/pkg/cloudapi/cloudapi.go new file mode 100644 index 0000000..d4a5a17 --- /dev/null +++ b/pkg/cloudapi/cloudapi.go @@ -0,0 +1,18 @@ +// List of method groups for the user +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to CloudAPI groups +type CloudAPI struct { + client interfaces.Caller +} + +// Builder to get access to CloudAPI +func New(client interfaces.Caller) *CloudAPI { + return &CloudAPI{ + client: client, + } +} diff --git a/pkg/cloudapi/compute.go b/pkg/cloudapi/compute.go new file mode 100644 index 0000000..cd507fe --- /dev/null +++ b/pkg/cloudapi/compute.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/compute" +) + +// Accessing the Compute method group +func (ca *CloudAPI) Compute() *compute.Compute { + return compute.New(ca.client) +} diff --git a/pkg/cloudapi/compute/affinity_group_check_start.go b/pkg/cloudapi/compute/affinity_group_check_start.go new file mode 100644 index 0000000..0c454cf --- /dev/null +++ b/pkg/cloudapi/compute/affinity_group_check_start.go @@ -0,0 +1,36 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityGroupCheckStartRequest struct to check all computes with current affinity label can start +type AffinityGroupCheckStartRequest struct { + // ID of the resource group + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Affinity group label + // Required: true + AffinityLabel string `url:"affinityLabel" json:"affinityLabel" validate:"required"` +} + +// AffinityGroupCheckStart check all computes with current affinity label can start +func (c Compute) AffinityGroupCheckStart(ctx context.Context, req AffinityGroupCheckStartRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/affinityGroupCheckStart" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/compute/affinity_label_remove.go b/pkg/cloudapi/compute/affinity_label_remove.go new file mode 100644 index 0000000..e6ca1d1 --- /dev/null +++ b/pkg/cloudapi/compute/affinity_label_remove.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityLabelRemoveRequest struct to clear affinity label for compute +type AffinityLabelRemoveRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// AffinityLabelRemove clear affinity label for compute +func (c Compute) AffinityLabelRemove(ctx context.Context, req AffinityLabelRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/affinityLabelRemove" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/affinity_label_set.go b/pkg/cloudapi/compute/affinity_label_set.go new file mode 100644 index 0000000..245fede --- /dev/null +++ b/pkg/cloudapi/compute/affinity_label_set.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityLabelSetRequest struct to set affinity label for compute +type AffinityLabelSetRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Affinity group label + // Required: true + AffinityLabel string `url:"affinityLabel" json:"affinityLabel" validate:"required"` +} + +// AffinityLabelSet set affinity label for compute +func (c Compute) AffinityLabelSet(ctx context.Context, req AffinityLabelSetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/affinityLabelSet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/affinity_relations.go b/pkg/cloudapi/compute/affinity_relations.go new file mode 100644 index 0000000..7c5cd59 --- /dev/null +++ b/pkg/cloudapi/compute/affinity_relations.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityRelationsRequest struct to get dict of computes +type AffinityRelationsRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// AffinityRelations gets dict of computes divided by affinity and anti affinity rules +func (c Compute) AffinityRelations(ctx context.Context, req AffinityRelationsRequest) (*RecordAffinityRelations, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/affinityRelations" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordAffinityRelations{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/compute/affinity_rule_add.go b/pkg/cloudapi/compute/affinity_rule_add.go new file mode 100644 index 0000000..23aad64 --- /dev/null +++ b/pkg/cloudapi/compute/affinity_rule_add.go @@ -0,0 +1,66 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityRuleAddRequest struct to add affinity rule +type AffinityRuleAddRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Compute or node, for whom rule applies + // Required: true + Topology string `url:"topology" json:"topology" validate:"computeTopology"` + + // The degree of 'strictness' of this rule + // Should be one of: + // - RECOMMENDED + // - REQUIRED + // Required: true + Policy string `url:"policy" json:"policy" validate:"computePolicy"` + + // The comparison mode is 'value', recorded by the specified 'key' + // Should be one of: + // - EQ + // - EN + // - ANY + // Required: true + Mode string `url:"mode" json:"mode" validate:"computeMode"` + + // Key that are taken into account when analyzing this rule will be identified + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Value that must match the key to be taken into account when analyzing this rule + // Required: false + // Not required on purpose: despite required tag on platform, empty string is allowed + Value string `url:"value" json:"value"` +} + +// AffinityRuleAdd add affinity rule +func (c Compute) AffinityRuleAdd(ctx context.Context, req AffinityRuleAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/affinityRuleAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/affinity_rule_remove.go b/pkg/cloudapi/compute/affinity_rule_remove.go new file mode 100644 index 0000000..7f680a6 --- /dev/null +++ b/pkg/cloudapi/compute/affinity_rule_remove.go @@ -0,0 +1,66 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityRuleRemoveRequest struct to remove affinity rule +type AffinityRuleRemoveRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Compute or node, for whom rule applies + // Required: true + Topology string `url:"topology" json:"topology" validate:"computeTopology"` + + // The degree of 'strictness' of this rule + // Should be one of: + // - RECOMMENDED + // - REQUIRED + // Required: true + Policy string `url:"policy" json:"policy" validate:"computePolicy"` + + // The comparison mode is 'value', recorded by the specified 'key' + // Should be one of: + // - EQ + // - EN + // - ANY + // Required: true + Mode string `url:"mode" json:"mode" validate:"computeMode"` + + // Key that are taken into account when analyzing this rule will be identified + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Value that must match the key to be taken into account when analyzing this rule + // Required: false + // Not required on purpose: despite required tag on platform, empty string is allowed + Value string `url:"value" json:"value"` +} + +// AffinityRuleRemove remove affinity rule +func (c Compute) AffinityRuleRemove(ctx context.Context, req AffinityRuleRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/affinityRuleRemove" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/affinity_rules_clear.go b/pkg/cloudapi/compute/affinity_rules_clear.go new file mode 100644 index 0000000..9e0bc80 --- /dev/null +++ b/pkg/cloudapi/compute/affinity_rules_clear.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityRulesClearRequest struct to clear affinity rules +type AffinityRulesClearRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// AffinityRulesClear clear affinity rules +func (c Compute) AffinityRulesClear(ctx context.Context, req AffinityRulesClearRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/affinityRulesClear" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/anti_affinity_rule_add.go b/pkg/cloudapi/compute/anti_affinity_rule_add.go new file mode 100644 index 0000000..582e67d --- /dev/null +++ b/pkg/cloudapi/compute/anti_affinity_rule_add.go @@ -0,0 +1,66 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AntiAffinityRuleAddRequest struct to add anti affinity rule +type AntiAffinityRuleAddRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Compute or node, for whom rule applies + // Required: true + Topology string `url:"topology" json:"topology" validate:"computeTopology"` + + // The degree of 'strictness' of this rule + // Should be one of: + // - RECOMMENDED + // - REQUIRED + // Required: true + Policy string `url:"policy" json:"policy" validate:"computePolicy"` + + // The comparison mode is 'value', recorded by the specified 'key' + // Should be one of: + // - EQ + // - EN + // - ANY + // Required: true + Mode string `url:"mode" json:"mode" validate:"computeMode"` + + // Key that are taken into account when analyzing this rule will be identified + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Value that must match the key to be taken into account when analyzing this rule + // Required: false + // Not required on purpose: despite required tag on platform, empty string is allowed + Value string `url:"value" json:"value"` +} + +// AntiAffinityRuleAdd add anti affinity rule +func (c Compute) AntiAffinityRuleAdd(ctx context.Context, req AntiAffinityRuleAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/antiAffinityRuleAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/anti_affinity_rule_remove.go b/pkg/cloudapi/compute/anti_affinity_rule_remove.go new file mode 100644 index 0000000..b1f2551 --- /dev/null +++ b/pkg/cloudapi/compute/anti_affinity_rule_remove.go @@ -0,0 +1,66 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AntiAffinityRuleRemoveRequest struct to remove anti affinity rule +type AntiAffinityRuleRemoveRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Compute or node, for whom rule applies + // Required: true + Topology string `url:"topology" json:"topology" validate:"computeTopology"` + + // The degree of 'strictness' of this rule + // Should be one of: + // - RECOMMENDED + // - REQUIRED + // Required: true + Policy string `url:"policy" json:"policy" validate:"computePolicy"` + + // The comparison mode is 'value', recorded by the specified 'key' + // Should be one of: + // - EQ + // - EN + // - ANY + // Required: true + Mode string `url:"mode" json:"mode" validate:"computeMode"` + + // Key that are taken into account when analyzing this rule will be identified + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Value that must match the key to be taken into account when analyzing this rule + // Required: false + // Not required on purpose: despite required tag on platform, empty string is allowed + Value string `url:"value" json:"value"` +} + +// AntiAffinityRuleRemove remove anti affinity rule +func (c Compute) AntiAffinityRuleRemove(ctx context.Context, req AntiAffinityRuleRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/antiAffinityRuleRemove" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/anti_affinity_rules_clear.go b/pkg/cloudapi/compute/anti_affinity_rules_clear.go new file mode 100644 index 0000000..25dc85d --- /dev/null +++ b/pkg/cloudapi/compute/anti_affinity_rules_clear.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AntiAffinityRulesClearRequest struct to clear anti affinity rules +type AntiAffinityRulesClearRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// AntiAffinityRulesClear clear anti affinity rules +func (c Compute) AntiAffinityRulesClear(ctx context.Context, req AntiAffinityRulesClearRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/antiAffinityRulesClear" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/attach_gpu.go b/pkg/cloudapi/compute/attach_gpu.go new file mode 100644 index 0000000..880bbe6 --- /dev/null +++ b/pkg/cloudapi/compute/attach_gpu.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AttachGPURequest struct to attach GPU for compute +type AttachGPURequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Identifier vGPU + // Required: true + VGPUID uint64 `url:"vgpuId" json:"vgpuId" validate:"required"` +} + +// AttachGPU attach GPU for compute, returns vgpu id on success +func (c Compute) AttachGPU(ctx context.Context, req AttachGPURequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/attachGpu" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/attach_pci_device.go b/pkg/cloudapi/compute/attach_pci_device.go new file mode 100644 index 0000000..8695068 --- /dev/null +++ b/pkg/cloudapi/compute/attach_pci_device.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AttachPCIDeviceRequest struct to attach PCI device +type AttachPCIDeviceRequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` +} + +// AttachPCIDevice attach PCI device +func (c Compute) AttachPCIDevice(ctx context.Context, req AttachPCIDeviceRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/attachPciDevice" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/audits.go b/pkg/cloudapi/compute/audits.go new file mode 100644 index 0000000..6029d89 --- /dev/null +++ b/pkg/cloudapi/compute/audits.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuditsRequest struct to get audit records +type AuditsRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Audits gets audit records for the specified compute object +func (c Compute) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/audits" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/compute/boot_disk_set.go b/pkg/cloudapi/compute/boot_disk_set.go new file mode 100644 index 0000000..ee14d57 --- /dev/null +++ b/pkg/cloudapi/compute/boot_disk_set.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BootDiskSetRequest struct to set boot disk for compute +type BootDiskSetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to set as boot + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// BootDiskSet sets boot disk for compute +func (c Compute) BootDiskSet(ctx context.Context, req BootDiskSetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/bootDiskSet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/boot_order_get.go b/pkg/cloudapi/compute/boot_order_get.go new file mode 100644 index 0000000..9526356 --- /dev/null +++ b/pkg/cloudapi/compute/boot_order_get.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BootOrderGetRequest struct to get boot order +type BootOrderGetRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// BootOrderGet gets actual compute boot order information +func (c Compute) BootOrderGet(ctx context.Context, req BootOrderGetRequest) ([]string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/bootOrderGet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + orders := make([]string, 0) + + err = json.Unmarshal(res, &orders) + if err != nil { + return nil, err + } + + return orders, nil +} diff --git a/pkg/cloudapi/compute/boot_order_set.go b/pkg/cloudapi/compute/boot_order_set.go new file mode 100644 index 0000000..cf8ec1c --- /dev/null +++ b/pkg/cloudapi/compute/boot_order_set.go @@ -0,0 +1,48 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BootOrderSetRequest struct to set boot order +type BootOrderSetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // List of boot devices + // Should be one of: + // - cdrom + // - network + // - hd + // Required: true + Order []string `url:"order" json:"order" validate:"min=1,computeOrder"` +} + +// BootOrderSet sets compute boot order +func (c Compute) BootOrderSet(ctx context.Context, req BootOrderSetRequest) ([]string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/bootOrderSet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + orders := make([]string, 0) + + err = json.Unmarshal(res, &orders) + if err != nil { + return nil, err + } + + return orders, nil +} diff --git a/pkg/cloudapi/compute/cd_eject.go b/pkg/cloudapi/compute/cd_eject.go new file mode 100644 index 0000000..fbc9c56 --- /dev/null +++ b/pkg/cloudapi/compute/cd_eject.go @@ -0,0 +1,37 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CDEjectRequest struct to eject CD image +type CDEjectRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// CDEject eject CD image to compute's CD-ROM +func (c Compute) CDEject(ctx context.Context, req CDEjectRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/cdEject" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + return result, nil +} diff --git a/pkg/cloudapi/compute/cd_insert.go b/pkg/cloudapi/compute/cd_insert.go new file mode 100644 index 0000000..ec942cd --- /dev/null +++ b/pkg/cloudapi/compute/cd_insert.go @@ -0,0 +1,41 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CDInsertRequest struct to insert new CD image +type CDInsertRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of CD-ROM image + // Required: true + CDROMID uint64 `url:"cdromId" json:"cdromId" validate:"required"` +} + +// CDInsert insert new CD image to compute's CD-ROM +func (c Compute) CDInsert(ctx context.Context, req CDInsertRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/cdInsert" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + return result, nil +} diff --git a/pkg/cloudapi/compute/change_ip.go b/pkg/cloudapi/compute/change_ip.go new file mode 100644 index 0000000..dc771bc --- /dev/null +++ b/pkg/cloudapi/compute/change_ip.go @@ -0,0 +1,54 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ChangeIPRequest struct to change IP for network +type ChangeIPRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Network type + // 'EXTNET' for connect to external network directly + // 'VINS' for connect to ViNS + // Required: true + NetType string `url:"netType" json:"netType" validate:"computeNetType"` + + // Network ID for connect to + // For EXTNET - external network ID + // For VINS - VINS ID + // Required: true + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // IP address to which we will change the existing one, it must be from the same subnet + // Required: true + IPAddr string `url:"ipAddr" json:"ipAddr" validate:"required"` +} + +// ChangeIP change reserved IP for compute instance +func (c Compute) ChangeIP(ctx context.Context, req ChangeIPRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/changeIp" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/change_link_state.go b/pkg/cloudapi/compute/change_link_state.go new file mode 100644 index 0000000..acdc668 --- /dev/null +++ b/pkg/cloudapi/compute/change_link_state.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ChangeLinkStateRequest struct to change link state +type ChangeLinkStateRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Interface name or MAC address + // Required: true + Interface string `url:"interface" json:"interface" validate:"required"` + + // Interface state + // Must be either "on" or "off" + // Required: true + State string `url:"state" json:"state" validate:"required,interfaceState"` +} + +// ChangeLinkState changes the status link virtual of compute +func (c Compute) ChangeLinkState(ctx context.Context, req ChangeLinkStateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/changeLinkState" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + return result, nil +} diff --git a/pkg/cloudapi/compute/clone.go b/pkg/cloudapi/compute/clone.go new file mode 100644 index 0000000..f66b7f0 --- /dev/null +++ b/pkg/cloudapi/compute/clone.go @@ -0,0 +1,55 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CloneRequest struct to clone compute instance +type CloneRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the clone + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Timestamp of the parent's snapshot to create clone from + // Required: false + SnapshotTimestamp uint64 `url:"snapshotTimestamp,omitempty" json:"snapshotTimestamp,omitempty"` + + // Name of the parent's snapshot to create clone from + // Required: false + SnapshotName string `url:"snapshotName,omitempty" json:"snapshotName,omitempty"` + + // true ignore that the compute is running + // Default: false + // Required: false + Force bool `url:"force" json:"force"` +} + +// Clone clones compute instance +func (c Compute) Clone(ctx context.Context, req CloneRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/clone" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/compute.go b/pkg/cloudapi/compute/compute.go new file mode 100644 index 0000000..070bc72 --- /dev/null +++ b/pkg/cloudapi/compute/compute.go @@ -0,0 +1,18 @@ +// API Actor for managing Compute. This actor is a final API for endusers to manage Compute +package compute + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to compute +type Compute struct { + client interfaces.Caller +} + +// Builder for compute endpoints +func New(client interfaces.Caller) *Compute { + return &Compute{ + client, + } +} diff --git a/pkg/cloudapi/compute/create_template.go b/pkg/cloudapi/compute/create_template.go new file mode 100644 index 0000000..0a28dfb --- /dev/null +++ b/pkg/cloudapi/compute/create_template.go @@ -0,0 +1,78 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateTemplateRequest struct to create template +type CreateTemplateRequest struct { + // ID of the compute to create template from + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name to assign to the template being created + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +type wrapperCreateTemplateRequest struct { + CreateTemplateRequest + + AsyncMode bool `url:"asyncMode"` +} + +// CreateTemplate create template from compute instance +func (c Compute) CreateTemplate(ctx context.Context, req CreateTemplateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateTemplateRequest{ + CreateTemplateRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/createTemplate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} + +// CreateTemplateAsync create template from compute instance +func (c Compute) CreateTemplateAsync(ctx context.Context, req CreateTemplateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateTemplateRequest{ + CreateTemplateRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/createTemplate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/compute/create_template_from_blank.go b/pkg/cloudapi/compute/create_template_from_blank.go new file mode 100644 index 0000000..d29788b --- /dev/null +++ b/pkg/cloudapi/compute/create_template_from_blank.go @@ -0,0 +1,112 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateTemplateFromBlankRequest struct to create template from boot disk of current compute +type CreateTemplateFromBlankRequest struct { + // ID of the compute to create template from + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the rescue disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Boot type of image BIOS or UEFI + // Required: true + BootType string `url:"boottype" json:"boottype" validate:"imageBootType"` + + // Image type linux, windows or other + // Required: true + ImageType string `url:"imagetype" json:"imagetype" validate:"imageType"` + + // Username for the image + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Password for the image + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // Account ID to make the image exclusive + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // SEP ID + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool for image create + // Required: false + PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // Does this machine supports hot resize + // Default: false + // Required: false + HotResize bool `url:"hotresize" json:"hotresize"` +} + +type wrapperCreateTemplateFromBlankRequest struct { + CreateTemplateFromBlankRequest + AsyncMode bool `url:"asyncMode"` +} + +// CreateTemplateFromBlank creates template from boot disk of current compute in sync mode. +// It returns id of created compute and error. +func (c Compute) CreateTemplateFromBlank(ctx context.Context, req CreateTemplateFromBlankRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateTemplateFromBlankRequest{ + CreateTemplateFromBlankRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/createTemplateFromBlank" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} + +// CreateTemplateFromBlankAsync creates template from boot disk of current compute in async mode. +// It returns guid of task and error. +func (c Compute) CreateTemplateFromBlankAsync(ctx context.Context, req CreateTemplateFromBlankRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateTemplateFromBlankRequest{ + CreateTemplateFromBlankRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/createTemplateFromBlank" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/compute/delete.go b/pkg/cloudapi/compute/delete.go new file mode 100644 index 0000000..5d5e67a --- /dev/null +++ b/pkg/cloudapi/compute/delete.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete compute +type DeleteRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Delete permanently + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` + + // Set True if you want to detach data disks (if any) from the compute before its deletion + // Required: false + DetachDisks bool `url:"detachDisks,omitempty" json:"detachDisks,omitempty"` +} + +// Delete deletes compute +func (c Compute) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/delete" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/delete_custom_fields.go b/pkg/cloudapi/compute/delete_custom_fields.go new file mode 100644 index 0000000..2666285 --- /dev/null +++ b/pkg/cloudapi/compute/delete_custom_fields.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteCustomFieldsRequest struct to delete compute's custom fields +type DeleteCustomFieldsRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// DeleteCustomFields deletes computes custom fields +func (c Compute) DeleteCustomFields(ctx context.Context, req DeleteCustomFieldsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/deleteCustomFields" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/detach_gpu.go b/pkg/cloudapi/compute/detach_gpu.go new file mode 100644 index 0000000..4006c4d --- /dev/null +++ b/pkg/cloudapi/compute/detach_gpu.go @@ -0,0 +1,43 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DetachGPURequest struct to detach vgpu for compute +type DetachGPURequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Identifier virtual GPU + // Required: false + VGPUID int64 `url:"vgpuId,omitempty" json:"vgpuId,omitempty"` +} + +// DetachGPU detach vgpu for compute. +// If param vgpuid is equivalent -1, then detach all vgpu for compute +func (c Compute) DetachGPU(ctx context.Context, req DetachGPURequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/detachGpu" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/detach_pci_device.go b/pkg/cloudapi/compute/detach_pci_device.go new file mode 100644 index 0000000..4fc6dc0 --- /dev/null +++ b/pkg/cloudapi/compute/detach_pci_device.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DetachPCIDeviceRequest struct to detach PCI device +type DetachPCIDeviceRequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Pci device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` +} + +// DetachPCIDevice detach PCI device +func (c Compute) DetachPCIDevice(ctx context.Context, req DetachPCIDeviceRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/detachPciDevice" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disable.go b/pkg/cloudapi/compute/disable.go new file mode 100644 index 0000000..50db29a --- /dev/null +++ b/pkg/cloudapi/compute/disable.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable compute +type DisableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Disable disables compute +func (c Compute) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/disable" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disk_add.go b/pkg/cloudapi/compute/disk_add.go new file mode 100644 index 0000000..6f5c3fe --- /dev/null +++ b/pkg/cloudapi/compute/disk_add.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskAddRequest struct to create and attach disk to compute +type DiskAddRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name for disk + // Required: true + DiskName string `url:"diskName" json:"diskName" validate:"required"` + + // Disk size in GB + // Required: true + Size uint64 `url:"size" json:"size" validate:"required"` + + // Type of the disk + // Should be one of: + // - D + // - B + // Required: false + DiskType string `url:"diskType,omitempty" json:"diskType,omitempty" validate:"omitempty,computeDiskType"` + + // Storage endpoint provider ID + // By default the same with boot disk + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool name + // By default will be chosen automatically + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Optional description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Specify image id for create disk from template + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` +} + +// DiskAdd creates new disk and attach to compute +func (c Compute) DiskAdd(ctx context.Context, req DiskAddRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/diskAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disk_attach.go b/pkg/cloudapi/compute/disk_attach.go new file mode 100644 index 0000000..74766b6 --- /dev/null +++ b/pkg/cloudapi/compute/disk_attach.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskAttachRequest struct to attach disk to compute +type DiskAttachRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to attach + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Type of the disk B;D + // Required: false + DiskType string `url:"diskType,omitempty" json:"diskType,omitempty" validate:"omitempty,computeDiskType"` +} + +// DiskAttach attach disk to compute +func (c Compute) DiskAttach(ctx context.Context, req DiskAttachRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/diskAttach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disk_del.go b/pkg/cloudapi/compute/disk_del.go new file mode 100644 index 0000000..a0793c7 --- /dev/null +++ b/pkg/cloudapi/compute/disk_del.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskDelRequest struct to detach and delete disk from compute +type DiskDelRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of disk instance + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // False if disk is to be deleted to recycle bin + // Required: true + Permanently bool `url:"permanently" json:"permanently"` +} + +// DiskDel delete disk and detach from compute +func (c Compute) DiskDel(ctx context.Context, req DiskDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/diskDel" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disk_detach.go b/pkg/cloudapi/compute/disk_detach.go new file mode 100644 index 0000000..410a5df --- /dev/null +++ b/pkg/cloudapi/compute/disk_detach.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskDetachRequest struct to detach disk from compute +type DiskDetachRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to detach + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// DiskDetach detach disk from compute +func (c Compute) DiskDetach(ctx context.Context, req DiskDetachRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/diskDetach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disk_migrate.go b/pkg/cloudapi/compute/disk_migrate.go new file mode 100644 index 0000000..b2ef322 --- /dev/null +++ b/pkg/cloudapi/compute/disk_migrate.go @@ -0,0 +1,53 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskMigrateRequest struct to migrate compute's disk to target disk +type DiskMigrateRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID source disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID target disk + // Required: true + TargetDiskID uint64 `url:"targetDiskId" json:"targetDiskId" validate:"required"` + + // Migration mode. 1 - Data migration and domain update were already completed by third-party software. + // Use this if target disk already connected to compute and you only need to save changes for next reboot. + // Required: false + Mode int64 `url:"mode,omitempty" json:"mode,omitempty"` +} + +// DiskMigrate - migrate compute's disk to target disk. Source disk will be detached, target disk will be attached to the same PCI slot. +// (WARNING) Current realisation is limited. No actual data migration will be performed. +// Use this API if target disk already connected to compute and you only need to save changes for next reboot (mode: 1). +func (c Compute) DiskMigrate(ctx context.Context, req DiskMigrateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/diskMigrate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disk_qos.go b/pkg/cloudapi/compute/disk_qos.go new file mode 100644 index 0000000..79b609e --- /dev/null +++ b/pkg/cloudapi/compute/disk_qos.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskQOSRequest struct to change QoS of the disk +type DiskQOSRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to apply limits + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Limit IO for a certain disk total and read/write options are not allowed to be combined + // Required: true + Limits string `url:"limits" json:"limits" validate:"required"` +} + +// DiskQOS change QoS of the disk +func (c Compute) DiskQOS(ctx context.Context, req DiskQOSRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/diskQos" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disk_resize.go b/pkg/cloudapi/compute/disk_resize.go new file mode 100644 index 0000000..769e48a --- /dev/null +++ b/pkg/cloudapi/compute/disk_resize.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskResizeRequest struct to change disk size +type DiskResizeRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to resize + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // New disk size + // Required: true + Size uint64 `url:"size" json:"size" validate:"required"` +} + +// DiskResize change disk size +func (c Compute) DiskResize(ctx context.Context, req DiskResizeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/diskResize" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/disk_switch_to_replication.go b/pkg/cloudapi/compute/disk_switch_to_replication.go new file mode 100644 index 0000000..9355f47 --- /dev/null +++ b/pkg/cloudapi/compute/disk_switch_to_replication.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskSwitchToReplicationRequest struct to switch disk to it's replication +type DiskSwitchToReplicationRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to switch + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Delete replication relationship + // Required: false + StopReplication bool `url:"stopReplication" json:"stopReplication"` +} + +// DiskSwitchToReplication switches disk to it's replication +func (c Compute) DiskSwitchToReplication(ctx context.Context, req DiskSwitchToReplicationRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/diskSwitchToReplication" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/enable.go b/pkg/cloudapi/compute/enable.go new file mode 100644 index 0000000..f4db82f --- /dev/null +++ b/pkg/cloudapi/compute/enable.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable compute +type EnableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Enable enables compute +func (c Compute) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/enable" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/filter.go b/pkg/cloudapi/compute/filter.go new file mode 100644 index 0000000..6dec2c4 --- /dev/null +++ b/pkg/cloudapi/compute/filter.go @@ -0,0 +1,170 @@ +package compute + +import ( + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/k8s" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/lb" +) + +// FilterByID returns ListComputes with specified ID. +func (lc ListComputes) FilterByID(id uint64) ListComputes { + predicate := func(ic ItemCompute) bool { + return ic.ID == id + } + + return lc.FilterFunc(predicate) +} + +// FilterByName returns ListComputes with specified Name. +func (lc ListComputes) FilterByName(name string) ListComputes { + predicate := func(ic ItemCompute) bool { + return ic.Name == name + } + + return lc.FilterFunc(predicate) +} + +// FilterByStatus returns ListComputes with specified Status. +func (lc ListComputes) FilterByStatus(status string) ListComputes { + predicate := func(ic ItemCompute) bool { + return ic.Status == status + } + + return lc.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListComputes with specified TechStatus. +func (lc ListComputes) FilterByTechStatus(techStatus string) ListComputes { + predicate := func(ic ItemCompute) bool { + return ic.TechStatus == techStatus + } + + return lc.FilterFunc(predicate) +} + +// FilterByDiskID returns ListComputes with specified DiskID. +func (lc ListComputes) FilterByDiskID(diskID uint64) ListComputes { + predicate := func(ic ItemCompute) bool { + for _, disk := range ic.Disks { + if disk.ID == diskID { + return true + } + } + return false + } + + return lc.FilterFunc(predicate) +} + +// FilterByK8SID returns master and worker nodes (ListComputes) inside specified K8S cluster. +func (lc ListComputes) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListComputes, error) { + caller := k8s.New(decortClient) + + req := k8s.GetRequest{ + K8SID: k8sID, + } + + cluster, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + predicate := func(ic ItemCompute) bool { + for _, info := range cluster.K8SGroups.Masters.DetailedInfo { + if info.ID == ic.ID { + return true + } + } + + for _, worker := range cluster.K8SGroups.Workers { + for _, info := range worker.DetailedInfo { + if info.ID == ic.ID { + return true + } + } + } + + return false + } + + res := lc.FilterFunc(predicate) + + return &res, nil +} + +// K8SMasters is used to filter master nodes. Best used after FilterByK8SID function. +func (lc ListComputes) FilterByK8SMasters() ListComputes { + predicate := func(ic ItemCompute) bool { + for _, rule := range ic.AntiAffinityRules { + if rule.Value == "master" { + return true + } + } + return false + } + + return lc.FilterFunc(predicate) +} + +// K8SMasters is used to filter worker nodes. Best used after FilterByK8SID function. +func (lc ListComputes) FilterByK8SWorkers() ListComputes { + predicate := func(ic ItemCompute) bool { + for _, rule := range ic.AntiAffinityRules { + if rule.Value == "worker" { + return true + } + } + return false + } + + return lc.FilterFunc(predicate) +} + +// FilterByLBID returns ListComputes used by specified Load Balancer. +func (lc ListComputes) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (*ListComputes, error) { + caller := lb.New(decortClient) + + req := lb.GetRequest{ + LBID: lbID, + } + + foundLB, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + predicate := func(ic ItemCompute) bool { + return ic.ID == foundLB.PrimaryNode.ComputeID || ic.ID == foundLB.SecondaryNode.ComputeID + } + + res := lc.FilterFunc(predicate) + + return &res, nil +} + +// FilterFunc allows filtering ListComputes based on a user-specified predicate. +func (lc ListComputes) FilterFunc(predicate func(ItemCompute) bool) ListComputes { + var result ListComputes + + for _, item := range lc.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemCompute +// If none was found, returns an empty struct. +func (lc ListComputes) FindOne() ItemCompute { + if len(lc.Data) == 0 { + return ItemCompute{} + } + + return lc.Data[0] +} diff --git a/pkg/cloudapi/compute/filter_test.go b/pkg/cloudapi/compute/filter_test.go new file mode 100644 index 0000000..7f2c1fe --- /dev/null +++ b/pkg/cloudapi/compute/filter_test.go @@ -0,0 +1,244 @@ +package compute + +import "testing" + +var computes = ListComputes{ + Data: []ItemCompute{ + { + ACL: ListACL{}, + AccountID: 132847, + AccountName: "std_2", + AffinityLabel: "", + AffinityRules: []ItemRule{ + { + GUID: "", + Key: "aff_key", + Mode: "ANY", + Policy: "RECOMMENDED", + Topology: "compute", + Value: "aff_val", + }, + }, + AffinityWeight: 0, + AntiAffinityRules: []ItemRule{ + { + GUID: "", + Key: "antiaff_key", + Mode: "ANY", + Policy: "RECOMMENDED", + Topology: "compute", + Value: "antiaff_val", + }, + }, + Architecture: "X86_64", + BootOrder: []string{ + "hd", "cdrom", + }, + BootDiskSize: 0, + CloneReference: 0, + Clones: []uint64{}, + ComputeCIID: 0, + CPU: 4, + CreatedBy: "timofey_tkachev_1@decs3o", + CreatedTime: 1676975175, + CustomFields: map[string]interface{}{}, + DeletedBy: "", + DeletedTime: 0, + Description: "", + Devices: nil, + Disks: []InfoDisk{ + { + ID: 65191, + PCISlot: 6, + }, + }, + Driver: "KVM_X86", + GID: 212, + GUID: 48500, + ID: 48500, + ImageID: 9884, + Interfaces: []ItemVNFInterface{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363500, + Name: "test", + Pinned: false, + RAM: 4096, + ReferenceID: "c7cb19ac-af4a-4067-852f-c5572949207e", + Registered: true, + ResName: "compute-48500", + RGID: 79724, + RGName: "std_broker2", + SnapSets: []ItemSnapSet{}, + StatelessSepID: 0, + StatelessSepType: "", + Status: "ENABLED", + Tags: map[string]string{}, + TechStatus: "STOPPED", + TotalDiskSize: 2, + UpdatedBy: "", + UpdatedTime: 1677058904, + UserManaged: true, + VGPUs: []uint64{}, + VINSConnected: 0, + VirtualImageID: 0, + }, + { + ACL: ListACL{}, + AccountID: 132848, + AccountName: "std_broker", + AffinityLabel: "", + AffinityRules: []ItemRule{}, + AffinityWeight: 0, + AntiAffinityRules: []ItemRule{}, + Architecture: "X86_64", + BootOrder: []string{ + "hd", "cdrom", + }, + BootDiskSize: 0, + CloneReference: 0, + Clones: []uint64{}, + ComputeCIID: 0, + CPU: 6, + CreatedBy: "timofey_tkachev_1@decs3o", + CreatedTime: 1677579436, + CustomFields: map[string]interface{}{}, + DeletedBy: "", + DeletedTime: 0, + Description: "", + Devices: nil, + Disks: []InfoDisk{ + { + ID: 65248, + PCISlot: 6, + }, + }, + Driver: "KVM_X86", + GID: 212, + GUID: 48556, + ID: 48556, + ImageID: 9884, + Interfaces: []ItemVNFInterface{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363853, + Name: "compute_2", + Pinned: false, + RAM: 4096, + ReferenceID: "a542c449-5b1c-4f90-88c5-7bb5f8ae68ff", + Registered: true, + ResName: "compute-48556", + RGID: 79727, + RGName: "sdk_negative_fields_test", + SnapSets: []ItemSnapSet{}, + StatelessSepID: 0, + StatelessSepType: "", + Status: "ENABLED", + Tags: map[string]string{}, + TechStatus: "STARTED", + TotalDiskSize: 1, + UpdatedBy: "", + UpdatedTime: 1677579436, + UserManaged: true, + VGPUs: []uint64{}, + VINSConnected: 0, + VirtualImageID: 0, + }, + }, + EntryCount: 2, +} + +func TestFilterByID(t *testing.T) { + actual := computes.FilterByID(48500).FindOne() + + if actual.ID != 48500 { + t.Fatal("expected ID 48500, found: ", actual.ID) + } + + actualEmpty := computes.FilterByID(0) + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByName(t *testing.T) { + actual := computes.FilterByName("test").FindOne() + + if actual.Name != "test" { + t.Fatal("expected compute with name 'test', found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := computes.FilterByStatus("ENABLED") + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected ENABLED status, found: ", item.Status) + } + } +} + +func TestFilterByTechStatus(t *testing.T) { + actual := computes.FilterByTechStatus("STARTED").FindOne() + + if actual.ID != 48556 { + t.Fatal("expected 48556 with STARTED techStatus, found: ", actual.ID) + } +} + +func TestFilterByDiskID(t *testing.T) { + actual := computes.FilterByDiskID(65248).FindOne() + + if actual.ID != 48556 { + t.Fatal("expected 48556 with DiskID 65248, found: ", actual.ID) + } +} + +func TestFilterFunc(t *testing.T) { + actual := computes.FilterFunc(func(ic ItemCompute) bool { + return ic.Registered == true + }) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 elements found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Registered != true { + t.Fatal("expected Registered to be true, actual: ", item.Registered) + } + } +} + +func TestSortingByCreatedTime(t *testing.T) { + actual := computes.SortByCreatedTime(false) + + if actual.Data[0].Name != "test" { + t.Fatal("expected 'test', found: ", actual.Data[0].Name) + } + + actual = computes.SortByCreatedTime(true) + if actual.Data[0].Name != "compute_2" { + t.Fatal("expected 'compute_2', found: ", actual.Data[0].Name) + } +} + +func TestSortingByCPU(t *testing.T) { + actual := computes.SortByCPU(false) + + if actual.Data[0].CPU != 4 { + t.Fatal("expected 4 CPU cores, found: ", actual.Data[0].CPU) + } + + actual = computes.SortByCPU(true) + + if actual.Data[0].CPU != 6 { + t.Fatal("expected 6 CPU cores, found: ", actual.Data[0].CPU) + } +} diff --git a/pkg/cloudapi/compute/get.go b/pkg/cloudapi/compute/get.go new file mode 100644 index 0000000..b6cf8a2 --- /dev/null +++ b/pkg/cloudapi/compute/get.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about compute +type GetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Get gets information about compute as a RecordCompute struct +func (c Compute) Get(ctx context.Context, req GetRequest) (*RecordCompute, error) { + res, err := c.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordCompute{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (c Compute) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/get" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/compute/get_audits.go b/pkg/cloudapi/compute/get_audits.go new file mode 100644 index 0000000..8be0bfa --- /dev/null +++ b/pkg/cloudapi/compute/get_audits.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetAuditsRequest struct to get compute audits +type GetAuditsRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// GetAudits gets compute audits +func (c Compute) GetAudits(ctx context.Context, req GetAuditsRequest) (ListShortAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/getAudits" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListShortAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/compute/get_console_url.go b/pkg/cloudapi/compute/get_console_url.go new file mode 100644 index 0000000..4a81a1f --- /dev/null +++ b/pkg/cloudapi/compute/get_console_url.go @@ -0,0 +1,35 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetConsoleURLRequest struct to get console URL +type GetConsoleURLRequest struct { + // ID of compute instance to get console for + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// GetConsoleURL gets computes console URL +func (c Compute) GetConsoleURL(ctx context.Context, req GetConsoleURLRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/getConsoleUrl" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/compute/get_custom_fields.go b/pkg/cloudapi/compute/get_custom_fields.go new file mode 100644 index 0000000..4ceb1f6 --- /dev/null +++ b/pkg/cloudapi/compute/get_custom_fields.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetCustomFieldsRequest struct to get Compute's customFields +type GetCustomFieldsRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// GetCustomFields gets Compute's customFields +func (c Compute) GetCustomFields(ctx context.Context, req GetCustomFieldsRequest) (interface{}, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/getCustomFields" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + var info interface{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/compute/get_log.go b/pkg/cloudapi/compute/get_log.go new file mode 100644 index 0000000..a97d00d --- /dev/null +++ b/pkg/cloudapi/compute/get_log.go @@ -0,0 +1,53 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetLogRequest struct to get compute logs +type GetLogRequest struct { + // ID of compute instance to get log for + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Path to log file + // Required: true + Path string `url:"path" json:"path" validate:"required"` +} + +// GetLog gets compute's log file by path +func (c Compute) GetLog(ctx context.Context, req GetLogRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/getLog" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} + +// GetLogGet gets compute's log file by path +func (c Compute) GetLogGet(ctx context.Context, req GetLogRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi//compute/getLog" + + res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/compute/ids.go b/pkg/cloudapi/compute/ids.go new file mode 100644 index 0000000..fa5c0f9 --- /dev/null +++ b/pkg/cloudapi/compute/ids.go @@ -0,0 +1,37 @@ +package compute + +// IDs gets array of ComputeIDs from ListComputes struct +func (lc ListComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lc.Data)) + for _, c := range lc.Data { + res = append(res, c.ID) + } + return res +} + +// IDs gets array of DiskIDs from ListInfoDisks struct +func (li ListInfoDisks) IDs() []uint64 { + res := make([]uint64, 0, len(li)) + for _, i := range li { + res = append(res, i.ID) + } + return res +} + +// IDs gets array of PFWsIDs from ListPFWs struct +func (lp ListPFWs) IDs() []uint64 { + res := make([]uint64, 0, len(lp.Data)) + for _, p := range lp.Data { + res = append(res, p.ID) + } + return res +} + +// IDs gets array of DiskIDs from ListComputeDisks struct +func (lcd ListComputeDisks) IDs() []uint64 { + res := make([]uint64, 0, len(lcd)) + for _, cd := range lcd { + res = append(res, cd.ID) + } + return res +} diff --git a/pkg/cloudapi/compute/list.go b/pkg/cloudapi/compute/list.go new file mode 100644 index 0000000..8d1f261 --- /dev/null +++ b/pkg/cloudapi/compute/list.go @@ -0,0 +1,100 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of available computes +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group name + // Required: false + RGName string `url:"rgName,omitempty" json:"rgName,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by IP address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by external network name + // Required: false + ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"` + + // Find by external network ID + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // Include deleted computes + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of the available computes. +// Filtering based on status is possible +func (c Compute) List(ctx context.Context, req ListRequest) (*ListComputes, error) { + + res, err := c.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of the available computes. +func (c Compute) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/list" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/compute/list_deleted.go b/pkg/cloudapi/compute/list_deleted.go new file mode 100644 index 0000000..fbae713 --- /dev/null +++ b/pkg/cloudapi/compute/list_deleted.go @@ -0,0 +1,84 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get deleted computes list +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group name + // Required: false + RGName string `url:"rgName,omitempty" json:"rgName,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by IP address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by external network name + // Required: false + ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"` + + // Find by external network ID + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list all deleted computes +func (c Compute) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListComputes, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/listDeleted" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/compute/list_pci_device.go b/pkg/cloudapi/compute/list_pci_device.go new file mode 100644 index 0000000..3fcfc4b --- /dev/null +++ b/pkg/cloudapi/compute/list_pci_device.go @@ -0,0 +1,69 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListPCIDeviceRequest struct to get list of PCI devices +type ListPCIDeviceRequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by device id + // Required: false + DevID uint64 `url:"devId,omitempty" json:"devId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListPCIDevice gets list PCI device +func (c Compute) ListPCIDevice(ctx context.Context, req ListPCIDeviceRequest) (*ListPCIDevices, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/listPciDevice" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListPCIDevices{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/compute/list_vgpu.go b/pkg/cloudapi/compute/list_vgpu.go new file mode 100644 index 0000000..6a2458d --- /dev/null +++ b/pkg/cloudapi/compute/list_vgpu.go @@ -0,0 +1,69 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListVGPURequest struct to get vGPU list +type ListVGPURequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Find by GPU id + // Required: false + GPUID uint64 `url:"gpuId,omitempty" json:"gpuId,omitempty"` + + // Find by type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Include deleted computes. If using field 'status', then includedeleted will be ignored + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` +} + +// ListVGPU gets list vGPU +func (c Compute) ListVGPU(ctx context.Context, req ListVGPURequest) (*ListVGPUs, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/listVGpu" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVGPUs{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/compute/models.go b/pkg/cloudapi/compute/models.go new file mode 100644 index 0000000..b51469d --- /dev/null +++ b/pkg/cloudapi/compute/models.go @@ -0,0 +1,1165 @@ +package compute + +import "strconv" + +// Access Control List +type RecordACL struct { + // Account ACL list + AccountACL ListACL `json:"accountAcl"` + + // Compute ACL list + ComputeACL ListACL `json:"computeAcl"` + + // Resource group ACL list + RGACL ListACL `json:"rgAcl"` +} + +type ListUsers struct { + // Data + Data RecordACL `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type Explicit bool + +func (e *Explicit) UnmarshalJSON(b []byte) error { + if b[0] == '"' { + b = b[1 : len(b)-1] + } + + res, err := strconv.ParseBool(string(b)) + if err != nil { + return err + } + + *e = Explicit(res) + + return nil +} + +// ACL information +type ItemACL struct { + // Explicit + Explicit Explicit `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Right + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// List ACL +type ListACL []ItemACL + +// Main information about usage snapshot +type ItemUsageSnapshot struct { + // Count + Count uint64 `json:"count,omitempty"` + + // Stored + Stored float64 `json:"stored"` + + // Label + Label string `json:"label,omitempty"` + + // Timestamp + Timestamp uint64 `json:"timestamp,omitempty"` +} + +// List of usage snapshot +type ListUsageSnapshots []ItemUsageSnapshot + +// Main information about snapshot +type ItemSnapshot struct { + // List disk ID + Disks []uint64 `json:"disks"` + + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Timestamp + Timestamp uint64 `json:"timestamp"` +} + +// List of snapshots +type ListSnapShots struct { + // Data + Data []ItemSnapshot `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about port forward +type ItemPFW struct { + // ID + ID uint64 `json:"id"` + + // Local IP + LocalIP string `json:"localIp"` + + // Local port + LocalPort uint64 `json:"localPort"` + + // Protocol + Protocol string `json:"protocol"` + + // Public port end + PublicPortEnd uint64 `json:"publicPortEnd"` + + // Public port start + PublicPortStart uint64 `json:"publicPortStart"` + + // Virtuel machine ID + VMID uint64 `json:"vmId"` +} + +// List port forwards +type ListPFWs struct { + // Data + Data []ItemPFW `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about affinity relations +type RecordAffinityRelations struct { + // Other node + OtherNode []interface{} `json:"otherNode"` + + // Other node indirect + OtherNodeIndirect []interface{} `json:"otherNodeIndirect"` + + // Other node indirect soft + OtherNodeIndirectSoft []interface{} `json:"otherNodeIndirectSoft"` + + // Other node soft + OtherNodeSoft []interface{} `json:"otherNodeSoft"` + + // Same node + SameNode []interface{} `json:"sameNode"` + + // Same node soft + SameNodeSoft []interface{} `json:"sameNodeSoft"` +} + +// Main information about attached network +type RecordNetAttach struct { + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Default GW + DefGW string `json:"defGw"` + + // Enabled + Enabled bool `json:"enabled"` + + // FLIPGroup ID + FLIPGroupID uint64 `json:"flipgroupId"` + + // GUID + GUID string `json:"guid"` + + // IP address + IPAddress string `json:"ipAddress"` + + // Listen SSH + ListenSSH bool `json:"listenSsh"` + + // MAC + MAC string `json:"mac"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network type + NetType string `json:"netType"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // QOS + QOS QOS `json:"qos"` + + // Target + Target string `json:"target"` + + // Type + Type string `json:"type"` + + // List VNF IDs + VNFs []uint64 `json:"vnfs"` + + // Maximum transmission unit + MTU uint64 `json:"mtu"` +} + +// Detailed information about audit +type ItemAudit struct { + // Call + Call string `json:"call"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List Detailed audits +type ListAudits []ItemAudit + +// Short information about audit +type ItemShortAudit struct { + // Epoch + Epoch float64 `json:"epoch"` + + // Message + Message string `json:"message"` +} + +// List short audits +type ListShortAudits []ItemShortAudit + +// Main information about rule +type ItemRule struct { + // GUID + GUID string `json:"guid"` + + // Key + Key string `json:"key"` + + // Mode + Mode string `json:"mode"` + + // Policy + Policy string `json:"policy"` + + // Topology + Topology string `json:"topology"` + + // Value + Value string `json:"value"` +} + +// List rules +type ListRules []ItemRule + +// Detailed information about compute +type RecordCompute struct { + // Access Control List + ACL RecordACL `json:"ACL"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Affinity label + AffinityLabel string `json:"affinityLabel"` + + // List affinity rules + AffinityRules ListRules `json:"affinityRules"` + + // Affinity weight + AffinityWeight uint64 `json:"affinityWeight"` + + // List anti affinity rules + AntiAffinityRules ListRules `json:"antiAffinityRules"` + + // Architecture + Architecture string `json:"arch"` + + // Boot order + BootOrder []string `json:"bootOrder"` + + // Boot disk size + BootDiskSize uint64 `json:"bootdiskSize"` + + // CD Image Id + CdImageId uint64 `json:"cdImageId"` + + // Chipset + Chipset string `json:"chipset"` + + // Clone reference + CloneReference uint64 `json:"cloneReference"` + + // List clone IDs + Clones []uint64 `json:"clones"` + + // Compute CI ID + ComputeCIID uint64 `json:"computeciId"` + + // CPU Pin + CPUPin bool `json:"cpupin"` + + // Number of cores + CPU uint64 `json:"cpus"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Custom fields items + CustomFields map[string]interface{} `json:"customFields"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Devices + Devices interface{} `json:"devices"` + + // List disks in compute + Disks ListComputeDisks `json:"disks"` + + // Driver + Driver string `json:"driver"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // HPBacked + HPBacked bool `json:"hpBacked"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Image name + ImageName string `json:"imageName"` + + // List interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Manager ID + ManagerID uint64 `json:"managerId"` + + // Manager type + ManagerType string `json:"managerType"` + + // Migration job + MigrationJob uint64 `json:"migrationjob"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // NeedReboot + NeedReboot bool `json:"needReboot"` + + // Numa Affinity + NumaAffinity string `json:"numaAffinity"` + + //NumaNodeId + NumaNodeId int64 `json:"numaNodeId"` + + // Natable VINS ID + NatableVINSID uint64 `json:"natableVinsId"` + + // Natable VINS IP + NatableVINSIP string `json:"natableVinsIp"` + + // Natable VINS Name + NatableVINSName string `json:"natableVinsName"` + + // Natable VINS network + NatableVINSNetwork string `json:"natableVinsNetwork"` + + // Natable VINS network name + NatableVINSNetworkName string `json:"natableVinsNetworkName"` + + // List OS Users + OSUsers ListOSUser `json:"osUsers"` + + // Pinned or not + Pinned bool `json:"pinned"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Registered or not + Registered bool `json:"registered"` + + // Resource name + ResName string `json:"resName"` + + // Reserved Node Cpus + ReservedNodeCpus []uint64 `json:"reservedNodeCpus"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // List snapsets + SnapSets ListSnapSets `json:"snapSets"` + + // Stateless SepID + StatelessSepID int64 `json:"statelessSepId"` + + // Stateless SepType + StatelessSepType string `json:"statelessSepType"` + + // Status + Status string `json:"status"` + + // Tags + Tags map[string]string `json:"tags"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User Managed or not + UserManaged bool `json:"userManaged"` + + // Userdata + Userdata interface{} `json:"userdata"` + + // vGPU list + VGPUs []ItemVGPU `json:"vgpus"` + + // Virtual image ID + VirtualImageID uint64 `json:"virtualImageId"` + + // Virtual image name + VirtualImageName string `json:"virtualImageName"` +} + +// Information about libvirt settings +type LibvirtSettings struct { + // TX mode + TXMode string `json:"txmode"` + + // IO event + IOEventFD string `json:"ioeventfd"` + + // Event ID + EventIDx string `json:"event_idx"` + + // Number of queues + Queues uint64 `json:"queues"` + + // RX queue size + RXQueueSize uint64 `json:"rx_queue_size"` + + // TX queue size + TXQueueSize uint64 `json:"tx_queue_size"` + + // GUID + GUID string `json:"guid"` +} + +// Main information about OS user +type ItemOSUser struct { + // GUID + GUID string `json:"guid"` + + // Login + Login string `json:"login"` + + // Password + Password string `json:"password"` + + // Public key + PubKey string `json:"pubkey"` +} + +// List OS users +type ListOSUser []ItemOSUser + +// Main information about snapsets +type ItemSnapSet struct { + // List disk IDs + Disks []uint64 `json:"disks"` + + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Timestamp + Timestamp uint64 `json:"timestamp"` +} + +// List snapsets +type ListSnapSets []ItemSnapSet + +// Main information about VNF +type ItemVNFInterface struct { + // Bus number + BusNumber uint64 `json:"bus_number"` + + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Default GW + DefGW string `json:"defGw"` + + // Enabled + Enabled bool `json:"enabled"` + + // FLIPGroup ID + FLIPGroupID uint64 `json:"flipgroupId"` + + // GUID + GUID string `json:"guid"` + + // IP address + IPAddress string `json:"ipAddress"` + + // Listen SSH or not + ListenSSH bool `json:"listenSsh"` + + // Libvirt Settings + LibvirtSettings LibvirtSettings `json:"libvirtSettings"` + + // MAC + MAC string `json:"mac"` + + // Maximum transmission unit + MTU uint64 `json:"mtu"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network type + NetType string `json:"netType"` + + // NodeID + NodeID int64 `json:"nodeId"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // QOS + QOS QOS `json:"qos"` + + // Target + Target string `json:"target"` + + // Type + Type string `json:"type"` + + // List VNF IDs + VNFs []uint64 `json:"vnfs"` +} + +type QOS struct { + ERate uint64 `json:"eRate"` + GUID string `json:"guid"` + InBurst uint64 `json:"inBurst"` + InRate uint64 `json:"inRate"` +} + +// List VNF interfaces +type ListInterfaces []ItemVNFInterface + +// List compute disks +type ListComputeDisks []ItemComputeDisk + +// Main information about compute disk +type ItemComputeDisk struct { + // CKey + CKey string `json:"_ckey"` + + // Access Control List + ACL map[string]interface{} `json:"acl"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Boot partition + BootPartition uint64 `json:"bootPartition"` + + // Bus number + BusNumber uint64 `json:"bus_number"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Destruction time + DestructionTime uint64 `json:"destructionTime"` + + // Disk path + DiskPath string `json:"diskPath"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // List image IDs + Images []uint64 `json:"images"` + + // IO tune + IOTune IOTune `json:"iotune"` + + // IQN + IQN string `json:"iqn"` + + // Login + Login string `json:"login"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Order + Order uint64 `json:"order"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // Password + Passwd string `json:"passwd"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Purge time + PurgeTime uint64 `json:"purgeTime"` + + // Reality device number + RealityDeviceNumber uint64 `json:"realityDeviceNumber"` + + // Replication + Replication ItemReplication `json:"replication"` + + // Resource ID + ResID string `json:"resId"` + + // Role + Role string `json:"role"` + + // SepID + SepID uint64 `json:"sepId"` + + // Shareable + Shareable bool `json:"shareable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + //Size used + SizeUsed float64 `json:"sizeUsed"` + + // List extend snapshots + Snapshots SnapshotExtendList `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` +} + +type ItemReplication struct { + // DiskID + DiskID uint64 `json:"diskId"` + + // PoolID + PoolID string `json:"poolId"` + + // Role + Role string `json:"role"` + + // SelfVolumeID + SelfVolumeID string `json:"selfVolumeId"` + + // StorageID + StorageID string `json:"storageId"` + + // VolumeID + VolumeID string `json:"volumeId"` +} + +// Main information about snapshot extend +type SnapshotExtend struct { + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Resource ID + ResID string `json:"resId"` + + // SnapSetGUID + SnapSetGUID string `json:"snapSetGuid"` + + // SnapSetTime + SnapSetTime uint64 `json:"snapSetTime"` + + // TimeStamp + TimeStamp uint64 `json:"timestamp"` +} + +// List Snapshot Extend +type SnapshotExtendList []SnapshotExtend + +// Main information about IO tune +type IOTune struct { + // ReadBytesSec + ReadBytesSec uint64 `json:"read_bytes_sec"` + + // ReadBytesSecMax + ReadBytesSecMax uint64 `json:"read_bytes_sec_max"` + + // ReadIOPSSec + ReadIOPSSec uint64 `json:"read_iops_sec"` + + // ReadIOPSSecMax + ReadIOPSSecMax uint64 `json:"read_iops_sec_max"` + + // SizeIOPSSec + SizeIOPSSec uint64 `json:"size_iops_sec"` + + // TotalBytesSec + TotalBytesSec uint64 `json:"total_bytes_sec"` + + // TotalBytesSecMax + TotalBytesSecMax uint64 `json:"total_bytes_sec_max"` + + // TotalIOPSSec + TotalIOPSSec uint64 `json:"total_iops_sec"` + + // TotalIOPSSecMax + TotalIOPSSecMax uint64 `json:"total_iops_sec_max"` + + // WriteBytesSec + WriteBytesSec uint64 `json:"write_bytes_sec"` + + // WriteBytesSecMax + WriteBytesSecMax uint64 `json:"write_bytes_sec_max"` + + // WriteIOPSSec + WriteIOPSSec uint64 `json:"write_iops_sec"` + + // WriteIOPSSecMax + WriteIOPSSecMax uint64 `json:"write_iops_sec_max"` +} + +// Main information about compute +type ItemCompute struct { + // Access Control List + ACL ListACL `json:"acl"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Affinity label + AffinityLabel string `json:"affinityLabel"` + + // List affinity rules + AffinityRules ListRules `json:"affinityRules"` + + // Affinity weight + AffinityWeight uint64 `json:"affinityWeight"` + + // List anti affinity rules + AntiAffinityRules ListRules `json:"antiAffinityRules"` + + // Architecture + Architecture string `json:"arch"` + + // Boot order + BootOrder []string `json:"bootOrder"` + + // Boot disk size + BootDiskSize uint64 `json:"bootdiskSize"` + + // CD Image Id + CdImageId uint64 `json:"cdImageId"` + + // Chipset + Chipset string `json:"chipset"` + + // Clone reference + CloneReference uint64 `json:"cloneReference"` + + // List clone IDs + Clones []uint64 `json:"clones"` + + // Compute CI ID + ComputeCIID uint64 `json:"computeciId"` + + // CPU Pin + CPUPin bool `json:"cpupin"` + + // Number of cores + CPU uint64 `json:"cpus"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Custom fields list + CustomFields map[string]interface{} `json:"customFields"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Devices + Devices interface{} `json:"devices"` + + // List disk items + Disks ListInfoDisks `json:"disks"` + + // Driver + Driver string `json:"driver"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // HPBacked + HPBacked bool `json:"hpBacked"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // List interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Manager ID + ManagerID uint64 `json:"managerId"` + + // Manager type + ManagerType string `json:"managerType"` + + // Migration job + MigrationJob uint64 `json:"migrationjob"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // NeedReboot + NeedReboot bool `json:"needReboot"` + + // Numa Affinity + NumaAffinity string `json:"numaAffinity"` + + //NumaNodeId + NumaNodeId int64 `json:"numaNodeId"` + + // Pinned or not + Pinned bool `json:"pinned"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Registered + Registered bool `json:"registered"` + + // Resource name + ResName string `json:"resName"` + + // Reserved Node Cpus + ReservedNodeCpus []uint64 `json:"reservedNodeCpus"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // List snapsets + SnapSets ListSnapSets `json:"snapSets"` + + // Stateless SepID + StatelessSepID int64 `json:"statelessSepId"` + + // Stateless SepType + StatelessSepType string `json:"statelessSepType"` + + // Status + Status string `json:"status"` + + // Tags + Tags map[string]string `json:"tags"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Total disk size + TotalDiskSize uint64 `json:"totalDisksSize"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User Managed or not + UserManaged bool `json:"userManaged"` + + // List vGPU IDs + VGPUs []uint64 `json:"vgpus"` + + // VINS connected + VINSConnected uint64 `json:"vinsConnected"` + + // Virtual image ID + VirtualImageID uint64 `json:"virtualImageId"` +} + +// ListInfoDisks +type ListInfoDisks []InfoDisk + +// Information Disk +type InfoDisk struct { + // ID + ID uint64 `json:"id"` + + // PCISlot + PCISlot int64 `json:"pciSlot"` + + // Bus number + BusNumber uint64 `json:"bus_number"` +} + +// List information about computes +type ListComputes struct { + // Data + Data []ItemCompute `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// List VGPUs +type ListVGPUs struct { + // Data + Data []ItemVGPU `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about vgpu device +type ItemVGPU struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Created Time + CreatedTime uint64 `json:"createdTime"` + + // Deleted Time + DeletedTime uint64 `json:"deletedTime"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Last Claimed By + LastClaimedBy uint64 `json:"lastClaimedBy"` + + // Last Update Time + LastUpdateTime uint64 `json:"lastUpdateTime"` + + // Mode + Mode string `json:"mode"` + + // PCI Slot + PCISlot uint64 `json:"pciSlot"` + + // PGPUID + PGPUID uint64 `json:"pgpuid"` + + // Profile ID + ProfileID uint64 `json:"profileId"` + + // RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // RG ID + RGID uint64 `json:"rgId"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // VM ID + VMID uint64 `json:"vmid"` +} + +// Main information about PCI device +type ItemPCIDevice struct { + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Description + Description string `json:"description"` + + // GUID + GUID uint64 `json:"guid"` + + // HwPath + HwPath string `json:"hwPath"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // Status + Status string `json:"status"` + + // System name + SystemName string `json:"systemName"` +} + +// List PCI devices +type ListPCIDevices struct { + // Data + Data []ItemPCIDevice `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/compute/move_to_rg.go b/pkg/cloudapi/compute/move_to_rg.go new file mode 100644 index 0000000..0cb5e40 --- /dev/null +++ b/pkg/cloudapi/compute/move_to_rg.go @@ -0,0 +1,57 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MoveToRGRequest struct to move compute to new resource group +type MoveToRGRequest struct { + // ID of the compute instance to move + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the target resource group + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // New name for the compute upon successful move, + // if name change required. + // Pass empty string if no name change necessary + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Should the compute be restarted upon successful move + // Required: false + Autostart bool `url:"autostart,omitempty" json:"autostart,omitempty"` + + // By default moving compute in a running state is not allowed. + // Set this flag to True to force stop running compute instance prior to move. + // Required: false + ForceStop bool `url:"forceStop,omitempty" json:"forceStop,omitempty"` +} + +// MoveToRG moves compute instance to new resource group +func (c Compute) MoveToRG(ctx context.Context, req MoveToRGRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/moveToRg" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/net_attach.go b/pkg/cloudapi/compute/net_attach.go new file mode 100644 index 0000000..d02d5ef --- /dev/null +++ b/pkg/cloudapi/compute/net_attach.go @@ -0,0 +1,62 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NetAttachRequest struct to attach network +type NetAttachRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Network type + // 'EXTNET' for connect to external network directly + // 'VINS' for connect to ViNS + // 'VFNIC' for connect to vfpool + // 'DPDK' for connect to DPDK + // Required: true + NetType string `url:"netType" json:"netType" validate:"computex86NetType"` + + // Network ID for connect to + // For EXTNET - external network ID + // For VINS - VINS ID + // Required: true + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // Directly required IP address for new network interface + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // Used only for DPDK type, must be 1-9216 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` +} + +// NetAttach attaches network to compute and gets info about network +func (c Compute) NetAttach(ctx context.Context, req NetAttachRequest) (*RecordNetAttach, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/netAttach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordNetAttach{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/compute/net_detach.go b/pkg/cloudapi/compute/net_detach.go new file mode 100644 index 0000000..29af344 --- /dev/null +++ b/pkg/cloudapi/compute/net_detach.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NetDetachRequest struct to detach network from compute +type NetDetachRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // IP of the network interface + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // MAC of the network interface + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` +} + +// NetDetach detaches network from compute +func (c Compute) NetDetach(ctx context.Context, req NetDetachRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/netDetach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/pause.go b/pkg/cloudapi/compute/pause.go new file mode 100644 index 0000000..6634bf3 --- /dev/null +++ b/pkg/cloudapi/compute/pause.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PauseRequest struct to pause compute +type PauseRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Pause pause compute +func (c Compute) Pause(ctx context.Context, req PauseRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/pause" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/pfw_add.go b/pkg/cloudapi/compute/pfw_add.go new file mode 100644 index 0000000..ee01c39 --- /dev/null +++ b/pkg/cloudapi/compute/pfw_add.go @@ -0,0 +1,56 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PFWAddRequest struct to add port forward rule +type PFWAddRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // External start port number for the rule + // Required: true + PublicPortStart uint64 `url:"publicPortStart" json:"publicPortStart" validate:"required"` + + // End port number (inclusive) for the ranged rule + // Default value: -1 + // Required: false + PublicPortEnd int64 `url:"publicPortEnd,omitempty" json:"publicPortEnd,omitempty"` + + // Internal base port number + // Required: false + LocalBasePort uint64 `url:"localBasePort,omitempty" json:"localBasePort,omitempty"` + + // Network protocol + // either "tcp" or "udp" + // Required: true + Proto string `url:"proto" json:"proto" validate:"proto"` +} + +// PFWAdd add port forward rule +func (c Compute) PFWAdd(ctx context.Context, req PFWAddRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/pfwAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/pfw_del.go b/pkg/cloudapi/compute/pfw_del.go new file mode 100644 index 0000000..0226a15 --- /dev/null +++ b/pkg/cloudapi/compute/pfw_del.go @@ -0,0 +1,59 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PFWDelRequest struct to delete port forward rule +type PFWDelRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the rule to delete. If specified, all other arguments will be ignored + // Required: false + PFWID uint64 `url:"ruleId,omitempty" json:"ruleId,omitempty"` + + // External start port number for the rule + // Required: false + PublicPortStart uint64 `url:"publicPortStart,omitempty" json:"publicPortStart,omitempty"` + + // End port number (inclusive) for the ranged rule + // Required: false + PublicPortEnd uint64 `url:"publicPortEnd,omitempty" json:"publicPortEnd,omitempty"` + + // Internal base port number + // Required: false + LocalBasePort uint64 `url:"localBasePort,omitempty" json:"localBasePort,omitempty"` + + // Network protocol + // either "tcp" or "udp" + // Required: false + Proto string `url:"proto,omitempty" json:"proto,omitempty"` +} + +// PFWDel deletes port forward rule +func (c Compute) PFWDel(ctx context.Context, req PFWDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/pfwDel" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/pfw_list.go b/pkg/cloudapi/compute/pfw_list.go new file mode 100644 index 0000000..bb2191e --- /dev/null +++ b/pkg/cloudapi/compute/pfw_list.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PFWListRequest struct to get list of port forwards +type PFWListRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// PFWList gets compute port forwards list +func (c Compute) PFWList(ctx context.Context, req PFWListRequest) (*ListPFWs, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/pfwList" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListPFWs{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/compute/pin_to_stack.go b/pkg/cloudapi/compute/pin_to_stack.go new file mode 100644 index 0000000..50f824e --- /dev/null +++ b/pkg/cloudapi/compute/pin_to_stack.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PinToStackRequest struct to pin compute to stack +type PinToStackRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// PinToStack pin compute to current stack +func (c Compute) PinToStack(ctx context.Context, req PinToStackRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/pinToStack" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/power_cycle.go b/pkg/cloudapi/compute/power_cycle.go new file mode 100644 index 0000000..c78d138 --- /dev/null +++ b/pkg/cloudapi/compute/power_cycle.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PowerCycleRequest struct to force stop and start compute +type PowerCycleRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// PowerCycle makes force stop and start compute +func (c Compute) PowerCycle(ctx context.Context, req PowerCycleRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/powerCycle" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/reboot.go b/pkg/cloudapi/compute/reboot.go new file mode 100644 index 0000000..009c2f3 --- /dev/null +++ b/pkg/cloudapi/compute/reboot.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RebootRequest struct to reboot compute +type RebootRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Reboot reboots compute +func (c Compute) Reboot(ctx context.Context, req RebootRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/reboot" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/redeploy.go b/pkg/cloudapi/compute/redeploy.go new file mode 100644 index 0000000..0a5959c --- /dev/null +++ b/pkg/cloudapi/compute/redeploy.go @@ -0,0 +1,59 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RedeployRequest struct to redeploy +type RedeployRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the new OS image, if image change is required + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` + + // new size for the boot disk in GB, if boot disk size change is required + // Required: false + DiskSize uint64 `url:"diskSize,omitempty" json:"diskSize,omitempty"` + + // How to handle data disks connected to this compute instance, + // KEEP, DETACH, DESTROY + // Required: false + DataDisks string `url:"dataDisks,omitempty" json:"dataDisks,omitempty"` + + // Should compute be restarted upon successful redeploy + // Required: false + AutoStart bool `url:"autoStart,omitempty" json:"autoStart,omitempty"` + + // Set this flag to True to force stop running compute instance and redeploy next + // Required: false + ForceStop bool `url:"forceStop,omitempty" json:"forceStop,omitempty"` +} + +// Redeploy redeploys compute +func (c Compute) Redeploy(ctx context.Context, req RedeployRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/redeploy" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/reset.go b/pkg/cloudapi/compute/reset.go new file mode 100644 index 0000000..9a13ca2 --- /dev/null +++ b/pkg/cloudapi/compute/reset.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ResetRequest struct to reset compute +type ResetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Reset resets compute +func (c Compute) Reset(ctx context.Context, req ResetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/reset" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/resize.go b/pkg/cloudapi/compute/resize.go new file mode 100644 index 0000000..9ae2709 --- /dev/null +++ b/pkg/cloudapi/compute/resize.go @@ -0,0 +1,62 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ResizeRequest struct to resize compute +type ResizeRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // New CPU count. + // Pass 0 if no change to CPU count is required + // Required: false + CPU uint64 `url:"cpu,omitempty" json:"cpu,omitempty"` + + // New RAM volume in MB. + // Pass 0 if no change to RAM volume is required + // Required: false + RAM uint64 `url:"ram,omitempty" json:"ram,omitempty"` + + // Force compute resize + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// GetRAM returns RAM field values +func (r ResizeRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +// Resize resizes compute instance +func (c Compute) Resize(ctx context.Context, req ResizeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/resize" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/restore.go b/pkg/cloudapi/compute/restore.go new file mode 100644 index 0000000..0a87826 --- /dev/null +++ b/pkg/cloudapi/compute/restore.go @@ -0,0 +1,32 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore compute +type RestoreRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Restore restores compute from recycle bin +func (c Compute) Restore(ctx context.Context, req RestoreRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/restore" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/compute/resume.go b/pkg/cloudapi/compute/resume.go new file mode 100644 index 0000000..1781c68 --- /dev/null +++ b/pkg/cloudapi/compute/resume.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ResumeRequest struct to resume compute +type ResumeRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Resume resume Compute from paused state +func (c Compute) Resume(ctx context.Context, req ResumeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/resume" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/serialize.go b/pkg/cloudapi/compute/serialize.go new file mode 100644 index 0000000..34e848a --- /dev/null +++ b/pkg/cloudapi/compute/serialize.go @@ -0,0 +1,43 @@ +package compute + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lc ListComputes) Serialize(params ...string) (serialization.Serialized, error) { + if lc.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lc, prefix, indent) + } + + return json.Marshal(lc) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ic ItemCompute) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ic, prefix, indent) + } + + return json.Marshal(ic) +} diff --git a/pkg/cloudapi/compute/set_custom_fields.go b/pkg/cloudapi/compute/set_custom_fields.go new file mode 100644 index 0000000..320c61f --- /dev/null +++ b/pkg/cloudapi/compute/set_custom_fields.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCustomFieldsRequest struct to set customFields values for the Compute +type SetCustomFieldsRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Custom fields for Compute. Must be dict. + // Required: true + CustomFields string `url:"customFields" json:"customFields" validate:"required"` +} + +// SetCustomFields sets customFields values for the Compute +func (c Compute) SetCustomFields(ctx context.Context, req SetCustomFieldsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/setCustomFields" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/snapshot_create.go b/pkg/cloudapi/compute/snapshot_create.go new file mode 100644 index 0000000..f1d69d2 --- /dev/null +++ b/pkg/cloudapi/compute/snapshot_create.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotCreateRequest struct to create snapshot +type SnapshotCreateRequest struct { + // ID of the compute instance to create snapshot for + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Text label for snapshot. + // Must be unique among this compute snapshots + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotCreate create compute snapshot +func (c Compute) SnapshotCreate(ctx context.Context, req SnapshotCreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/snapshotCreate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/compute/snapshot_delete.go b/pkg/cloudapi/compute/snapshot_delete.go new file mode 100644 index 0000000..7f2b1e9 --- /dev/null +++ b/pkg/cloudapi/compute/snapshot_delete.go @@ -0,0 +1,69 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotDeleteRequest struct to delete snapshot +type SnapshotDeleteRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Text label of snapshot to delete + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +type wrapperSnapshotDeleteRequeststruct struct { + SnapshotDeleteRequest + + AsyncMode bool `url:"asyncMode"` +} + +// SnapshotDelete deletes specified compute snapshot +func (c Compute) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSnapshotDeleteRequeststruct{ + SnapshotDeleteRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/snapshotDelete" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} + +// SnapshotDeleteAsync deletes specified compute snapshot +func (c Compute) SnapshotDeleteAsync(ctx context.Context, req SnapshotDeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSnapshotDeleteRequeststruct{ + SnapshotDeleteRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/snapshotDelete" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/compute/snapshot_list.go b/pkg/cloudapi/compute/snapshot_list.go new file mode 100644 index 0000000..21a999e --- /dev/null +++ b/pkg/cloudapi/compute/snapshot_list.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotListRequest struct to get list snapshots +type SnapshotListRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// SnapshotList gets list of compute snapshots +func (c Compute) SnapshotList(ctx context.Context, req SnapshotListRequest) (*ListSnapShots, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/snapshotList" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListSnapShots{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/compute/snapshot_rollback.go b/pkg/cloudapi/compute/snapshot_rollback.go new file mode 100644 index 0000000..78134dc --- /dev/null +++ b/pkg/cloudapi/compute/snapshot_rollback.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotRollbackRequest struct for rollback +type SnapshotRollbackRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Text label of snapshot to rollback + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotRollback rollbacks specified compute snapshot +func (c Compute) SnapshotRollback(ctx context.Context, req SnapshotRollbackRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/snapshotRollback" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/snapshot_usage.go b/pkg/cloudapi/compute/snapshot_usage.go new file mode 100644 index 0000000..f3f62a1 --- /dev/null +++ b/pkg/cloudapi/compute/snapshot_usage.go @@ -0,0 +1,47 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotUsageRequest struct to get compute snapshot real size on storage +type SnapshotUsageRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Specify to show usage exact for this snapshot. + // Leave empty for get usage for all compute snapshots + // Required: false + Label string `url:"label,omitempty" json:"label,omitempty"` +} + +// SnapshotUsage gets compute snapshot real size on storage. +// Always returns list of json objects, and first json object contains summary about all related +// snapshots. +func (c Compute) SnapshotUsage(ctx context.Context, req SnapshotUsageRequest) (ListUsageSnapshots, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/snapshotUsage" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListUsageSnapshots{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/compute/sorting.go b/pkg/cloudapi/compute/sorting.go new file mode 100644 index 0000000..648ecbc --- /dev/null +++ b/pkg/cloudapi/compute/sorting.go @@ -0,0 +1,98 @@ +package compute + +import "sort" + +// SortByCPU sorts ListComputes by the CPU core amount in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByCPU(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].CPU > lc.Data[j].CPU + } + + return lc.Data[i].CPU < lc.Data[j].CPU + }) + + return lc +} + +// SortByRAM sorts ListComputes by the RAM amount in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByRAM(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].RAM > lc.Data[j].RAM + } + + return lc.Data[i].RAM < lc.Data[j].RAM + }) + + return lc +} + +// SortByCreatedTime sorts ListComputes by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByCreatedTime(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].CreatedTime > lc.Data[j].CreatedTime + } + + return lc.Data[i].CreatedTime < lc.Data[j].CreatedTime + }) + + return lc +} + +// SortByUpdatedTime sorts ListComputes by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByUpdatedTime(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].UpdatedTime > lc.Data[j].UpdatedTime + } + + return lc.Data[i].UpdatedTime < lc.Data[j].UpdatedTime + }) + + return lc +} + +// SortByDeletedTime sorts ListComputes by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByDeletedTime(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].DeletedTime > lc.Data[j].DeletedTime + } + + return lc.Data[i].DeletedTime < lc.Data[j].DeletedTime + }) + + return lc +} diff --git a/pkg/cloudapi/compute/start.go b/pkg/cloudapi/compute/start.go new file mode 100644 index 0000000..46c6807 --- /dev/null +++ b/pkg/cloudapi/compute/start.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StartRequest struct to start compute +type StartRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of CD-ROM live image to boot + // Required: false + AltBootID uint64 `url:"altBootId,omitempty" json:"altBootId,omitempty"` +} + +// Start starts compute +func (c Compute) Start(ctx context.Context, req StartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/start" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/stop.go b/pkg/cloudapi/compute/stop.go new file mode 100644 index 0000000..356859f --- /dev/null +++ b/pkg/cloudapi/compute/stop.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StopRequest struct to stop compute +type StopRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Force stop compute + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Stop stops compute +func (c Compute) Stop(ctx context.Context, req StopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/stop" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/tag_add.go b/pkg/cloudapi/compute/tag_add.go new file mode 100644 index 0000000..e0e0761 --- /dev/null +++ b/pkg/cloudapi/compute/tag_add.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// TagAddRequest struct to add tag to compute +type TagAddRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Tag key + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Tag value + // Required: true + Value string `url:"value" json:"value" validate:"required"` +} + +// TagAdd add tag to compute tags dict +func (c Compute) TagAdd(ctx context.Context, req TagAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/tagAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/tag_remove.go b/pkg/cloudapi/compute/tag_remove.go new file mode 100644 index 0000000..e6af8fb --- /dev/null +++ b/pkg/cloudapi/compute/tag_remove.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// TagRemoveRequest struct to remove tag from compute +type TagRemoveRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Tag key + // Required: true + Key string `url:"key" json:"key" validate:"required"` +} + +// TagRemove removes tag from compute tags dict +func (c Compute) TagRemove(ctx context.Context, req TagRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/tagRemove" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/unpin_from_stack.go b/pkg/cloudapi/compute/unpin_from_stack.go new file mode 100644 index 0000000..feb8f1f --- /dev/null +++ b/pkg/cloudapi/compute/unpin_from_stack.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UnpinFromStackRequest struct for unpin from stack +type UnpinFromStackRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// UnpinFromStack unpins compute from current stack +func (c Compute) UnpinFromStack(ctx context.Context, req UnpinFromStackRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/unpinFromStack" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/update.go b/pkg/cloudapi/compute/update.go new file mode 100644 index 0000000..744ee66 --- /dev/null +++ b/pkg/cloudapi/compute/update.go @@ -0,0 +1,68 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update compute +type UpdateRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // New name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Rule for VM placement with NUMA affinity. + // Possible values - none (placement without NUMA affinity), + // strict (strictly with NUMA affinity, if not possible - do not start VM), + // loose (use NUMA affinity if possible) + // Required: false + // Default: none + NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"` + + // Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node + // Required: false + // Default: false + CPUPin bool `url:"cpupin" json:"cpupin"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node + // Required: false + // Default: false + HPBacked bool `url:"hpBacked" json:"hpBacked"` +} + +// Update updates some properties of the compute +func (c Compute) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/update" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/user_grant.go b/pkg/cloudapi/compute/user_grant.go new file mode 100644 index 0000000..56411c5 --- /dev/null +++ b/pkg/cloudapi/compute/user_grant.go @@ -0,0 +1,50 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserGrantRequest struct to grant access to compute +type UserGrantRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the user to add + // Required: true + Username string `url:"userName" json:"userName" validate:"required"` + + // Access type + // Should be one of: + // - 'R' for Read only + // - 'RCX' for Write + // - 'ARCXDU' for Admin + // Required: true + AccessType string `url:"accesstype" json:"accesstype" validate:"accessType"` +} + +// UserGrant grants user access to the compute +func (c Compute) UserGrant(ctx context.Context, req UserGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/userGrant" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/user_list.go b/pkg/cloudapi/compute/user_list.go new file mode 100644 index 0000000..7defb6f --- /dev/null +++ b/pkg/cloudapi/compute/user_list.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserListRequest struct to get list of users for compute +type UserListRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// UserList gets users list for compute +func (c Compute) UserList(ctx context.Context, req UserListRequest) (*ListUsers, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/userList" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListUsers{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/compute/user_revoke.go b/pkg/cloudapi/compute/user_revoke.go new file mode 100644 index 0000000..36d26f1 --- /dev/null +++ b/pkg/cloudapi/compute/user_revoke.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserRevokeRequest struct to revoke user access +type UserRevokeRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the user to remove + // Required: true + Username string `url:"userName" json:"userName" validate:"required"` +} + +// UserRevoke revokes user access to the compute +func (c Compute) UserRevoke(ctx context.Context, req UserRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/userRevoke" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/user_update.go b/pkg/cloudapi/compute/user_update.go new file mode 100644 index 0000000..f13e22c --- /dev/null +++ b/pkg/cloudapi/compute/user_update.go @@ -0,0 +1,50 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserUpdateRequest struct to update user access +type UserUpdateRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the user to update + // Required: true + Username string `url:"userName" json:"userName" validate:"required"` + + // Access type + // Should be one of: + // - 'R' for Read only + // - 'RCX' for Write + // - 'ARCXDU' for Admin + // Required: true + AccessType string `url:"accesstype" json:"accesstype" validate:"accessType"` +} + +// UserUpdate updates user access to the compute +func (c Compute) UserUpdate(ctx context.Context, req UserUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/userUpdate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks.go b/pkg/cloudapi/disks.go new file mode 100644 index 0000000..b5fa588 --- /dev/null +++ b/pkg/cloudapi/disks.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/disks" +) + +// Accessing the Disks method group +func (ca *CloudAPI) Disks() *disks.Disks { + return disks.New(ca.client) +} diff --git a/pkg/cloudapi/disks/create.go b/pkg/cloudapi/disks/create.go new file mode 100644 index 0000000..328be84 --- /dev/null +++ b/pkg/cloudapi/disks/create.go @@ -0,0 +1,77 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create disk +type CreateRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // ID of the grid (platform) + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Name of disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Description of disk + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Size in GB, default is 0 + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Type of disk + // - B=Boot + // - D=Data + // - T=Temp + // Required: true + Type string `url:"type" json:"type" validate:"diskType"` + + // Size in GB default is 0 + // Required: false + SSDSize uint64 `url:"ssdSize,omitempty" json:"ssdSize,omitempty"` + + // Max IOPS disk can perform defaults to 2000 + // Required: false + IOPS uint64 `url:"iops,omitempty" json:"iops,omitempty"` + + // Storage endpoint provider ID to create disk + // Required: false + SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"` + + // Pool name to create disk + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` +} + +// Create creates a disk +func (d Disks) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/create" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/delete.go b/pkg/cloudapi/disks/delete.go new file mode 100644 index 0000000..dd2572f --- /dev/null +++ b/pkg/cloudapi/disks/delete.go @@ -0,0 +1,46 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest to delete disk +type DeleteRequest struct { + // ID of disk to delete + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Detach disk from machine first + // Required: false + Detach bool `url:"detach,omitempty" json:"detach,omitempty"` + + // Whether to completely delete the disk, works only with non attached disks + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes disk by ID +func (d Disks) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/delete" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/delete_disks.go b/pkg/cloudapi/disks/delete_disks.go new file mode 100644 index 0000000..edaf7d7 --- /dev/null +++ b/pkg/cloudapi/disks/delete_disks.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisksDeleteRequest struct for multiple disks +type DisksDeleteRequest struct { + // List of disk ids to delete + // Required: true + DisksIDs []uint64 `url:"diskIds" json:"diskIds" validate:"required"` + + // Whether to completely delete the disks, works only with non attached disks + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// DeleteDisks deletes multiple disks permanently +func (d Disks) DeleteDisks(ctx context.Context, req DisksDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/deleteDisks" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/disks.go b/pkg/cloudapi/disks/disks.go new file mode 100644 index 0000000..f025474 --- /dev/null +++ b/pkg/cloudapi/disks/disks.go @@ -0,0 +1,18 @@ +// API Actor api, this actor is the final api a enduser uses to manage his resources +package disks + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to disks +type Disks struct { + client interfaces.Caller +} + +// Builder for disks endpoints +func New(client interfaces.Caller) *Disks { + return &Disks{ + client, + } +} diff --git a/pkg/cloudapi/disks/filter.go b/pkg/cloudapi/disks/filter.go new file mode 100644 index 0000000..4dd8e4c --- /dev/null +++ b/pkg/cloudapi/disks/filter.go @@ -0,0 +1,321 @@ +package disks + +import ( + "context" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/k8s" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/lb" +) + +// FilterByID returns ListDisks with specified ID. +func (ld ListDisks) FilterByID(id uint64) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.ID == id + } + + return ld.FilterFunc(predicate) +} + +// FilterByName returns ListDisks with specified Name. +func (ld ListDisks) FilterByName(name string) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.Name == name + } + + return ld.FilterFunc(predicate) +} + +// FilterByStatus returns ListDisks with specified Status. +func (ld ListDisks) FilterByStatus(status string) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.Status == status + } + + return ld.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListDisks with specified TechStatus. +func (ld ListDisks) FilterByTechStatus(techStatus string) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.TechStatus == techStatus + } + + return ld.FilterFunc(predicate) +} + +// FilterByComputeID is used to filter ListDisks attached to specified compute. +func (ld ListDisks) FilterByComputeID(computeID uint64) ListDisks { + predicate := func(idisk ItemDisk) bool { + for k := range idisk.Computes { + if k == strconv.FormatUint(computeID, 10) { + return true + } + } + + return false + } + + return ld.FilterFunc(predicate) +} + +// FilterByK8SID is used to filter ListDisks by specified K8S cluster. +func (ld ListDisks) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListDisks, error) { + caller := k8s.New(decortClient) + + req := k8s.GetRequest{ + K8SID: k8sID, + } + + cluster, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + var result ListDisks + + for _, masterCompute := range cluster.K8SGroups.Masters.DetailedInfo { + result.Data = append(result.Data, ld.FilterByComputeID(masterCompute.ID).Data...) + } + + for _, workerGroup := range cluster.K8SGroups.Workers { + for _, workerCompute := range workerGroup.DetailedInfo { + result.Data = append(result.Data, ld.FilterByComputeID(workerCompute.ID).Data...) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return &result, nil +} + +// FilterByLBID is used to filter ListDisks used by computes inside specified Load Balancer. +func (ld ListDisks) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (*ListDisks, error) { + caller := lb.New(decortClient) + + req := lb.GetRequest{ + LBID: lbID, + } + + foundLB, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + var result ListDisks + result.Data = append(result.Data, ld.FilterByComputeID(foundLB.PrimaryNode.ComputeID).Data...) + result.Data = append(result.Data, ld.FilterByComputeID(foundLB.SecondaryNode.ComputeID).Data...) + + result.EntryCount = uint64(len(result.Data)) + + return &result, nil +} + +// FilterFunc allows filtering ListDisks based on a user-specified predicate. +func (ld ListDisks) FilterFunc(predicate func(ItemDisk) bool) ListDisks { + var result ListDisks + + for _, item := range ld.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemDisk +// If none was found, returns an empty struct. +func (ld ListDisks) FindOne() ItemDisk { + if len(ld.Data) == 0 { + return ItemDisk{} + } + + return ld.Data[0] +} + +// FilterByID returns ListSearchDisks with specified ID. +func (ld ListSearchDisks) FilterByID(id uint64) ListSearchDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.ID == id + } + + return ld.FilterFunc(predicate) +} + +// FilterByName returns ListSearchDisks with specified Name. +func (ld ListSearchDisks) FilterByName(name string) ListSearchDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.Name == name + } + + return ld.FilterFunc(predicate) +} + +// FilterByStatus returns ListSearchDisks with specified Status. +func (ld ListSearchDisks) FilterByStatus(status string) ListSearchDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.Status == status + } + + return ld.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListSearchDisks with specified TechStatus. +func (ld ListSearchDisks) FilterByTechStatus(techStatus string) ListSearchDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.TechStatus == techStatus + } + + return ld.FilterFunc(predicate) +} + +// FilterByComputeID is used to filter ListSearchDisks attached to specified compute. +func (ld ListSearchDisks) FilterByComputeID(computeID uint64) ListSearchDisks { + predicate := func(idisk ItemDisk) bool { + for k := range idisk.Computes { + if k == strconv.FormatUint(computeID, 10) { + return true + } + } + + return false + } + + return ld.FilterFunc(predicate) +} + +// FilterByK8SID is used to filter ListSearchDisks by specified K8S cluster. +func (ld ListSearchDisks) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (ListSearchDisks, error) { + caller := k8s.New(decortClient) + + req := k8s.GetRequest{ + K8SID: k8sID, + } + + cluster, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + var result ListSearchDisks + + for _, masterCompute := range cluster.K8SGroups.Masters.DetailedInfo { + result = append(result, ld.FilterByComputeID(masterCompute.ID)...) + } + + for _, workerGroup := range cluster.K8SGroups.Workers { + for _, workerCompute := range workerGroup.DetailedInfo { + result = append(result, ld.FilterByComputeID(workerCompute.ID)...) + } + } + + return result, nil +} + +// FilterByLBID is used to filter ListSearchDisks used by computes inside specified Load Balancer. +func (ld ListSearchDisks) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (ListSearchDisks, error) { + caller := lb.New(decortClient) + + req := lb.GetRequest{ + LBID: lbID, + } + + foundLB, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + var result ListSearchDisks + result = append(result, ld.FilterByComputeID(foundLB.PrimaryNode.ComputeID)...) + result = append(result, ld.FilterByComputeID(foundLB.SecondaryNode.ComputeID)...) + + return result, nil +} + +// FilterFunc allows filtering ListSearchDisks based on a user-specified predicate. +func (ld ListSearchDisks) FilterFunc(predicate func(ItemDisk) bool) ListSearchDisks { + var result ListSearchDisks + + for _, item := range ld { + if predicate(item) { + result = append(result, item) + } + } + + return result +} + +// FindOne returns first found ItemDisk +// If none was found, returns an empty struct. +func (ld ListSearchDisks) FindOne() ItemDisk { + if len(ld) == 0 { + return ItemDisk{} + } + + return ld[0] +} + +// FilterByID returns ListDisksUnattached with specified ID. +func (lu ListDisksUnattached) FilterByID(id uint64) ListDisksUnattached { + predicate := func(idisk ItemDiskUnattached) bool { + return idisk.ID == id + } + + return lu.FilterFunc(predicate) +} + +// FilterByName returns ListDisksUnattached with specified Name. +func (lu ListDisksUnattached) FilterByName(name string) ListDisksUnattached { + predicate := func(idisk ItemDiskUnattached) bool { + return idisk.Name == name + } + + return lu.FilterFunc(predicate) +} + +// FilterByStatus returns ListDisksUnattached with specified Status. +func (lu ListDisksUnattached) FilterByStatus(status string) ListDisksUnattached { + predicate := func(idisk ItemDiskUnattached) bool { + return idisk.Status == status + } + + return lu.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListDisksUnattached with specified TechStatus. +func (lu ListDisksUnattached) FilterByTechStatus(techStatus string) ListDisksUnattached { + predicate := func(idisk ItemDiskUnattached) bool { + return idisk.TechStatus == techStatus + } + + return lu.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListDisksUnattached based on a user-specified predicate. +func (lu ListDisksUnattached) FilterFunc(predicate func(ItemDiskUnattached) bool) ListDisksUnattached { + var result ListDisksUnattached + + for _, item := range lu.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemDiskUnattached +// If none was found, returns an empty struct. +func (lu ListDisksUnattached) FindOne() ItemDiskUnattached { + if len(lu.Data) == 0 { + return ItemDiskUnattached{} + } + + return lu.Data[0] +} diff --git a/pkg/cloudapi/disks/filter_test.go b/pkg/cloudapi/disks/filter_test.go new file mode 100644 index 0000000..8b72929 --- /dev/null +++ b/pkg/cloudapi/disks/filter_test.go @@ -0,0 +1,556 @@ +package disks + +import ( + "testing" +) + +var techStatusAllocated = "ALLOCATED" + +var disks = ListDisks{ + Data: []ItemDisk{ + { + MachineID: 0, + MachineName: "", + DeviceName: "vda", + AccountID: 132847, + AccountName: "std_2", + ACL: map[string]interface{}{}, + Computes: map[string]string{ + "48500": "test", + }, + CreatedTime: 1676975177, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + GID: 212, + ID: 65191, + ImageID: 9884, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + Name: "bootdisk", + Order: 0, + Params: "", + ParentID: 0, + PCISlot: 6, + Pool: "vmstor", + PresentTo: []uint64{ + 27, + }, + PurgeTime: 0, + ResID: "sample", + ResName: "sample", + Role: "", + Shareable: false, + SizeMax: 2, + SizeUsed: 2, + Snapshots: []ItemSnapshot{}, + Status: "ASSIGNED", + TechStatus: techStatusAllocated, + Type: "B", + VMID: 48500, + }, + { + MachineID: 0, + MachineName: "", + DeviceName: "vda", + AccountID: 132852, + AccountName: "std", + ACL: map[string]interface{}{}, + Computes: map[string]string{ + "48502": "stdvm2", + }, + CreatedTime: 1676982606, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + GID: 212, + ID: 65193, + ImageID: 9885, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + Name: "bootdisk", + Order: 0, + Params: "", + ParentID: 0, + PCISlot: 6, + Pool: "vmstor", + PresentTo: []uint64{ + 27, + 27, + }, + PurgeTime: 0, + ResID: "sample", + ResName: "sample", + Role: "", + Shareable: false, + SizeMax: 4, + SizeUsed: 4, + Snapshots: []ItemSnapshot{}, + Status: "ASSIGNED", + TechStatus: techStatusAllocated, + Type: "B", + VMID: 48502, + }, + }, + EntryCount: 2, +} + +func TestListDisks_FilterByID(t *testing.T) { + actual := disks.FilterByID(65193) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + actualItem := actual.FindOne() + + if actualItem.ID != 65193 { + t.Fatal("expected ID 65193, found: ", actualItem.ID) + } +} + +func TestListDisks_FilterByName(t *testing.T) { + actual := disks.FilterByName("bootdisk") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 elements, found: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Name != "bootdisk" { + t.Fatal("expected 'bootdisk' name, found: ", item.Name) + } + } +} + +func TestListDisks_FilterByStatus(t *testing.T) { + actual := disks.FilterByStatus("ASSIGNED") + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual.Data { + if item.Status != "ASSIGNED" { + t.Fatal("expected 'ASSIGNED' status, found: ", item.Status) + } + } +} + +func TestListDisks_FilterByTechStatus(t *testing.T) { + actual := disks.FilterByTechStatus(techStatusAllocated) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual.Data { + if item.TechStatus != techStatusAllocated { + t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus) + } + } +} + +func TestListDisks_FilterFunc(t *testing.T) { + actual := disks.FilterFunc(func(id ItemDisk) bool { + return len(id.PresentTo) == 2 + }) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + if len(actual.Data[0].PresentTo) != 2 { + t.Fatal("expected 2 elements in PresentTo, found: ", len(actual.Data[0].PresentTo)) + } +} + +func TestListDisks_SortByCreatedTime(t *testing.T) { + actual := disks.SortByCreatedTime(false) + + if actual.Data[0].ID != 65191 { + t.Fatal("expected ID 65191, found: ", actual.Data[0].ID) + } + + actual = disks.SortByCreatedTime(true) + + if actual.Data[0].ID != 65193 { + t.Fatal("expected ID 65193, found: ", actual.Data[0].ID) + } +} + +var searchDisks = ListSearchDisks{ + ItemDisk{ + MachineID: 0, + MachineName: "", + DeviceName: "vda", + AccountID: 132847, + AccountName: "std_2", + ACL: map[string]interface{}{}, + Computes: map[string]string{ + "48500": "test", + }, + CreatedTime: 1676975177, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + GID: 212, + ID: 65191, + ImageID: 9884, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + Name: "bootdisk", + Order: 0, + Params: "", + ParentID: 0, + PCISlot: 6, + Pool: "vmstor", + PresentTo: []uint64{ + 27, + }, + PurgeTime: 0, + ResID: "sample", + ResName: "sample", + Role: "", + Shareable: false, + SizeMax: 2, + SizeUsed: 2, + Snapshots: []ItemSnapshot{}, + Status: "ASSIGNED", + TechStatus: techStatusAllocated, + Type: "B", + VMID: 48500, + }, + ItemDisk{ + MachineID: 0, + MachineName: "", + DeviceName: "vda", + AccountID: 132852, + AccountName: "std", + ACL: map[string]interface{}{}, + Computes: map[string]string{ + "48502": "stdvm2", + }, + CreatedTime: 1676982606, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + GID: 212, + ID: 65193, + ImageID: 9885, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + Name: "bootdisk", + Order: 0, + Params: "", + ParentID: 0, + PCISlot: 6, + Pool: "vmstor", + PresentTo: []uint64{ + 27, + 27, + }, + PurgeTime: 0, + ResID: "sample", + ResName: "sample", + Role: "", + Shareable: false, + SizeMax: 4, + SizeUsed: 4, + Snapshots: []ItemSnapshot{}, + Status: "ASSIGNED", + TechStatus: techStatusAllocated, + Type: "B", + VMID: 48502, + }, +} + +func TestListSearchDisks_FilterByID(t *testing.T) { + actual := searchDisks.FilterByID(65193) + + if len(actual) == 0 { + t.Fatal("No elements were found") + } + + actualItem := actual.FindOne() + + if actualItem.ID != 65193 { + t.Fatal("expected ID 65193, found: ", actualItem.ID) + } +} + +func TestListSearchDisks_FilterByName(t *testing.T) { + actual := searchDisks.FilterByName("bootdisk") + + if len(actual) != 2 { + t.Fatal("expected 2 elements, found: ", len(actual)) + } + + for _, item := range actual { + if item.Name != "bootdisk" { + t.Fatal("expected 'bootdisk' name, found: ", item.Name) + } + } +} + +func TestListSearchDisks_FilterByStatus(t *testing.T) { + actual := searchDisks.FilterByStatus("ASSIGNED") + + if len(actual) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual { + if item.Status != "ASSIGNED" { + t.Fatal("expected 'ASSIGNED' status, found: ", item.Status) + } + } +} + +func TestListSearchDisks_FilterByTechStatus(t *testing.T) { + actual := searchDisks.FilterByTechStatus(techStatusAllocated) + + if len(actual) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual { + if item.TechStatus != techStatusAllocated { + t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus) + } + } +} + +func TestListSearchDisks_FilterFunc(t *testing.T) { + actual := searchDisks.FilterFunc(func(id ItemDisk) bool { + return len(id.PresentTo) == 2 + }) + + if len(actual) == 0 { + t.Fatal("No elements were found") + } + + if len(actual[0].PresentTo) != 2 { + t.Fatal("expected 2 elements in PresentTo, found: ", len(actual[0].PresentTo)) + } +} + +func TestListSearchDisks_SortByCreatedTime(t *testing.T) { + actual := searchDisks.SortByCreatedTime(false) + + if actual[0].ID != 65191 { + t.Fatal("expected ID 65191, found: ", actual[0].ID) + } + + actual = searchDisks.SortByCreatedTime(true) + + if actual[0].ID != 65193 { + t.Fatal("expected ID 65193, found: ", actual[0].ID) + } +} + +var unattachedDisks = ListDisksUnattached{ + Data: []ItemDiskUnattached{ + { + CKey: "", + Meta: []interface{}{ + "cloudbroker", + "disk", + 1, + }, + AccountID: 149, + AccountName: "test_account1", + ACL: map[string]interface{}{}, + BootPartition: 0, + CreatedTime: 1681477547, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + DiskPath: "", + GID: 2002, + GUID: 22636, + ID: 22636, + ImageID: 0, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + IQN: "", + Login: "", + Milestones: 43834, + Name: "test_disk", + Order: 0, + Params: "", + ParentID: 0, + Password: "", + PCISlot: -1, + Pool: "data05", + PresentTo: []uint64{}, + PurgeAttempts: 0, + PurgeTime: 0, + RealityDeviceNumber: 0, + ReferenceID: "", + ResID: "79bd3bd8-3424-48d3-963f-1870d506f169", + ResName: "volumes/volume_22636", + Role: "", + SEPID: 1, + Shareable: false, + SizeMax: 0, + SizeUsed: 0, + Snapshots: nil, + Status: "CREATED", + TechStatus: techStatusAllocated, + Type: "D", + VMID: 0, + }, + { + CKey: "", + Meta: []interface{}{ + "cloudbroker", + "disk", + 1, + }, + AccountID: 150, + AccountName: "test_account", + ACL: map[string]interface{}{}, + BootPartition: 0, + CreatedTime: 1681477558, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + DiskPath: "", + GID: 2002, + GUID: 22637, + ID: 22637, + ImageID: 0, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + IQN: "", + Login: "", + Milestones: 43834, + Name: "test_disk", + Order: 0, + Params: "", + ParentID: 0, + Password: "", + PCISlot: -1, + Pool: "data05", + PresentTo: []uint64{ + 27, + 27, + }, + PurgeAttempts: 0, + PurgeTime: 0, + RealityDeviceNumber: 0, + ReferenceID: "", + ResID: "79bd3bd8-3424-48d3-963f-1870d506f169", + ResName: "volumes/volume_22637", + Role: "", + SEPID: 1, + Shareable: false, + SizeMax: 0, + SizeUsed: 0, + Snapshots: nil, + Status: "CREATED", + TechStatus: techStatusAllocated, + Type: "B", + VMID: 0, + }, + }, + EntryCount: 2, +} + +func TestListDisksUnattached_FilterByID(t *testing.T) { + actual := unattachedDisks.FilterByID(22636) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + actualItem := actual.FindOne() + + if actualItem.ID != 22636 { + t.Fatal("expected ID 22636, found: ", actualItem.ID) + } +} + +func TestListDisksUnattached_FilterByName(t *testing.T) { + actual := unattachedDisks.FilterByName("test_disk") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 elements, found: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Name != "test_disk" { + t.Fatal("expected 'test_disk' name, found: ", item.Name) + } + } +} + +func TestListDisksUnattached_FilterByStatus(t *testing.T) { + actual := unattachedDisks.FilterByStatus("CREATED") + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual.Data { + if item.Status != "CREATED" { + t.Fatal("expected 'CREATED' status, found: ", item.Status) + } + } +} + +func TestListDisksUnattached_FilterByTechStatus(t *testing.T) { + actual := unattachedDisks.FilterByTechStatus(techStatusAllocated) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual.Data { + if item.TechStatus != techStatusAllocated { + t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus) + } + } +} + +func TestListDisksUnattached_FilterFunc(t *testing.T) { + actual := unattachedDisks.FilterFunc(func(id ItemDiskUnattached) bool { + return len(id.PresentTo) == 2 + }) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + if len(actual.Data[0].PresentTo) != 2 { + t.Fatal("expected 2 elements in PresentTo, found: ", len(actual.Data[0].PresentTo)) + } +} + +func TestListDisksUnattached_SortByCreatedTime(t *testing.T) { + actual := unattachedDisks.SortByCreatedTime(false) + + if actual.Data[0].ID != 22636 { + t.Fatal("expected ID 22636, found: ", actual.Data[0].ID) + } + + actual = unattachedDisks.SortByCreatedTime(true) + + if actual.Data[0].ID != 22637 { + t.Fatal("expected ID 22637, found: ", actual.Data[0].ID) + } + +} diff --git a/pkg/cloudapi/disks/from_platform_disk.go b/pkg/cloudapi/disks/from_platform_disk.go new file mode 100644 index 0000000..baf9ec4 --- /dev/null +++ b/pkg/cloudapi/disks/from_platform_disk.go @@ -0,0 +1,126 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FromPlatformDiskRequest struct to create template from platform disk +type FromPlatformDiskRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Name of the rescue disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Boot type of image BIOS or UEFI + // Required: true + BootType string `url:"boottype" json:"boottype" validate:"imageBootType"` + + // Image type linux, windows or other + // Required: true + ImageType string `url:"imagetype" json:"imagetype" validate:"imageType"` + + // Binary architecture of this image + // Should be: + // - X86_64 + // Required: true + Architecture string `url:"architecture" json:"architecture" validate:"imageArchitecture"` + + // Username for the image + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Password for the image + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // Account ID to make the image exclusive + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // SEP ID + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool for image create + // Required: false + PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // List of types of compute suitable for image + // Example: [ "KVM_X86" ] + // Required: true + Drivers []string `url:"drivers" json:"drivers" validate:"required"` + + // Does this machine supports hot resize + // Required: false + HotResize bool `url:"hotresize" json:"hotresize"` + + // Bootable image + // Required: true + Bootable bool `url:"bootable" json:"bootable"` +} + +type wrapperFromPlatformDiskRequest struct { + FromPlatformDiskRequest + AsyncMode bool `url:"asyncMode"` +} + +// FromPlatformDisk creates template from platform disk in sync mode. +// It returns id of created disk and error. +func (d Disks) FromPlatformDisk(ctx context.Context, req FromPlatformDiskRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/fromPlatformDisk" + + reqWrapped := wrapperFromPlatformDiskRequest{ + FromPlatformDiskRequest: req, + AsyncMode: false, + } + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} + +// FromPlatformDiskAsync creates template from platform disk in async mode. +// It returns guid of task and error. +func (d Disks) FromPlatformDiskAsync(ctx context.Context, req FromPlatformDiskRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/fromPlatformDisk" + + reqWrapped := wrapperFromPlatformDiskRequest{ + FromPlatformDiskRequest: req, + AsyncMode: true, + } + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/disks/get.go b/pkg/cloudapi/disks/get.go new file mode 100644 index 0000000..afacad5 --- /dev/null +++ b/pkg/cloudapi/disks/get.go @@ -0,0 +1,48 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about disk +type GetRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// Get gets disk details as a RecordDisk struct +// Notice: the devicename field is the name as it is passed to the kernel (kname in linux) for unattached disks this field has no relevant value +func (d Disks) Get(ctx context.Context, req GetRequest) (*RecordDisk, error) { + res, err := d.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordDisk{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets disk details as an array of bytes +// Notice: the devicename field is the name as it is passed to the kernel (kname in linux) for unattached disks this field has no relevant value +func (d Disks) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/get" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/disks/ids.go b/pkg/cloudapi/disks/ids.go new file mode 100644 index 0000000..a904b38 --- /dev/null +++ b/pkg/cloudapi/disks/ids.go @@ -0,0 +1,28 @@ +package disks + +// IDs gets array of DiskIDs from ListDisks struct +func (ld ListDisks) IDs() []uint64 { + res := make([]uint64, 0, len(ld.Data)) + for _, d := range ld.Data { + res = append(res, d.ID) + } + return res +} + +// IDs gets array of DiskIDs from ListDisksUnattached struct +func (ldu ListDisksUnattached) IDs() []uint64 { + res := make([]uint64, 0, len(ldu.Data)) + for _, d := range ldu.Data { + res = append(res, d.ID) + } + return res +} + +// IDs gets array of DiskIDs from ListSearchDisks struct +func (lsd ListSearchDisks) IDs() []uint64 { + res := make([]uint64, 0, len(lsd)) + for _, d := range lsd { + res = append(res, d.ID) + } + return res +} \ No newline at end of file diff --git a/pkg/cloudapi/disks/limitio.go b/pkg/cloudapi/disks/limitio.go new file mode 100644 index 0000000..d85e485 --- /dev/null +++ b/pkg/cloudapi/disks/limitio.go @@ -0,0 +1,96 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// LimitIORequest struct for limit IO +type LimitIORequest struct { + // ID of the disk to limit + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Alias for total_iops_sec for backwards compatibility + // Required: false + IOPS uint64 `url:"iops,omitempty" json:"iops,omitempty"` + + // TotalBytesSec + // Required: false + TotalBytesSec uint64 `url:"total_bytes_sec,omitempty" json:"total_bytes_sec,omitempty"` + + // ReadBytesSec + // Required: false + ReadBytesSec uint64 `url:"read_bytes_sec,omitempty" json:"read_bytes_sec,omitempty"` + + // WriteBytesSec + // Required: false + WriteBytesSec uint64 `url:"write_bytes_sec,omitempty" json:"write_bytes_sec,omitempty"` + + // TotalIOPSSec + // Required: false + TotalIOPSSec uint64 `url:"total_iops_sec,omitempty" json:"total_iops_sec,omitempty"` + + // ReadIOPSSec + // Required: false + ReadIOPSSec uint64 `url:"read_iops_sec,omitempty" json:"read_iops_sec,omitempty"` + + // WriteIOPSSec + // Required: false + WriteIOPSSec uint64 `url:"write_iops_sec,omitempty" json:"write_iops_sec,omitempty"` + + // TotalBytesSecMax + // Required: false + TotalBytesSecMax uint64 `url:"total_bytes_sec_max,omitempty" json:"total_bytes_sec_max,omitempty"` + + // ReadBytesSecMax + // Required: false + ReadBytesSecMax uint64 `url:"read_bytes_sec_max,omitempty" json:"read_bytes_sec_max,omitempty"` + + // WriteBytesSecMax + // Required: false + WriteBytesSecMax uint64 `url:"write_bytes_sec_max,omitempty" json:"write_bytes_sec_max,omitempty"` + + // TotalIOPSSecMax + // Required: false + TotalIOPSSecMax uint64 `url:"total_iops_sec_max,omitempty" json:"total_iops_sec_max,omitempty"` + + // ReadIOPSSecMax + // Required: false + ReadIOPSSecMax uint64 `url:"read_iops_sec_max,omitempty" json:"read_iops_sec_max,omitempty"` + + // WriteIOPSSecMax + // Required: false + WriteIOPSSecMax uint64 `url:"write_iops_sec_max,omitempty" json:"write_iops_sec_max,omitempty"` + + // SizeIOPSSec + // Required: false + SizeIOPSSec uint64 `url:"size_iops_sec,omitempty" json:"size_iops_sec,omitempty"` +} + +// LimitIO limit IO for a certain disk +// total and read/write options are not allowed to be combined +// see http://libvirt.org/formatdomain.html#elementsDisks iotune section for more details +func (d Disks) LimitIO(ctx context.Context, req LimitIORequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/limitIO" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/list.go b/pkg/cloudapi/disks/list.go new file mode 100644 index 0000000..476f6f7 --- /dev/null +++ b/pkg/cloudapi/disks/list.go @@ -0,0 +1,95 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of disks +type ListRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account name + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by max size disk + // Required: false + DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by shared, true or false + // Required: false + Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"` + + // ID of the account the disks belong to + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Type of the disks + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Find by sep ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by pool name + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of the created disks belonging to an account as a ListDisks struct +func (d Disks) List(ctx context.Context, req ListRequest) (*ListDisks, error) { + + res, err := d.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of the created disks belonging to an account as an array of bytes +func (d Disks) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/list" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/disks/list_deleted.go b/pkg/cloudapi/disks/list_deleted.go new file mode 100644 index 0000000..729799a --- /dev/null +++ b/pkg/cloudapi/disks/list_deleted.go @@ -0,0 +1,76 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted disks +type ListDeletedRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account name + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by max size disk + // Required: false + DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"` + + // Find by shared, true or false + // Required: false + Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"` + + // ID of the account the disks belong to + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Type of the disks + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list the deleted disks belonging to an account +func (d Disks) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListDisks, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/listDeleted" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/disks/list_types.go b/pkg/cloudapi/disks/list_types.go new file mode 100644 index 0000000..79ea859 --- /dev/null +++ b/pkg/cloudapi/disks/list_types.go @@ -0,0 +1,52 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListTypesRequest struct to get list types of disks +type ListTypesRequest struct { + // Show detailed disk types by seps + // Required: true + Detailed bool `url:"detailed" json:"detailed"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListTypes gets list defined disk types +func (d Disks) ListTypes(ctx context.Context, req ListTypesRequest) (*ListTypes, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/listTypes" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListTypes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/disks/list_unattached.go b/pkg/cloudapi/disks/list_unattached.go new file mode 100644 index 0000000..6172db0 --- /dev/null +++ b/pkg/cloudapi/disks/list_unattached.go @@ -0,0 +1,80 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListUnattachedRequest struct to get list of unattached disk +type ListUnattachedRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by account name + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by max size disk + // Required: false + DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Type of the disks + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // ID of the account + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by sep ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by pool name + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListUnattached gets list of unattached disks +func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (*ListDisksUnattached, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/listUnattached" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListDisksUnattached{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/disks/models.go b/pkg/cloudapi/disks/models.go new file mode 100644 index 0000000..2838b65 --- /dev/null +++ b/pkg/cloudapi/disks/models.go @@ -0,0 +1,480 @@ +package disks + +// Main information about disk +type ItemDisk struct { + // Access Control List + ACL map[string]interface{} `json:"acl"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Computes + Computes map[string]string `json:"computes"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Device name + DeviceName string `json:"devicename"` + + // Description + Description string `json:"desc"` + + // Destruction time + DestructionTime uint64 `json:"destructionTime"` + + // Grid ID + GID uint64 `json:"gid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // List of image IDs + Images []uint64 `json:"images"` + + // IOTune + IOTune IOTune `json:"iotune"` + + // Machine ID + MachineID uint64 `json:"machineId"` + + // Machine name + MachineName string `json:"machineName"` + + // Name + Name string `json:"name"` + + // Order + Order uint64 `json:"order"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Purge time + PurgeTime uint64 `json:"purgeTime"` + + // Replication + Replication ItemReplication `json:"replication"` + + // Resource ID + ResID string `json:"resId"` + + // Resource name + ResName string `json:"resName"` + + // Role + Role string `json:"role"` + + // SepType + SepType string `json:"sepType"` + + // Shareable + Shareable bool `json:"shareable"` + + // SepID + SepID uint64 `json:"sepId"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List of snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` +} + +type ItemDiskUnattached struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control List + ACL map[string]interface{} `json:"acl"` + + // Boot Partition + BootPartition uint64 `json:"bootPartition"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Destruction time + DestructionTime uint64 `json:"destructionTime"` + + // Disk path + DiskPath string `json:"diskPath"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Images + Images []uint64 `json:"images"` + + // IOTune + IOTune IOTune `json:"iotune"` + + // IQN + IQN string `json:"iqn"` + + // Login + Login string `json:"login"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Order + Order uint64 `json:"order"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // Password + Password string `json:"passwd"` + + //PCISlot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Purge attempts + PurgeAttempts uint64 `json:"purgeAttempts"` + + // Purge time + PurgeTime uint64 `json:"purgeTime"` + + // Reality device number + RealityDeviceNumber uint64 `json:"realityDeviceNumber"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Resource ID + ResID string `json:"resId"` + + // Resource name + ResName string `json:"resName"` + + // Role + Role string `json:"role"` + + // ID SEP + SEPID uint64 `json:"sepId"` + + // Shareable + Shareable bool `json:"shareable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List of snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` +} + +// List of disks searched +type ListSearchDisks []ItemDisk + +// List of disks +type ListDisks struct { + // Data + Data []ItemDisk `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// List of unattached disks +type ListDisksUnattached struct { + // Data + Data []ItemDiskUnattached `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about snapshot +type ItemSnapshot struct { + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + ReferenceID string `json:"referenceId"` + + // Resource ID + ResID string `json:"resId"` + + // SnapSetGUID + SnapSetGUID string `json:"snapSetGuid"` + + // SnapSetTime + SnapSetTime uint64 `json:"snapSetTime"` + + // TimeStamp + TimeStamp uint64 `json:"timestamp"` +} + +// List of snapshots +type ListSnapshots []ItemSnapshot + +// Main information about IO tune +type IOTune struct { + // ReadBytesSec + ReadBytesSec uint64 `json:"read_bytes_sec"` + + // ReadBytesSecMax + ReadBytesSecMax uint64 `json:"read_bytes_sec_max"` + + // ReadIOPSSec + ReadIOPSSec uint64 `json:"read_iops_sec"` + + // ReadIOPSSecMax + ReadIOPSSecMax uint64 `json:"read_iops_sec_max"` + + // SizeIOPSSec + SizeIOPSSec uint64 `json:"size_iops_sec"` + + // TotalBytesSec + TotalBytesSec uint64 `json:"total_bytes_sec"` + + // TotalBytesSecMax + TotalBytesSecMax uint64 `json:"total_bytes_sec_max"` + + // TotalIOPSSec + TotalIOPSSec uint64 `json:"total_iops_sec"` + + // TotalIOPSSecMax + TotalIOPSSecMax uint64 `json:"total_iops_sec_max"` + + // WriteBytesSec + WriteBytesSec uint64 `json:"write_bytes_sec"` + + // WriteBytesSecMax + WriteBytesSecMax uint64 `json:"write_bytes_sec_max"` + + // WriteIOPSSec + WriteIOPSSec uint64 `json:"write_iops_sec"` + + // WriteIOPSSecMax + WriteIOPSSecMax uint64 `json:"write_iops_sec_max"` +} + +// Detailed information about disk +type RecordDisk struct { + // Access Control List + ACL map[string]interface{} `json:"acl"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Computes + Computes map[string]string `json:"computes"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Device name + DeviceName string `json:"devicename"` + + // Description + Description string `json:"desc"` + + // Destruction time + DestructionTime uint64 `json:"destructionTime"` + + // Grid ID + GID uint64 `json:"gid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // List of image IDs + Images []uint64 `json:"images"` + + // IOTune + IOTune IOTune `json:"iotune"` + + // Name + Name string `json:"name"` + + // Order + Order uint64 `json:"order"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Purge time + PurgeTime uint64 `json:"purgeTime"` + + // Replication + Replication ItemReplication `json:"replication"` + + // Resource ID + ResID string `json:"resId"` + + // Resource name + ResName string `json:"resName"` + + // Role + Role string `json:"role"` + + // SepType + SepType string `json:"sepType"` + + // SepID + SepID uint64 `json:"sepId"` + + // Shareable + Shareable bool `json:"shareable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List of snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` +} + +type ItemReplication struct { + // DiskID + DiskID uint64 `json:"diskId"` + + // PoolID + PoolID string `json:"poolId"` + + // Role + Role string `json:"role"` + + // SelfVolumeID + SelfVolumeID string `json:"selfVolumeId"` + + // StorageID + StorageID string `json:"storageId"` + + // VolumeID + VolumeID string `json:"volumeId"` +} + +type ListTypes struct { + // Data + Data []interface{} `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/disks/rename.go b/pkg/cloudapi/disks/rename.go new file mode 100644 index 0000000..2d88fdc --- /dev/null +++ b/pkg/cloudapi/disks/rename.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RenameRequest struct to rename disk +type RenameRequest struct { + // ID of the disk to rename + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // New name of disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +// Rename renames disk +func (d Disks) Rename(ctx context.Context, req RenameRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/rename" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/replicate.go b/pkg/cloudapi/disks/replicate.go new file mode 100644 index 0000000..b3139c2 --- /dev/null +++ b/pkg/cloudapi/disks/replicate.go @@ -0,0 +1,52 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicateRequest struct to create an empty disk in chosen SEP and pool combination. +type ReplicateRequest struct { + // Id of the disk to replicate. This disk will become master in replication + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Name of replica disk to create + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of SEP to create slave disk + // Required: true + SepID uint64 `url:"sepId" json:"sepId" validate:"required"` + + // Pool name to create slave disk in + // Required: true + PoolName string `url:"poolName" json:"poolName" validate:"required"` +} + +// Create an empty disk in chosen SEP and pool combination. +// Starts replication between chosen disk and newly created disk +// Note: only TATLIN type SEP are supported for replications between +func (d Disks) Replicate(ctx context.Context, req ReplicateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/replicate" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/replication_resume.go b/pkg/cloudapi/disks/replication_resume.go new file mode 100644 index 0000000..909cb8d --- /dev/null +++ b/pkg/cloudapi/disks/replication_resume.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationResume struct to resume suspended replication +type ReplicationResumeRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationResume resume suspended replication +func (d Disks) ReplicationResume(ctx context.Context, req ReplicationResumeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/replicationResume" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/replication_reverse.go b/pkg/cloudapi/disks/replication_reverse.go new file mode 100644 index 0000000..de2b2de --- /dev/null +++ b/pkg/cloudapi/disks/replication_reverse.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationReverseRequest struct to change role between disks replications +type ReplicationReverseRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationReverse change role between disks replications +func (d Disks) ReplicationReverse(ctx context.Context, req ReplicationReverseRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/replicationReverse" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/replication_start.go b/pkg/cloudapi/disks/replication_start.go new file mode 100644 index 0000000..c048d88 --- /dev/null +++ b/pkg/cloudapi/disks/replication_start.go @@ -0,0 +1,43 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationStartRequest struct to starts replication between two chosen disks +type ReplicationStartRequest struct { + // Id of the disk to replicate. Primary disk in replication + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID of target disk. Secondary disk in replication + // Required: true + TargetDiskID uint64 `url:"targetDiskId" json:"targetDiskId" validate:"required"` +} + +// ReplicationStart starts replication between two chosen disks. It's required for both disks to have same size to avoid replication conflicts +// Note: Source disk's SEP and target SEP supported only of TATLIN type. +func (d Disks) ReplicationStart(ctx context.Context, req ReplicationStartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/replicationStart" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/replication_status.go b/pkg/cloudapi/disks/replication_status.go new file mode 100644 index 0000000..23e5991 --- /dev/null +++ b/pkg/cloudapi/disks/replication_status.go @@ -0,0 +1,32 @@ +package disks + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationStatusRequest struct to get replication status +type ReplicationStatusRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationStatus get replication status +func (d Disks) ReplicationStatus(ctx context.Context, req ReplicationStatusRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/replicationStatus" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/disks/replication_stop.go b/pkg/cloudapi/disks/replication_stop.go new file mode 100644 index 0000000..a74fd18 --- /dev/null +++ b/pkg/cloudapi/disks/replication_stop.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationStopRequest struct to remove replication between disks completely +type ReplicationStopRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationStop remove replication between disks completely +func (d Disks) ReplicationStop(ctx context.Context, req ReplicationStopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/replicationStop" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/replication_suspend.go b/pkg/cloudapi/disks/replication_suspend.go new file mode 100644 index 0000000..c644d8d --- /dev/null +++ b/pkg/cloudapi/disks/replication_suspend.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationSuspendRequest struct to pause replication with possibility to resume from pause moment +type ReplicationSuspendRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationSuspend pause replication with possibility to resume from pause moment +func (d Disks) ReplicationSuspend(ctx context.Context, req ReplicationSuspendRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/replicationSuspend" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/resize.go b/pkg/cloudapi/disks/resize.go new file mode 100644 index 0000000..ea7f130 --- /dev/null +++ b/pkg/cloudapi/disks/resize.go @@ -0,0 +1,70 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ResizeRequest struct to resize disk +type ResizeRequest struct { + // ID of the disk to resize + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // New size of the disk in GB + // Required: true + Size uint64 `url:"size" json:"size" validate:"required"` +} + +// Resize resizes disk +// Returns 200 if disk is resized online, else will return 202, +// in that case please stop and start your machine after changing the disk size, for your changes to be reflected. +// This method will not be used for disks, assigned to computes. Only unassigned disks and disks, assigned with "old" virtual machines. +func (d Disks) Resize(ctx context.Context, req ResizeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/resize" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// Resize2 resize disk +// Returns 200 if disk is resized online, else will return 202, +// in that case please stop and start your machine after changing the disk size, for your changes to be reflected. +// This method will not be used for disks, assigned to "old" virtual machines. Only unassigned disks and disks, assigned with computes. +func (d Disks) Resize2(ctx context.Context, req ResizeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/resize2" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/restore.go b/pkg/cloudapi/disks/restore.go new file mode 100644 index 0000000..5d784e9 --- /dev/null +++ b/pkg/cloudapi/disks/restore.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore a deleted unattached disk +type RestoreRequest struct { + // ID of the disk to restore + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// Restore restores a deleted unattached disk from recycle bin +func (d Disks) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/restore" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/search.go b/pkg/cloudapi/disks/search.go new file mode 100644 index 0000000..498bb1b --- /dev/null +++ b/pkg/cloudapi/disks/search.go @@ -0,0 +1,48 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" +) + +// SearchRequest struct for search +type SearchRequest struct { + // ID of the account to search for the Disk + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + // Name of the Disk to search for + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // If false, then disks having one of the statuses are not listed + // Required: false + ShowAll bool `url:"show_all,omitempty" json:"show_all,omitempty"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// Search searches disks +func (d Disks) Search(ctx context.Context, req SearchRequest) (ListSearchDisks, error) { + url := "/cloudapi/disks/search" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListSearchDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/disks/serialize.go b/pkg/cloudapi/disks/serialize.go new file mode 100644 index 0000000..bf22f8e --- /dev/null +++ b/pkg/cloudapi/disks/serialize.go @@ -0,0 +1,99 @@ +package disks + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ld ListDisks) Serialize(params ...string) (serialization.Serialized, error) { + if ld.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ld, prefix, indent) + } + + return json.Marshal(ld) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ld ListSearchDisks) Serialize(params ...string) (serialization.Serialized, error) { + if len(ld) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ld, prefix, indent) + } + + return json.Marshal(ld) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (idisk ItemDisk) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(idisk, prefix, indent) + } + + return json.Marshal(idisk) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lu ListDisksUnattached) Serialize(params ...string) (serialization.Serialized, error) { + if lu.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lu, prefix, indent) + } + + return json.Marshal(lu) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (idisk ItemDiskUnattached) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(idisk, prefix, indent) + } + + return json.Marshal(idisk) +} diff --git a/pkg/cloudapi/disks/share.go b/pkg/cloudapi/disks/share.go new file mode 100644 index 0000000..180f32b --- /dev/null +++ b/pkg/cloudapi/disks/share.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ShareRequest struct to share disk data +type ShareRequest struct { + // ID of the disk to share + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// Share shares data disk +func (d Disks) Share(ctx context.Context, req ShareRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/share" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/snapshot_delete.go b/pkg/cloudapi/disks/snapshot_delete.go new file mode 100644 index 0000000..79a77de --- /dev/null +++ b/pkg/cloudapi/disks/snapshot_delete.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotDeleteRequest struct to delete snapshot +type SnapshotDeleteRequest struct { + // ID of disk to delete + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Label of the snapshot to delete + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotDelete deletes a snapshot +func (d Disks) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/snapshotDelete" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/disks/snapshot_rollback.go b/pkg/cloudapi/disks/snapshot_rollback.go new file mode 100644 index 0000000..68c9a95 --- /dev/null +++ b/pkg/cloudapi/disks/snapshot_rollback.go @@ -0,0 +1,47 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotRollbackRequest struct to rollback snapshot +type SnapshotRollbackRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Label of the snapshot to rollback + // Required: false + Label string `url:"label,omitempty" json:"label,omitempty"` + + // Timestamp of the snapshot to rollback + // Required: false + TimeStamp uint64 `url:"timestamp,omitempty" json:"timestamp,omitempty"` +} + +// SnapshotRollback rollbacks an individual disk snapshot +func (d Disks) SnapshotRollback(ctx context.Context, req SnapshotRollbackRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/snapshotRollback" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil + +} diff --git a/pkg/cloudapi/disks/sorting.go b/pkg/cloudapi/disks/sorting.go new file mode 100644 index 0000000..39c9055 --- /dev/null +++ b/pkg/cloudapi/disks/sorting.go @@ -0,0 +1,174 @@ +package disks + +import "sort" + +// SortByCreatedTime sorts ListDisks by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListDisks) SortByCreatedTime(inverse bool) ListDisks { + if len(ld.Data) < 2 { + return ld + } + + sort.Slice(ld.Data, func(i, j int) bool { + if inverse { + return ld.Data[i].CreatedTime > ld.Data[j].CreatedTime + } + + return ld.Data[i].CreatedTime < ld.Data[j].CreatedTime + }) + + return ld +} + +// SortByDestructionTime sorts ListDisks by the DestructionTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListDisks) SortByDestructionTime(inverse bool) ListDisks { + if len(ld.Data) < 2 { + return ld + } + + sort.Slice(ld.Data, func(i, j int) bool { + if inverse { + return ld.Data[i].DestructionTime > ld.Data[j].DestructionTime + } + + return ld.Data[i].DestructionTime < ld.Data[j].DestructionTime + }) + + return ld +} + +// SortByDeletedTime sorts ListDisks by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListDisks) SortByDeletedTime(inverse bool) ListDisks { + if len(ld.Data) < 2 { + return ld + } + + sort.Slice(ld.Data, func(i, j int) bool { + if inverse { + return ld.Data[i].DeletedTime > ld.Data[j].DeletedTime + } + + return ld.Data[i].DeletedTime < ld.Data[j].DeletedTime + }) + + return ld +} + +// SortByCreatedTime sorts ListSearchDisks by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListSearchDisks) SortByCreatedTime(inverse bool) ListSearchDisks { + if len(ld) < 2 { + return ld + } + + sort.Slice(ld, func(i, j int) bool { + if inverse { + return ld[i].CreatedTime > ld[j].CreatedTime + } + + return ld[i].CreatedTime < ld[j].CreatedTime + }) + + return ld +} + +// SortByDestructionTime sorts ListSearchDisks by the DestructionTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListSearchDisks) SortByDestructionTime(inverse bool) ListSearchDisks { + if len(ld) < 2 { + return ld + } + + sort.Slice(ld, func(i, j int) bool { + if inverse { + return ld[i].DestructionTime > ld[j].DestructionTime + } + + return ld[i].DestructionTime < ld[j].DestructionTime + }) + + return ld +} + +// SortByDeletedTime sorts ListSearchDisks by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListSearchDisks) SortByDeletedTime(inverse bool) ListSearchDisks { + if len(ld) < 2 { + return ld + } + + sort.Slice(ld, func(i, j int) bool { + if inverse { + return ld[i].DeletedTime > ld[j].DeletedTime + } + + return ld[i].DeletedTime < ld[j].DeletedTime + }) + + return ld +} + +// SortByCreatedTime sorts ListDisksUnattached by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lu ListDisksUnattached) SortByCreatedTime(inverse bool) ListDisksUnattached { + if len(lu.Data) < 2 { + return lu + } + + sort.Slice(lu.Data, func(i, j int) bool { + if inverse { + return lu.Data[i].CreatedTime > lu.Data[j].CreatedTime + } + + return lu.Data[i].CreatedTime < lu.Data[j].CreatedTime + }) + + return lu +} + +// SortByDestructionTime sorts ListDisksUnattached by the DestructionTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lu ListDisksUnattached) SortByDestructionTime(inverse bool) ListDisksUnattached { + if len(lu.Data) < 2 { + return lu + } + + sort.Slice(lu.Data, func(i, j int) bool { + if inverse { + return lu.Data[i].DestructionTime > lu.Data[j].DestructionTime + } + + return lu.Data[i].DestructionTime < lu.Data[j].DestructionTime + }) + + return lu +} + +// SortByDeletedTime sorts ListDisksUnattached by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lu ListDisksUnattached) SortByDeletedTime(inverse bool) ListDisksUnattached { + if len(lu.Data) < 2 { + return lu + } + + sort.Slice(lu.Data, func(i, j int) bool { + if inverse { + return lu.Data[i].DeletedTime > lu.Data[j].DeletedTime + } + + return lu.Data[i].DeletedTime < lu.Data[j].DeletedTime + }) + + return lu +} diff --git a/pkg/cloudapi/disks/unshare.go b/pkg/cloudapi/disks/unshare.go new file mode 100644 index 0000000..78fc832 --- /dev/null +++ b/pkg/cloudapi/disks/unshare.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UnshareRequest struct to unshare data disk +type UnshareRequest struct { + // ID of the disk to unshare + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// Unshare unshares data disk +func (d Disks) Unshare(ctx context.Context, req UnshareRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/unshare" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/dpdknet.go b/pkg/cloudapi/dpdknet.go new file mode 100644 index 0000000..b6f91d8 --- /dev/null +++ b/pkg/cloudapi/dpdknet.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/dpdknet" + +// Accessing the DPDKNet method group +func (ca *CloudAPI) DPDKNet() *dpdknet.DPDKNet { + return dpdknet.New(ca.client) +} diff --git a/pkg/cloudapi/dpdknet/dpdknet.go b/pkg/cloudapi/dpdknet/dpdknet.go new file mode 100644 index 0000000..c7de8c2 --- /dev/null +++ b/pkg/cloudapi/dpdknet/dpdknet.go @@ -0,0 +1,15 @@ +package dpdknet + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to DPDK network +type DPDKNet struct { + client interfaces.Caller +} + +// Builder for dpdk endpoints +func New(client interfaces.Caller) *DPDKNet { + return &DPDKNet{ + client, + } +} diff --git a/pkg/cloudapi/dpdknet/get.go b/pkg/cloudapi/dpdknet/get.go new file mode 100644 index 0000000..98ef42e --- /dev/null +++ b/pkg/cloudapi/dpdknet/get.go @@ -0,0 +1,46 @@ +package dpdknet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about DPDK network +type GetRequest struct { + // ID of the DPDK network + // Required: true + DPDKID uint64 `url:"dpdkId" json:"dpdkId" validate:"required"` +} + +// Get DPDK network details as a RecordDPDKNet struct +func (d DPDKNet) Get(ctx context.Context, req GetRequest) (*RecordDPDKNet, error) { + res, err := d.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordDPDKNet{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets DPDK network details as an array of bytes +func (d DPDKNet) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/dpdknet/get" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/dpdknet/list.go b/pkg/cloudapi/dpdknet/list.go new file mode 100644 index 0000000..cc139a8 --- /dev/null +++ b/pkg/cloudapi/dpdknet/list.go @@ -0,0 +1,79 @@ +package dpdknet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of DPDK networks +type ListRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by gid + // Required: false + GID uint64 `url:"gid,omitempty" json:"gid,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by computeIDs + // Required: false + ComputeIDs []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of the created DPDK networks belonging to an account as a ListDPDKNet struct +func (d DPDKNet) List(ctx context.Context, req ListRequest) (*ListDPDKNet, error) { + + res, err := d.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListDPDKNet{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of the created DPDK networks belonging to an account as an array of bytes +func (d DPDKNet) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/dpdknet/list" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/dpdknet/models.go b/pkg/cloudapi/dpdknet/models.go new file mode 100644 index 0000000..7bcf84d --- /dev/null +++ b/pkg/cloudapi/dpdknet/models.go @@ -0,0 +1,92 @@ +package dpdknet + +// Detailed information about DPDK network +type RecordDPDKNet struct { + // List of accounts with access + AccountAccess []uint64 `json:"accountAccess"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Description + Description string `json:"description"` + + // Grid ID + GID uint64 `json:"gid"` + + // Guid ID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // List of resource groups with access + RGAccess []uint64 `json:"rgAccess"` + + // Status + Status string `json:"status"` + + // OVS bridge + OVSBridge string `json:"ovsBridge"` + + // Vlan ID + VlanID uint64 `json:"vlanId"` + + // Compute IDs + ComputeIDs []uint64 `json:"computeIds"` +} + +type ListDPDKNet struct { + // Data + Data []ItemDPDKNet `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type ItemDPDKNet struct { + // List of accounts with access + AccountAccess []uint64 `json:"accountAccess"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Description + Description string `json:"description"` + + // Grid ID + GID uint64 `json:"gid"` + + // Guid ID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // List of resource groups with access + RGAccess []uint64 `json:"rgAccess"` + + // Status + Status string `json:"status"` + + // OVS bridge + OVSBridge string `json:"ovsBridge"` + + // Vlan ID + VlanID uint64 `json:"vlanId"` + + // Compute IDs + ComputeIDs []uint64 `json:"computeIds"` +} diff --git a/pkg/cloudapi/extnet.go b/pkg/cloudapi/extnet.go new file mode 100644 index 0000000..d6012c9 --- /dev/null +++ b/pkg/cloudapi/extnet.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/extnet" +) + +// Accessing the ExtNet method group +func (ca *CloudAPI) ExtNet() *extnet.ExtNet { + return extnet.New(ca.client) +} diff --git a/pkg/cloudapi/extnet/extnet.go b/pkg/cloudapi/extnet/extnet.go new file mode 100644 index 0000000..5530e03 --- /dev/null +++ b/pkg/cloudapi/extnet/extnet.go @@ -0,0 +1,18 @@ +// API Actor for use external networks +package extnet + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to extnet +type ExtNet struct { + client interfaces.Caller +} + +// Builder for extnet endpoints +func New(client interfaces.Caller) *ExtNet { + return &ExtNet{ + client, + } +} diff --git a/pkg/cloudapi/extnet/filter.go b/pkg/cloudapi/extnet/filter.go new file mode 100644 index 0000000..c6d1e7b --- /dev/null +++ b/pkg/cloudapi/extnet/filter.go @@ -0,0 +1,53 @@ +package extnet + +// FilterByID returns ListExtNets with specified ID. +func (lenet ListExtNets) FilterByID(id uint64) ListExtNets { + predicate := func(iexnet ItemExtNet) bool { + return iexnet.ID == id + } + + return lenet.FilterFunc(predicate) +} + +// FilterByName returns ListExtNets with specified Name. +func (lenet ListExtNets) FilterByName(name string) ListExtNets { + predicate := func(iexnet ItemExtNet) bool { + return iexnet.Name == name + } + + return lenet.FilterFunc(predicate) +} + +// FilterByStatus returns ListExtNets with specified Status. +func (lenet ListExtNets) FilterByStatus(status string) ListExtNets { + predicate := func(iexnet ItemExtNet) bool { + return iexnet.Status == status + } + + return lenet.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListExtNets based on a user-specified predicate. +func (lenet ListExtNets) FilterFunc(predicate func(ItemExtNet) bool) ListExtNets { + var result ListExtNets + + for _, item := range lenet.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemExtNet +// If none was found, returns an empty struct. +func (lenet ListExtNets) FindOne() ItemExtNet { + if lenet.EntryCount == 0 { + return ItemExtNet{} + } + + return lenet.Data[0] +} diff --git a/pkg/cloudapi/extnet/filter_test.go b/pkg/cloudapi/extnet/filter_test.go new file mode 100644 index 0000000..1b05b7b --- /dev/null +++ b/pkg/cloudapi/extnet/filter_test.go @@ -0,0 +1,67 @@ +package extnet + +import "testing" + +var extnets = ListExtNets{ + Data: []ItemExtNet{ + { + ID: 3, + IPCIDR: "176.118.164.0/24", + Name: "176.118.164.0/24", + Status: "ENABLED", + }, + { + ID: 10, + IPCIDR: "45.134.255.0/24", + Name: "45.134.255.0/24", + Status: "ENABLED", + }, + { + ID: 13, + IPCIDR: "88.218.249.0/24", + Name: "88.218.249.0/24", + Status: "DISABLED", + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := extnets.FilterByID(10).FindOne() + + if actual.ID != 10 { + t.Fatal("expected ID 10, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + name := "88.218.249.0/24" + actual := extnets.FilterByName(name).FindOne() + + if actual.Name != name { + t.Fatal("expected ", name, " found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := extnets.FilterByStatus("ENABLED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := extnets.FilterFunc(func(ien ItemExtNet) bool { + return ien.IPCIDR == ien.Name + }) + + if len(actual.Data) != 3 { + t.Fatal("expected 3 elements, found: ", len(actual.Data)) + } +} diff --git a/pkg/cloudapi/extnet/get.go b/pkg/cloudapi/extnet/get.go new file mode 100644 index 0000000..1e287bf --- /dev/null +++ b/pkg/cloudapi/extnet/get.go @@ -0,0 +1,46 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about external network +type GetRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// Get gets detailed information about external network as a RecordExtNet struct +func (e ExtNet) Get(ctx context.Context, req GetRequest) (*RecordExtNet, error) { + res, err := e.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordExtNet{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed information about external network as an array of bytes +func (e ExtNet) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/extnet/get" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/extnet/get_default.go b/pkg/cloudapi/extnet/get_default.go new file mode 100644 index 0000000..98b6628 --- /dev/null +++ b/pkg/cloudapi/extnet/get_default.go @@ -0,0 +1,24 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" +) + +// GetDefault get default external network ID +func (e ExtNet) GetDefault(ctx context.Context) (uint64, error) { + url := "/cloudapi/extnet/getDefault" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/extnet/ids.go b/pkg/cloudapi/extnet/ids.go new file mode 100644 index 0000000..637c716 --- /dev/null +++ b/pkg/cloudapi/extnet/ids.go @@ -0,0 +1,19 @@ +package extnet + +// IDs gets array of ExtNetIDs from ListExtNets struct +func (le ListExtNets) IDs() []uint64 { + res := make([]uint64, 0, len(le.Data)) + for _, e := range le.Data { + res = append(res, e.ID) + } + return res +} + +// IDs gets array of ComputeIDs from ListExtNetComputes struct +func (le ListExtNetComputes) IDs() []uint64 { + res := make([]uint64, 0, len(le.Data)) + for _, e := range le.Data { + res = append(res, e.ID) + } + return res +} diff --git a/pkg/cloudapi/extnet/list.go b/pkg/cloudapi/extnet/list.go new file mode 100644 index 0000000..f42e07a --- /dev/null +++ b/pkg/cloudapi/extnet/list.go @@ -0,0 +1,83 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of external network +type ListRequest struct { + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by network ip address + // Required: false + Network string `url:"network,omitempty" json:"network,omitempty"` + + // Find by vlan ID + // Required: false + VLANID uint64 `url:"vlanId,omitempty" json:"vlanId,omitempty"` + + // Find by vnfDevices ID + // Required: false + VNFDevID uint64 `url:"vnfDevId,omitempty" json:"vnfDevId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all available external networks as a ListExtNets struct +func (e ExtNet) List(ctx context.Context, req ListRequest) (*ListExtNets, error) { + + res, err := e.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListExtNets{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all available external networks as an array of bytes +func (e ExtNet) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/extnet/list" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/extnet/list_computes.go b/pkg/cloudapi/extnet/list_computes.go new file mode 100644 index 0000000..bcd608e --- /dev/null +++ b/pkg/cloudapi/extnet/list_computes.go @@ -0,0 +1,61 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListComputesRequest struct to get list computes +type ListComputesRequest struct { + // Filter by account ID + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by rg ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by compute ID + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListComputes gets computes from account with extnets +func (e ExtNet) ListComputes(ctx context.Context, req ListComputesRequest) (*ListExtNetComputes, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/extnet/listComputes" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListExtNetComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/extnet/models.go b/pkg/cloudapi/extnet/models.go new file mode 100644 index 0000000..257e2b9 --- /dev/null +++ b/pkg/cloudapi/extnet/models.go @@ -0,0 +1,228 @@ +package extnet + +// Main information about external network +type ItemExtNet struct { + // ID + ID uint64 `json:"id"` + + // IPCIDR + IPCIDR string `json:"ipcidr"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` +} + +// Extend information about external network +type ItemExtNetExtend struct { + // Main information about external network + ItemExtNet + + // IP address + IPAddr string `json:"ipaddr"` +} + +// List of information about external network +type ListExtNets struct { + Data []ItemExtNet `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// List of extend information about external network +type ListExtNetExtends []ItemExtNetExtend + +// Main information about compute with external network +type ItemExtNetCompute struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // List of extend information about external network + ExtNets ListExtNetExtends `json:"extnets"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` +} + +// List of information about computes with external network +type ListExtNetComputes struct { + // Data + Data []ItemExtNetCompute `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// QOS +type QOS struct { + // EBurst + EBurst uint64 `json:"eBurst"` + + // ERate + ERate uint64 `json:"eRate"` + + // GUID + GUID string `json:"guid"` + + // InBurst + InBurst uint64 `json:"inBurst"` + + // InRate + InRate uint64 `json:"inRate"` +} + +// Main information about reservations +type ItemReservation struct { + // ClientType + ClientType string `json:"clientType"` + + // Description + Description string `json:"desc"` + + // Domain name + DomainName string `json:"domainname"` + + // Hostname + Hostname string `json:"hostname"` + + // IP + IP string `json:"ip"` + + // MAC + MAC string `json:"mac"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmId"` +} + +// List of information about reservations +type ListReservations []ItemReservation + +// VNFs +type VNFs struct { + DHCP uint64 `json:"dhcp"` +} + +type Excluded struct { + // ClientType + ClientType string `json:"clientType"` + + // Domain name + DomainName string `json:"domainname"` + + // Host name + HostName string `json:"hostname"` + + // IP + IP string `json:"ip"` + + // MAC + MAC string `json:"mac"` + + // Type + Type string `json:"type"` + + // VMID + VMID uint64 `json:"vmId"` +} + +// Detailed information about external network +type RecordExtNet struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // CheckIps + CheckIPs []string `json:"checkIps"` + + // Default + Default bool `json:"default"` + + // Default QOS + DefaultQOS QOS `json:"defaultQos"` + + // Description + Description string `json:"desc"` + + // list of DNS + DNS []string `json:"dns"` + + // Excluded + Excluded []Excluded `json:"excluded"` + + // Free IPs + FreeIPs int64 `json:"free_ips"` + + // Gateway + Gateway string `json:"gateway"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IPCIDR + IPCIDR string `json:"ipcidr"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // Network ID + NetworkID uint64 `json:"networkId"` + + // OVS Bridge + OVSBridge string `json:"ovsBridge"` + + // PreReservation IP num + PreReservationsNum uint64 `json:"preReservationsNum"` + + // Prefix + Prefix uint64 `json:"prefix"` + + // PriVNFDevID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // List reservations + Reservations ListReservations `json:"reservations"` + + // Shared with + SharedWith []uint64 `json:"sharedWith"` + + // Status + Status string `json:"status"` + + // VLAN ID + VLANID uint64 `json:"vlanId"` + + // VNFs + VNFs VNFs `json:"vnfs"` +} diff --git a/pkg/cloudapi/extnet/serialize.go b/pkg/cloudapi/extnet/serialize.go new file mode 100644 index 0000000..6cfed24 --- /dev/null +++ b/pkg/cloudapi/extnet/serialize.go @@ -0,0 +1,43 @@ +package extnet + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lenet ListExtNets) Serialize(params ...string) (serialization.Serialized, error) { + if lenet.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lenet, prefix, indent) + } + + return json.Marshal(lenet) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ienet ItemExtNet) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ienet, prefix, indent) + } + + return json.Marshal(ienet) +} diff --git a/pkg/cloudapi/flipgroup.go b/pkg/cloudapi/flipgroup.go new file mode 100644 index 0000000..61bcbc6 --- /dev/null +++ b/pkg/cloudapi/flipgroup.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/flipgroup" +) + +// Accessing the FLIPGroup method group +func (ca *CloudAPI) FLIPGroup() *flipgroup.FLIPGroup { + return flipgroup.New(ca.client) +} diff --git a/pkg/cloudapi/flipgroup/compute_add.go b/pkg/cloudapi/flipgroup/compute_add.go new file mode 100644 index 0000000..86b591e --- /dev/null +++ b/pkg/cloudapi/flipgroup/compute_add.go @@ -0,0 +1,42 @@ +package flipgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ComputeAddRequest struct to add compute instance +type ComputeAddRequest struct { + // ID of the Floating IP group to add compute instance to + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` + + // ID of the compute instance to add to this group + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// ComputeAdd add compute instance to the Floating IP group +func (f FLIPGroup) ComputeAdd(ctx context.Context, req ComputeAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/flipgroup/computeAdd" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/flipgroup/compute_remove.go b/pkg/cloudapi/flipgroup/compute_remove.go new file mode 100644 index 0000000..c75d69a --- /dev/null +++ b/pkg/cloudapi/flipgroup/compute_remove.go @@ -0,0 +1,42 @@ +package flipgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ComputeRemoveRequest struct to remove compute instance +type ComputeRemoveRequest struct { + // ID of the Floating IP group to remove compute instance from + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` + + // ID of the compute instance to remove + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// ComputeRemove remove compute instance from the Floating IP group +func (f FLIPGroup) ComputeRemove(ctx context.Context, req ComputeRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/flipgroup/computeRemove" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/flipgroup/create.go b/pkg/cloudapi/flipgroup/create.go new file mode 100644 index 0000000..3a9f6a4 --- /dev/null +++ b/pkg/cloudapi/flipgroup/create.go @@ -0,0 +1,69 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create FLIPGroup +type CreateRequest struct { + // Account ID + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // FLIPGroup name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Network type + // Should be one of: + // - EXTNET + // - VINS + // Required: true + NetType string `url:"netType" json:"netType" validate:"computeNetType"` + + // ID of external network or VINS + // Required: true + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // Type of client + // - 'compute' + // - 'vins' (will be later) + // Required: true + ClientType string `url:"clientType" json:"clientType" validate:"flipgroupClientType"` + + // IP address to associate with this group. If empty, the platform will autoselect IP address + // Required: false + IP string `url:"ip,omitempty" json:"ip,omitempty"` + + // Text description of this FLIPGorup instance + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// Create method will create a new FLIPGorup in the specified Account +func (f FLIPGroup) Create(ctx context.Context, req CreateRequest) (*RecordFLIPGroupCreated, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/flipgroup/create" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordFLIPGroupCreated{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/flipgroup/delete.go b/pkg/cloudapi/flipgroup/delete.go new file mode 100644 index 0000000..a766fdc --- /dev/null +++ b/pkg/cloudapi/flipgroup/delete.go @@ -0,0 +1,38 @@ +package flipgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete FLIPGroup +type DeleteRequest struct { + // FLIPGroup ID + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` +} + +// Delete method wil delete Floating IP group +func (f FLIPGroup) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/flipgroup/delete" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/flipgroup/edit.go b/pkg/cloudapi/flipgroup/edit.go new file mode 100644 index 0000000..8d696e5 --- /dev/null +++ b/pkg/cloudapi/flipgroup/edit.go @@ -0,0 +1,46 @@ +package flipgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EditRequest struct to edit FLIPGroup +type EditRequest struct { + // FLIPGroup ID + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` + + // FLIPGroup name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // FLIPGroup description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// Edit edits FLIPGroup fields +func (f FLIPGroup) Edit(ctx context.Context, req EditRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/flipgroup/edit" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/flipgroup/flipgroup.go b/pkg/cloudapi/flipgroup/flipgroup.go new file mode 100644 index 0000000..0b19d15 --- /dev/null +++ b/pkg/cloudapi/flipgroup/flipgroup.go @@ -0,0 +1,18 @@ +// API to manage FLIPGroup instances +package flipgroup + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to FLIPGroup +type FLIPGroup struct { + client interfaces.Caller +} + +// Builder for FLIPGroup endpoints +func New(client interfaces.Caller) *FLIPGroup { + return &FLIPGroup{ + client, + } +} diff --git a/pkg/cloudapi/flipgroup/get.go b/pkg/cloudapi/flipgroup/get.go new file mode 100644 index 0000000..e5ad341 --- /dev/null +++ b/pkg/cloudapi/flipgroup/get.go @@ -0,0 +1,46 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about FLIPGroup +type GetRequest struct { + // FLIPGroup ID + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` +} + +// Get gets details of the specified Floating IP group as a RecordFLIPGroup struct +func (f FLIPGroup) Get(ctx context.Context, req GetRequest) (*RecordFLIPGroup, error) { + res, err := f.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordFLIPGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets details of the specified Floating IP group as an array of bytes +func (f FLIPGroup) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/flipgroup/get" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/flipgroup/ids.go b/pkg/cloudapi/flipgroup/ids.go new file mode 100644 index 0000000..c326dcd --- /dev/null +++ b/pkg/cloudapi/flipgroup/ids.go @@ -0,0 +1,10 @@ +package flipgroup + +// IDs gets array of FLIPGroupIDs from ListFLIPGroups struct +func (le ListFLIPGroups) IDs() []uint64 { + res := make([]uint64, 0, len(le.Data)) + for _, e := range le.Data { + res = append(res, e.ID) + } + return res +} diff --git a/pkg/cloudapi/flipgroup/list.go b/pkg/cloudapi/flipgroup/list.go new file mode 100644 index 0000000..f59b9ae --- /dev/null +++ b/pkg/cloudapi/flipgroup/list.go @@ -0,0 +1,99 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of FLIPGroup available to the current user +type ListRequest struct { + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by vinsId + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Find by VINS name + // Required: false + VINSName string `url:"vinsName,omitempty" json:"vinsName,omitempty"` + + // Find by extnetId + // Required: false + ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"` + + // Find by IP + // Required: false + ByIP string `url:"byIp,omitempty" json:"byIp,omitempty"` + + // Find by accountId + // Required: false + AccountId uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by list of clientIds + // Required: false + ClientIDs []uint64 `url:"clientIds,omitempty" json:"clientIds,omitempty"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Find by connId + // Required: false + ConnId uint64 `url:"connId,omitempty" json:"connId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` +} + +// List gets list of FLIPGroup managed cluster instances available to the current user as a ListFLIPGroups struct +func (f FLIPGroup) List(ctx context.Context, req ListRequest) (*ListFLIPGroups, error) { + + res, err := f.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListFLIPGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of FLIPGroup managed cluster instances available to the current user as an array of bytes +func (f FLIPGroup) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/flipgroup/list" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/flipgroup/models.go b/pkg/cloudapi/flipgroup/models.go new file mode 100644 index 0000000..2d718da --- /dev/null +++ b/pkg/cloudapi/flipgroup/models.go @@ -0,0 +1,169 @@ +package flipgroup + +// Main information about FLIPGroup +type RecordFLIPGroupCreated struct { + // Default GW + DefaultGW string `json:"defaultGW"` + + // ID + ID uint64 `json:"id"` + + // IP + IP string `json:"ip"` + + // Name + Name string `json:"name"` + + // Network mask + NetMask uint64 `json:"netmask"` +} + +type RecordFLIPGroup struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // List of client IDs + ClientIDs []uint64 `json:"clientIds"` + + // Client names + ClientNames []string `json:"clientNames"` + + // Client type + ClientType string `json:"clientType"` + + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IP + IP string `json:"ip"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network type + NetType string `json:"netType"` + + // Network + Network string `json:"network"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// Detailed information about FLIPGroup +type ItemFLIPGroup struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // List of client IDs + ClientIDs []uint64 `json:"clientIds"` + + // Client type + ClientType string `json:"clientType"` + + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IP + IP string `json:"ip"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network type + NetType string `json:"netType"` + + // NetMask + NetMask uint64 `json:"netmask"` + + // Status + Status string `json:"status"` +} + +// List of FLIPGroup +type ListFLIPGroups struct { + Data []ItemFLIPGroup `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/flipgroup/serialize.go b/pkg/cloudapi/flipgroup/serialize.go new file mode 100644 index 0000000..ee1ad19 --- /dev/null +++ b/pkg/cloudapi/flipgroup/serialize.go @@ -0,0 +1,43 @@ +package flipgroup + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lfg ListFLIPGroups) Serialize(params ...string) (serialization.Serialized, error) { + if len(lfg.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lfg, prefix, indent) + } + + return json.Marshal(lfg) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ifg ItemFLIPGroup) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ifg, prefix, indent) + } + + return json.Marshal(ifg) +} diff --git a/pkg/cloudapi/image.go b/pkg/cloudapi/image.go new file mode 100644 index 0000000..adc3eb2 --- /dev/null +++ b/pkg/cloudapi/image.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/image" +) + +// Accessing the Image method group +func (ca *CloudAPI) Image() *image.Image { + return image.New(ca.client) +} diff --git a/pkg/cloudapi/image/create.go b/pkg/cloudapi/image/create.go new file mode 100644 index 0000000..f9e2bfa --- /dev/null +++ b/pkg/cloudapi/image/create.go @@ -0,0 +1,104 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create image +type CreateRequest struct { + // Name of the rescue disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // URL where to download media from + // Required: true + URL string `url:"url" json:"url" validate:"required,url"` + + // Boot type of image bios or UEFI + // Required: true + BootType string `url:"boottype" json:"boottype" validate:"required,imageBootType"` + + // Image type + // Should be: + // - linux + // - windows + // - or other + // Required: true + ImageType string `url:"imagetype" json:"imagetype" validate:"required,imageType"` + + // Account ID to make the image exclusive + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming + // Should be: + // - eth + // - ens (default value) + // Required: false + NetworkInterfaceNaming string `url:"networkInterfaceNaming,omitempty" json:"networkInterfaceNaming,omitempty" validate:"omitempty,networkInterfaceNaming"` + + // Does this machine supports hot resize + // Required: false + HotResize bool `url:"hotresize,omitempty" json:"hotresize,omitempty"` + + // Optional username for the image + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Optional password for the image + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // Username for upload binary media + // Required: false + UsernameDL string `url:"usernameDL,omitempty" json:"usernameDL,omitempty"` + + // Password for upload binary media + // Required: false + PasswordDL string `url:"passwordDL,omitempty" json:"passwordDL,omitempty"` + + // Storage endpoint provider ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool for image create + // Required: false + Pool string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // Binary architecture of this image + // Should be: + // - X86_64 + // Required: false + Architecture string `url:"architecture,omitempty" json:"architecture,omitempty" validate:"omitempty,imageArchitecture"` + + // List of types of compute suitable for image + // Example: [ "KVM_X86" ] + // Required: true + Drivers []string `url:"drivers" json:"drivers" validate:"min=1,max=2,imageDrivers"` +} + +// Create creates image from a media identified by URL +func (i Image) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/create" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/image/create_virtual.go b/pkg/cloudapi/image/create_virtual.go new file mode 100644 index 0000000..d899357 --- /dev/null +++ b/pkg/cloudapi/image/create_virtual.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateVirtualRequest struct to create virtual image +type CreateVirtualRequest struct { + // Name of the virtual image to create + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of real image to link this virtual image to upon creation + // Required: true + TargetID uint64 `url:"targetId" json:"targetId" validate:"required"` +} + +// CreateVirtual creates virtual image +func (i Image) CreateVirtual(ctx context.Context, req CreateVirtualRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/createVirtual" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/image/delete.go b/pkg/cloudapi/image/delete.go new file mode 100644 index 0000000..a989985 --- /dev/null +++ b/pkg/cloudapi/image/delete.go @@ -0,0 +1,38 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete image +type DeleteRequest struct { + // ID of the image to delete + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` +} + +// Delete deletes image by ID +func (i Image) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/delete" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/image/filter.go b/pkg/cloudapi/image/filter.go new file mode 100644 index 0000000..4367ff7 --- /dev/null +++ b/pkg/cloudapi/image/filter.go @@ -0,0 +1,62 @@ +package image + +// FilterByID returns ListImages with specified ID. +func (li ListImages) FilterByID(id uint64) ListImages { + predicate := func(ii ItemImage) bool { + return ii.ID == id + } + + return li.FilterFunc(predicate) +} + +// FilterByName returns ListImages with specified Name. +func (li ListImages) FilterByName(name string) ListImages { + predicate := func(ii ItemImage) bool { + return ii.Name == name + } + + return li.FilterFunc(predicate) +} + +// FilterByStatus returns ListImages with specified Status. +func (li ListImages) FilterByStatus(status string) ListImages { + predicate := func(ii ItemImage) bool { + return ii.Status == status + } + + return li.FilterFunc(predicate) +} + +// FilterByBootType returns ListImages with specified BootType. +func (li ListImages) FilterByBootType(bootType string) ListImages { + predicate := func(ii ItemImage) bool { + return ii.BootType == bootType + } + + return li.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListImages based on a user-specified predicate. +func (li ListImages) FilterFunc(predicate func(ItemImage) bool) ListImages { + var result ListImages + + for _, item := range li.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemImage +// If none was found, returns an empty struct. +func (li ListImages) FindOne() ItemImage { + if len(li.Data) == 0 { + return ItemImage{} + } + + return li.Data[0] +} diff --git a/pkg/cloudapi/image/filter_test.go b/pkg/cloudapi/image/filter_test.go new file mode 100644 index 0000000..180c735 --- /dev/null +++ b/pkg/cloudapi/image/filter_test.go @@ -0,0 +1,127 @@ +package image + +import "testing" + +var images = ListImages{ + Data: []ItemImage{ + { + AccountID: 0, + Architecture: "X86_64", + BootType: "bios", + Bootable: true, + Description: "", + Drivers: []string{ + "KVM_X86", + }, + HotResize: true, + ID: 9882, + LinkTo: 0, + Name: "u16", + Pool: "vmstor", + Size: 5, + Status: "CREATED", + Type: "linux", + Username: "", + Virtual: false, + }, + { + AccountID: 0, + Architecture: "X86_64", + BootType: "bois", + Bootable: true, + Description: "", + Drivers: []string{ + "KVM_X86", + }, + HotResize: false, + ID: 9884, + LinkTo: 0, + Name: "alpine-virt-3.17", + Pool: "vmstor", + Size: 1, + Status: "CREATED", + Type: "linux", + Username: "", + Virtual: true, + }, + { + AccountID: 1, + Architecture: "X86_64", + BootType: "bios", + Bootable: true, + Description: "", + Drivers: []string{ + "KVM_X86", + }, + HotResize: true, + ID: 9885, + LinkTo: 0, + Name: "test", + Pool: "vmstor", + Size: 4, + Status: "DESTROYED", + Type: "linux", + Username: "", + Virtual: false, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := images.FilterByID(9885).FindOne() + + if actual.ID != 9885 { + t.Fatal("expected ID 9885, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := images.FilterByName("u16").FindOne() + + if actual.Name != "u16" { + t.Fatal("expected Name 'u16', found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := images.FilterByStatus("CREATED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "CREATED" { + t.Fatal("expected Status 'CREATED', found: ", item.Status) + } + } +} + +func TestFilterByBootType(t *testing.T) { + actual := images.FilterByBootType("bios") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.BootType != "bios" { + t.Fatal("expected BootType 'bios', found: ", item.BootType) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := images.FilterFunc(func(ii ItemImage) bool { + return ii.Virtual == true + }) + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + if actual.Data[0].Virtual != true { + t.Fatal("expected Virtual true, found false") + } +} diff --git a/pkg/cloudapi/image/get.go b/pkg/cloudapi/image/get.go new file mode 100644 index 0000000..8ea5f1a --- /dev/null +++ b/pkg/cloudapi/image/get.go @@ -0,0 +1,52 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about image +type GetRequest struct { + // ID of image to get + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // If set to False returns only images in status CREATED + // Required: false + ShowAll bool `url:"showAll,omitempty" json:"showAll,omitempty"` +} + +// Get gets image by ID. +// Returns image as a RecordImage struct if user has rights on it +func (i Image) Get(ctx context.Context, req GetRequest) (*RecordImage, error) { + res, err := i.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordImage{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets image by ID. +// Returns image as an array of bytes if user has rights on it +func (i Image) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/get" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/image/ids.go b/pkg/cloudapi/image/ids.go new file mode 100644 index 0000000..5c0577f --- /dev/null +++ b/pkg/cloudapi/image/ids.go @@ -0,0 +1,19 @@ +package image + +// IDs gets array of ImageIDs from ListImages struct +func (li ListImages) IDs() []uint64 { + res := make([]uint64, 0, len(li.Data)) + for _, i := range li.Data { + res = append(res, i.ID) + } + return res +} + +// IDs gets array of HistoryIDs from ListHistories struct +func (lh ListHistories) IDs() []uint64 { + res := make([]uint64, 0, len(lh)) + for _, h := range lh { + res = append(res, h.ID) + } + return res +} diff --git a/pkg/cloudapi/image/image.go b/pkg/cloudapi/image/image.go new file mode 100644 index 0000000..06469bc --- /dev/null +++ b/pkg/cloudapi/image/image.go @@ -0,0 +1,18 @@ +// Lists all the images. A image is a template which can be used to deploy machines +package image + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to image +type Image struct { + client interfaces.Caller +} + +// Builder for image endpoints +func New(client interfaces.Caller) *Image { + return &Image{ + client, + } +} diff --git a/pkg/cloudapi/image/link.go b/pkg/cloudapi/image/link.go new file mode 100644 index 0000000..caf8934 --- /dev/null +++ b/pkg/cloudapi/image/link.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// LinkRequest struct to link virtual image to another image +type LinkRequest struct { + // ID of the virtual image + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // ID of real image to link this virtual image to + // Required: true + TargetID uint64 `url:"targetId" json:"targetId" validate:"required"` +} + +// Link links virtual image to another image in the platform +func (i Image) Link(ctx context.Context, req LinkRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/link" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/image/list.go b/pkg/cloudapi/image/list.go new file mode 100644 index 0000000..d8bb80c --- /dev/null +++ b/pkg/cloudapi/image/list.go @@ -0,0 +1,107 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of available images +type ListRequest struct { + // Find by storage endpoint provider ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by ID + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by architecture + // Required: false + Architecture string `url:"architecture,omitempty" json:"architecture,omitempty"` + + // Find by type + // Required: false + TypeImage string `url:"typeImage,omitempty" json:"typeImage,omitempty"` + + // Find by image size + // Required: false + ImageSize uint64 `url:"imageSize,omitempty" json:"imageSize,omitempty"` + + // Find by SEP name + // Required: false + SEPName string `url:"sepName,omitempty" json:"sepName,omitempty"` + + // Find by pool + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Find by public True or False + // Required: false + Public interface{} `url:"public,omitempty" json:"public,omitempty" validate:"omitempty,isBool"` + + // Find by hot resize True or False + // Required: false + HotResize interface{} `url:"hotResize,omitempty" json:"hotResize,omitempty" validate:"omitempty,isBool"` + + // Find by bootable True or False + // Required: false + Bootable interface{} `url:"bootable,omitempty" json:"bootable,omitempty" validate:"omitempty,isBool"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by enabled True or False + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` +} + +// List gets list of available images as a ListImages struct, optionally filtering by account ID +func (i Image) List(ctx context.Context, req ListRequest) (*ListImages, error) { + + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListImages{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of available images as an array of bytes +func (i Image) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/list" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/image/models.go b/pkg/cloudapi/image/models.go new file mode 100644 index 0000000..b04df24 --- /dev/null +++ b/pkg/cloudapi/image/models.go @@ -0,0 +1,202 @@ +package image + +// Main information about image +type ItemImage struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Architecture + Architecture string `json:"architecture"` + + // Boot type + BootType string `json:"bootType"` + + // Bootable + Bootable bool `json:"bootable"` + + // CDROM + CDROM bool `json:"cdrom"` + + // Description + Description string `json:"desc"` + + // List drivers + Drivers []string `json:"drivers"` + + // HotResize + HotResize bool `json:"hotResize"` + + // ID + ID uint64 `json:"id"` + + // Link to + LinkTo uint64 `json:"linkTo"` + + // Name + Name string `json:"name"` + + // NetworkInterfaceNaming + NetworkInterfaceNaming string `json:"networkInterfaceNaming"` + + // Pool + Pool string `json:"pool"` + + // SepID + SepID uint64 `json:"sepId"` + + // Size + Size uint64 `json:"size"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // Username + Username string `json:"username"` + + // Virtual + Virtual bool `json:"virtual"` +} + +// List of information about images +type ListImages struct { + // Data + Data []ItemImage `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// ListHistories of record image +type ListHistories []History + +// History of record image +type History struct { + // GUID + GUID string `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Timestamp + Timestamp uint64 `json:"timestamp"` +} + +// Detailed information about image +type RecordImage struct { + // UNCPathj + UNCPath string `json:"UNCPath"` + + // CKey + CKey string `json:"_ckey"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Access Control List + ACL interface{} `json:"acl"` + + // Architecture + Architecture string `json:"architecture"` + + // Boot type + BootType string `json:"bootType"` + + // Bootable + Bootable bool `json:"bootable"` + + // CdPresentedTo + CdPresentedTo interface{} `json:"cdPresentedTo"` + + // ComputeCI ID + ComputeCIID uint64 `json:"computeciId"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // List of drivers + Drivers []string `json:"drivers"` + + // Enabled + Enabled bool `json:"enabled"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // History + History ListHistories `json:"history"` + + // HotResize + HotResize bool `json:"hotResize"` + + // ID + ID uint64 `json:"id"` + + // Last modified + LastModified uint64 `json:"lastModified"` + + // Link to + LinkTo uint64 `json:"linkTo"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // NetworkInterfaceNaming + NetworkInterfaceNaming string `json:"networkInterfaceNaming"` + + // Password + Password string `json:"password"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // ProviderName + ProviderName string `json:"provider_name"` + + // Purge attempts + PurgeAttempts uint64 `json:"purgeAttempts"` + + // Resource ID + ResID string `json:"resId"` + + // RescueCD + RescueCD bool `json:"rescuecd"` + + // SepID + SepID uint64 `json:"sepId"` + + // SharedWith list + SharedWith []uint64 `json:"sharedWith"` + + // Size + Size uint64 `json:"size"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // Username + Username string `json:"username"` + + // Version + Version string `json:"version"` +} diff --git a/pkg/cloudapi/image/rename.go b/pkg/cloudapi/image/rename.go new file mode 100644 index 0000000..5c3c735 --- /dev/null +++ b/pkg/cloudapi/image/rename.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RenameRequest struct to rename image +type RenameRequest struct { + // ID of the virtual image to rename + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // New name + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +// Rename renames image +func (i Image) Rename(ctx context.Context, req RenameRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/rename" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/image/serialize.go b/pkg/cloudapi/image/serialize.go new file mode 100644 index 0000000..718036b --- /dev/null +++ b/pkg/cloudapi/image/serialize.go @@ -0,0 +1,43 @@ +package image + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (li ListImages) Serialize(params ...string) (serialization.Serialized, error) { + if len(li.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(li, prefix, indent) + } + + return json.Marshal(li) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ii ItemImage) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ii, prefix, indent) + } + + return json.Marshal(ii) +} diff --git a/pkg/cloudapi/k8ci.go b/pkg/cloudapi/k8ci.go new file mode 100644 index 0000000..bee6d0c --- /dev/null +++ b/pkg/cloudapi/k8ci.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/k8ci" +) + +// Accessing the K8CI method group +func (ca *CloudAPI) K8CI() *k8ci.K8CI { + return k8ci.New(ca.client) +} diff --git a/pkg/cloudapi/k8ci/filter.go b/pkg/cloudapi/k8ci/filter.go new file mode 100644 index 0000000..67c5db0 --- /dev/null +++ b/pkg/cloudapi/k8ci/filter.go @@ -0,0 +1,44 @@ +package k8ci + +// FilterByID returns ListK8CI with specified ID. +func (lkc ListK8CI) FilterByID(id uint64) ListK8CI { + predicate := func(ikc ItemK8CI) bool { + return ikc.ID == id + } + + return lkc.FilterFunc(predicate) +} + +// FilterByName returns ListK8CI with specified Name. +func (lkc ListK8CI) FilterByName(name string) ListK8CI { + predicate := func(ikc ItemK8CI) bool { + return ikc.Name == name + } + + return lkc.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListK8CI based on a user-specified predicate. +func (lkc ListK8CI) FilterFunc(predicate func(ItemK8CI) bool) ListK8CI { + var result ListK8CI + + for _, item := range lkc.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemK8CI +// If none was found, returns an empty struct. +func (lkc ListK8CI) FindOne() ItemK8CI { + if len(lkc.Data) == 0 { + return ItemK8CI{} + } + + return lkc.Data[0] +} diff --git a/pkg/cloudapi/k8ci/filter_test.go b/pkg/cloudapi/k8ci/filter_test.go new file mode 100644 index 0000000..61fa550 --- /dev/null +++ b/pkg/cloudapi/k8ci/filter_test.go @@ -0,0 +1,90 @@ +package k8ci + +import "testing" + +var k8ciItems = ListK8CI{ + Data: []ItemK8CI{ + { + CreatedTime: 123902139, + Status: "ENABLED", + Description: "", + ID: 1, + Name: "purple_snake", + Version: "1", + LBImageID: 654, + NetworkPlugins: []string{ + "flannel", + "calico", + "weavenet", + }, + }, + { + CreatedTime: 123902232, + Status: "ENABLED", + Description: "", + ID: 2, + Name: "green_giant", + Version: "2", + LBImageID: 654, + NetworkPlugins: []string{ + "flannel", + "calico", + "weavenet", + }, + }, + { + CreatedTime: 123902335, + Status: "DISABLED", + Description: "", + ID: 3, + Name: "magenta_cloud", + Version: "3", + NetworkPlugins: []string{ + "flannel", + "calico", + "weavenet", + }, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := k8ciItems.FilterByID(2).FindOne() + + if actual.ID != 2 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := k8ciItems.FilterByName("magenta_cloud").FindOne() + + if actual.Name != "magenta_cloud" { + t.Fatal("expected Name 'magenta_cloud', found: ", actual.Name) + } +} + +func TestFilterFunc(t *testing.T) { + actual := k8ciItems.FilterFunc(func(ikc ItemK8CI) bool { + return ikc.CreatedTime > 123902139 + }) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedTime < 123902139 { + t.Fatal("expected CreatedTime greater than 123902139, found: ", item.CreatedTime) + } + } +} + +func TestSortingByCreatedTime(t *testing.T) { + actual := k8ciItems.SortByCreatedTime(true) + + if actual.Data[0].CreatedTime != 123902335 && actual.Data[2].CreatedTime != 123902139 { + t.Fatal("expected inverse sort, found normal") + } +} diff --git a/pkg/cloudapi/k8ci/get.go b/pkg/cloudapi/k8ci/get.go new file mode 100644 index 0000000..a961af0 --- /dev/null +++ b/pkg/cloudapi/k8ci/get.go @@ -0,0 +1,45 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about K8CI +type GetRequest struct { + // ID of the K8 catalog item to get + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` +} + +// Get gets details of the specified K8 catalog item as a RecordK8CI struct +func (k K8CI) Get(ctx context.Context, req GetRequest) (*RecordK8CI, error) { + res, err := k.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordK8CI{} + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets details of the specified K8 catalog item as an array of bytes +func (k K8CI) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8ci/get" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/k8ci/ids.go b/pkg/cloudapi/k8ci/ids.go new file mode 100644 index 0000000..608f59e --- /dev/null +++ b/pkg/cloudapi/k8ci/ids.go @@ -0,0 +1,10 @@ +package k8ci + +// IDs gets array of K8CIIDs from ListK8CI struct +func (lk ListK8CI) IDs() []uint64 { + res := make([]uint64, 0, len(lk.Data)) + for _, k := range lk.Data { + res = append(res, k.ID) + } + return res +} diff --git a/pkg/cloudapi/k8ci/k8ci.go b/pkg/cloudapi/k8ci/k8ci.go new file mode 100644 index 0000000..c0093ef --- /dev/null +++ b/pkg/cloudapi/k8ci/k8ci.go @@ -0,0 +1,18 @@ +// API to manage K8CI instances +package k8ci + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to K8CI +type K8CI struct { + client interfaces.Caller +} + +// Builder for K8CI endpoints +func New(client interfaces.Caller) *K8CI { + return &K8CI{ + client, + } +} diff --git a/pkg/cloudapi/k8ci/list.go b/pkg/cloudapi/k8ci/list.go new file mode 100644 index 0000000..5175cad --- /dev/null +++ b/pkg/cloudapi/k8ci/list.go @@ -0,0 +1,83 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of information about images +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by worker driver + // Required: false + WorkerDriver string `url:"workerDriver,omitempty" json:"workerDriver,omitempty"` + + // Find by master driver + // Required: false + MasterDriver string `url:"masterDriver,omitempty" json:"masterDriver,omitempty"` + + // Find by network plugin + // Required: false + NetworkPlugins string `url:"netPlugins,omitempty" json:"netPlugins,omitempty"` + + // List disabled items as well + // Required: false + IncludeDisabled bool `url:"includeDisabled,omitempty" json:"includeDisabled,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all k8ci catalog items available to the current user as a ListK8CI struct +func (k K8CI) List(ctx context.Context, req ListRequest) (*ListK8CI, error) { + + res, err := k.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListK8CI{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all k8ci catalog items available to the current user as an array of bytes +func (k K8CI) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8ci/list" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/k8ci/list_deleted.go b/pkg/cloudapi/k8ci/list_deleted.go new file mode 100644 index 0000000..28cfc11 --- /dev/null +++ b/pkg/cloudapi/k8ci/list_deleted.go @@ -0,0 +1,68 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list information about deleted k8ci items +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"k8cId,omitempty" json:"k8cId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by worker driver + // Required: false + WorkerDriver string `url:"workerDriver,omitempty" json:"workerDriver,omitempty"` + + // Find by master driver + // Required: false + MasterDriver string `url:"masterDriver,omitempty" json:"masterDriver,omitempty"` + + // Find by network plugin + // Required: false + NetworkPlugins string `url:"netPlugins,omitempty" json:"netPlugins,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list all deleted k8ci catalog items available to the current user +func (k K8CI) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListK8CI, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8ci/listDeleted" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListK8CI{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/k8ci/models.go b/pkg/cloudapi/k8ci/models.go new file mode 100644 index 0000000..76b0ce3 --- /dev/null +++ b/pkg/cloudapi/k8ci/models.go @@ -0,0 +1,53 @@ +package k8ci + +// Detailed information about K8CI +type ItemK8CI struct { + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Description + Description string `json:"desc"` + + // ID + ID uint64 `json:"id"` + + // LB image ID + LBImageID uint64 `json:"lbImageId"` + + // Name + Name string `json:"name"` + + // Network plugins + NetworkPlugins []string `json:"networkPlugins"` + + // Status + Status string `json:"status"` + + // Version + Version string `json:"version"` +} + +// List of K8CI +type ListK8CI struct { + Data []ItemK8CI `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// Main information about K8CI +type RecordK8CI struct { + // Description + Description string `json:"desc"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Network plugins + NetworkPlugins []string `json:"networkPlugins"` + + // Version + Version string `json:"version"` +} diff --git a/pkg/cloudapi/k8ci/serialize.go b/pkg/cloudapi/k8ci/serialize.go new file mode 100644 index 0000000..c420ca7 --- /dev/null +++ b/pkg/cloudapi/k8ci/serialize.go @@ -0,0 +1,43 @@ +package k8ci + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lkc ListK8CI) Serialize(params ...string) (serialization.Serialized, error) { + if len(lkc.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lkc, prefix, indent) + } + + return json.Marshal(lkc) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ikc ItemK8CI) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ikc, prefix, indent) + } + + return json.Marshal(ikc) +} diff --git a/pkg/cloudapi/k8ci/sorting.go b/pkg/cloudapi/k8ci/sorting.go new file mode 100644 index 0000000..865c239 --- /dev/null +++ b/pkg/cloudapi/k8ci/sorting.go @@ -0,0 +1,22 @@ +package k8ci + +import "sort" + +// SortByCreatedTime sorts ListK8CI by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lkc ListK8CI) SortByCreatedTime(inverse bool) ListK8CI { + if len(lkc.Data) < 2 { + return lkc + } + + sort.Slice(lkc.Data, func(i, j int) bool { + if inverse { + return lkc.Data[i].CreatedTime > lkc.Data[j].CreatedTime + } + + return lkc.Data[i].CreatedTime < lkc.Data[j].CreatedTime + }) + + return lkc +} diff --git a/pkg/cloudapi/k8s.go b/pkg/cloudapi/k8s.go new file mode 100644 index 0000000..a6e88c3 --- /dev/null +++ b/pkg/cloudapi/k8s.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/k8s" +) + +// Accessing the K8S method group +func (ca *CloudAPI) K8S() *k8s.K8S { + return k8s.New(ca.client) +} diff --git a/pkg/cloudapi/k8s/create.go b/pkg/cloudapi/k8s/create.go new file mode 100644 index 0000000..70f5042 --- /dev/null +++ b/pkg/cloudapi/k8s/create.go @@ -0,0 +1,199 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create kubernetes cluster +type CreateRequest struct { + // Name of Kubernetes cluster + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Resource Group ID for cluster placement + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // ID of Kubernetes catalog item (k8sci) for cluster + // Required: true + K8SCIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` + + // Name for first worker group created with cluster + // Required: true + WorkerGroupName string `url:"workerGroupName" json:"workerGroupName" validate:"required,workerGroupName"` + + // Network plugin + // Must be one of these values: flannel, weavenet, calico + // Required: true + NetworkPlugin string `url:"networkPlugin" json:"networkPlugin" validate:"required,networkPlugin"` + + // ID of SEP to create boot disks for master nodes. Uses images SEP ID if not set + // Required: false + MasterSEPID uint64 `url:"masterSepId,omitempty" json:"masterSepId,omitempty"` + + // Pool to use if master SEP ID is set, can be also empty if needed to be chosen by system + // Required: false + MasterSEPPool string `url:"masterSepPool,omitempty" json:"masterSepPool,omitempty"` + + // ID of SEP to create boot disks for default worker nodes group. Uses images SEP ID if not set + // Required: false + WorkerSEPID uint64 `url:"workerSepId,omitempty" json:"workerSepId,omitempty"` + + // Pool to use if worker SEP ID is set, can be also empty if needed to be chosen by system + // Required: false + WorkerSEPPool string `url:"workerSepPool,omitempty" json:"workerSepPool,omitempty"` + + // List of strings with labels for default worker group + // i.e: ["label1=value1", "label2=value2"] + // Required: false + Labels []string `url:"labels,omitempty" json:"labels,omitempty"` + + // List of strings with taints for default worker group + // i.e: ["key1=value1:NoSchedule", "key2=value2:NoExecute"] + // Required: false + Taints []string `url:"taints,omitempty" json:"taints,omitempty"` + + // List of strings with annotations for worker group + // i.e: ["key1=value1", "key2=value2"] + // Required: false + Annotations []string `url:"annotations,omitempty" json:"annotations,omitempty"` + + // Number of master nodes to create + // Required: false + MasterNum uint `url:"masterNum,omitempty" json:"masterNum,omitempty"` + + // Master node CPU count + // Required: false + MasterCPU uint `url:"masterCpu,omitempty" json:"masterCpu,omitempty"` + + // Master node RAM volume in MB + // Required: false + MasterRAM uint64 `url:"masterRam,omitempty" json:"masterRam,omitempty"` + + // Master node boot disk size in GB If 0 is specified, size is defined by the OS image size + // Required: false + MasterDisk uint `url:"masterDisk,omitempty" json:"masterDisk,omitempty"` + + // Number of worker nodes to create in default worker group + // Required: false + WorkerNum uint `url:"workerNum,omitempty" json:"workerNum,omitempty"` + + // Worker node CPU count + // Required: false + WorkerCPU uint `url:"workerCpu,omitempty" json:"workerCpu,omitempty"` + + // Worker node RAM volume in MB + // Required: false + WorkerRAM uint64 `url:"workerRam,omitempty" json:"workerRam,omitempty"` + + // Worker node boot disk size in GB. If 0 is specified, size is defined by the OS image size + // Required: false + WorkerDisk uint `url:"workerDisk,omitempty" json:"workerDisk,omitempty"` + + // ID of the external network to connect load balancer and cluster ViNS. If 0 is specified, external network selects automatically to + // Required: false + ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"` + + // ID of the ViNS to connect k8s cluster. If nothing is specified, ViNS will be created automatically + // Required: false + VinsId uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Create Kubernetes cluster with masters nodes behind load balancer if true. + // Otherwise give all cluster nodes direct external addresses from selected ExtNet + // Required: false + WithLB bool `url:"withLB" json:"withLB"` + + // Custom sysctl values for Load Balancer instance. Applied on boot + // Required: false + LbSysctlParams []map[string]interface{} `url:"lbSysctlParams,omitempty" json:"lbSysctlParams,omitempty"` + + // Use Highly Available schema for LB deploy + // Required: false + HighlyAvailable bool `url:"highlyAvailableLB,omitempty" json:"highlyAvailableLB,omitempty"` + + // Optional extra Subject Alternative Names (SANs) to use for the API Server serving certificate. Can be both IP addresses and DNS names + // Required: false + AdditionalSANs []string `url:"additionalSANs,omitempty" json:"additionalSANs,omitempty"` + + // Is used to define settings and actions that should be performed before any other component in the cluster starts. + // It allows you to configure things like node registration, network setup, and other initialization tasks. insert a valid JSON string with all levels of nesting + // Required: false + InitConfiguration string `url:"initConfiguration,omitempty" json:"initConfiguration,omitempty"` + + // Is used to define global settings and configurations for the entire cluster. + // It includes parameters such as cluster name, DNS settings, authentication methods, and other cluster-wide configurations. + // Insert a valid JSON string with all levels of nesting + // Required: false + ClusterConfiguration string `url:"clusterConfiguration,omitempty" json:"clusterConfiguration,omitempty"` + + // Is used to configure the behavior and settings of the Kubelet, which is the primary node agent that runs on each node in the cluster. + // It includes parameters such as node IP address, resource allocation, pod eviction policies, and other Kubelet-specific configurations. + // Insert a valid JSON string with all levels of nesting + // Required: false + KubeletConfiguration string `url:"kubeletConfiguration,omitempty" json:"kubeletConfiguration,omitempty"` + + // Is used to configure the behavior and settings of the Kube-proxy, which is responsible for network proxying and load balancing within the cluster. + // It includes parameters such as proxy mode, cluster IP ranges, and other Kube-proxy specific configurations. + // Insert a valid JSON string with all levels of nesting + // Required: false + KubeProxyConfiguration string `url:"kubeProxyConfiguration,omitempty" json:"kubeProxyConfiguration,omitempty"` + + // Is used to configure the behavior and settings for joining a node to a cluster. + // It includes parameters such as the cluster's control plane endpoint, token, and certificate key. insert a valid JSON string with all levels of nesting + // Required: false + JoinConfiguration string `url:"joinConfiguration,omitempty" json:"joinConfiguration,omitempty"` + + // Text description of this Kubernetes cluster + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Meta data for working group computes, format YAML "user_data": 1111 + // Required: false + UserData string `url:"userData,omitempty" json:"userData,omitempty"` + + // Use only selected ExtNet for infrastructure connections + // Required: false + ExtNetOnly bool `url:"extnetOnly,omitempty" json:"extnetOnly,omitempty"` + + // Insert ssl certificate in x509 pem format + // Required: false + OidcCertificate string `url:"oidcCertificate,omitempty" json:"oidcCertificate,omitempty"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` +} + +// GetRAM returns RAM field values +func (r CreateRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 2) + + res["MasterRAM"] = r.MasterRAM + res["WorkerRAM"] = r.WorkerRAM + + return res +} + +// Create creates a new Kubernetes cluster in the specified Resource Group +func (k8s K8S) Create(ctx context.Context, req CreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/create" + + res, err := k8s.client.DecortApiCallMP(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/k8s/delete.go b/pkg/cloudapi/k8s/delete.go new file mode 100644 index 0000000..bb615fd --- /dev/null +++ b/pkg/cloudapi/k8s/delete.go @@ -0,0 +1,43 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete kubernetes cluster +type DeleteRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // True if cluster is destroyed permanently. + // Otherwise it can be restored from Recycle Bin + // Required: true + Permanently bool `url:"permanently" json:"permanently"` +} + +// Delete deletes kubernetes cluster +func (k8s K8S) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/delete" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/delete_master_from_group.go b/pkg/cloudapi/k8s/delete_master_from_group.go new file mode 100644 index 0000000..df0cedf --- /dev/null +++ b/pkg/cloudapi/k8s/delete_master_from_group.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteMasterFromGroupRequest struct to delete master from group +type DeleteMasterFromGroupRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the masters compute group + // Required: true + MasterGroupID uint64 `url:"masterGroupId" json:"masterGroupId" validate:"required"` + + // List of Compute IDs of master nodes to delete + // Required: true + MasterIDs []uint64 `url:"masterIds" json:"masterIds" validate:"min=1"` +} + +// DeleteMasterFromGroup deletes compute from masters group in selected Kubernetes cluster +func (k8s K8S) DeleteMasterFromGroup(ctx context.Context, req DeleteMasterFromGroupRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/deleteMasterFromGroup" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/delete_worker_from_group.go b/pkg/cloudapi/k8s/delete_worker_from_group.go new file mode 100644 index 0000000..deb90a0 --- /dev/null +++ b/pkg/cloudapi/k8s/delete_worker_from_group.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteWorkerFromGroupRequest struct to delete worker from group +type DeleteWorkerFromGroupRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // Compute ID of worker node to delete + // Required: true + WorkerID uint64 `url:"workerId" json:"workerId" validate:"required"` +} + +// DeleteWorkerFromGroup deletes worker compute from workers group in selected Kubernetes cluster +func (k8s K8S) DeleteWorkerFromGroup(ctx context.Context, req DeleteWorkerFromGroupRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/deleteWorkerFromGroup" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/disable_enable.go b/pkg/cloudapi/k8s/disable_enable.go new file mode 100644 index 0000000..9f207ca --- /dev/null +++ b/pkg/cloudapi/k8s/disable_enable.go @@ -0,0 +1,60 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableEnableRequest struct to disable/enable kubernetes cluster +type DisableEnableRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Disable disables kubernetes cluster by ID +func (k8s K8S) Disable(ctx context.Context, req DisableEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/disable" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// Enable enables kubernetes cluster by ID +func (k8s K8S) Enable(ctx context.Context, req DisableEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/enable" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/filter.go b/pkg/cloudapi/k8s/filter.go new file mode 100644 index 0000000..0c64960 --- /dev/null +++ b/pkg/cloudapi/k8s/filter.go @@ -0,0 +1,98 @@ +package k8s + +// FilterByID returns ListK8SClusters with specified ID. +func (lkc ListK8SClusters) FilterByID(id uint64) ListK8SClusters { + predicate := func(ikc ItemK8SCluster) bool { + return ikc.ID == id + } + + return lkc.FilterFunc(predicate) +} + +// FilterByName returns ListK8SClusters with specified Name. +func (lkc ListK8SClusters) FilterByName(name string) ListK8SClusters { + predicate := func(ikc ItemK8SCluster) bool { + return ikc.Name == name + } + + return lkc.FilterFunc(predicate) +} + +// FilterByAccountID returns ListK8SClusters with specified AccountID. +func (lkc ListK8SClusters) FilterByAccountID(accountID uint64) ListK8SClusters { + predicate := func(ikc ItemK8SCluster) bool { + return ikc.AccountID == accountID + } + + return lkc.FilterFunc(predicate) +} + +// FilterByRGID returns ListK8SClusters with specified RGID. +func (lkc ListK8SClusters) FilterByRGID(rgID uint64) ListK8SClusters { + predicate := func(ikc ItemK8SCluster) bool { + return ikc.RGID == rgID + } + + return lkc.FilterFunc(predicate) +} + +// FilterByStatus returns ListK8SClusters with specified Status. +func (lkc ListK8SClusters) FilterByStatus(status string) ListK8SClusters { + predicate := func(ikc ItemK8SCluster) bool { + return ikc.Status == status + } + + return lkc.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListK8SClusters with specified TechStatus. +func (lkc ListK8SClusters) FilterByTechStatus(techStatus string) ListK8SClusters { + predicate := func(ikc ItemK8SCluster) bool { + return ikc.TechStatus == techStatus + } + + return lkc.FilterFunc(predicate) +} + +// FilterByCreatedBy returns ListK8SClusters created by specified user. +func (lkc ListK8SClusters) FilterByCreatedBy(createdBy string) ListK8SClusters { + predicate := func(ikc ItemK8SCluster) bool { + return ikc.CreatedBy == createdBy + } + + return lkc.FilterFunc(predicate) +} + +// FilterByDeletedBy returns ListK8SClusters deleted by specified user. +func (lkc ListK8SClusters) FilterByDeletedBy(deletedBy string) ListK8SClusters { + predicate := func(ikc ItemK8SCluster) bool { + return ikc.DeletedBy == deletedBy + } + + return lkc.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListK8SClusters based on a user-specified predicate. +func (lkc ListK8SClusters) FilterFunc(predicate func(ItemK8SCluster) bool) ListK8SClusters { + var result ListK8SClusters + + for _, item := range lkc.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemK8SCluster +// If none was found, returns an empty struct. +func (lkc ListK8SClusters) FindOne() ItemK8SCluster { + if len(lkc.Data) == 0 { + return ItemK8SCluster{} + } + + return lkc.Data[0] +} diff --git a/pkg/cloudapi/k8s/filter_test.go b/pkg/cloudapi/k8s/filter_test.go new file mode 100644 index 0000000..fa824b2 --- /dev/null +++ b/pkg/cloudapi/k8s/filter_test.go @@ -0,0 +1,190 @@ +package k8s + +import "testing" + +var k8sItems = ListK8SClusters{ + Data: []ItemK8SCluster{ + { + AccountID: 1, + AccountName: "test_1", + ACL: []interface{}{}, + BServiceID: 1, + CIID: 1, + Config: nil, + CreatedBy: "test_user", + CreatedTime: 132454563, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExtNetID: 1, + GID: 0, + GUID: 1, + ID: 1, + LBID: 1, + Milestones: 999999, + Name: "k8s_1", + RGID: 1, + RGName: "rg_1", + Status: "ENABLED", + TechStatus: "STARTED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 0, + }, + { + AccountID: 2, + AccountName: "test_2", + ACL: []interface{}{}, + BServiceID: 2, + CIID: 2, + Config: nil, + CreatedBy: "test_user", + CreatedTime: 132454638, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExtNetID: 2, + GID: 0, + GUID: 2, + ID: 2, + LBID: 2, + Milestones: 999999, + Name: "k8s_2", + RGID: 2, + RGName: "rg_2", + Status: "ENABLED", + TechStatus: "STARTED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 0, + }, + { + AccountID: 3, + AccountName: "test_3", + ACL: []interface{}{}, + BServiceID: 3, + CIID: 3, + Config: nil, + CreatedBy: "test_user", + CreatedTime: 132454682, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExtNetID: 3, + GID: 0, + GUID: 3, + ID: 3, + LBID: 3, + Milestones: 999999, + Name: "k8s_3", + RGID: 3, + RGName: "rg_3", + Status: "DISABLED", + TechStatus: "STOPPED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 0, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := k8sItems.FilterByID(1).FindOne() + + if actual.ID != 1 { + t.Fatal("expected 1 ID, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := k8sItems.FilterByName("k8s_3").FindOne() + + if actual.Name != "k8s_3" { + t.Fatal("expected Name 'k8s_3', found: ", actual.Name) + } +} + +func TestFilterByAccountID(t *testing.T) { + actual := k8sItems.FilterByAccountID(2).FindOne() + + if actual.AccountID != 2 { + t.Fatal("expected AccountID 2, found: ", actual.AccountID) + } +} + +func TestFilterByRGID(t *testing.T) { + actual := k8sItems.FilterByRGID(3).FindOne() + + if actual.RGID != 3 { + t.Fatal("expected RGID 3, found: ", actual.RGID) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := k8sItems.FilterByStatus("ENABLED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterByTechStatus(t *testing.T) { + actual := k8sItems.FilterByTechStatus("STARTED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.TechStatus != "STARTED" { + t.Fatal("expected TechStatus 'STARTED', found: ", item.TechStatus) + } + } +} + +func TestFilterByCreatedBy(t *testing.T) { + actual := k8sItems.FilterByCreatedBy("test_user") + + if len(actual.Data) != 3 { + t.Fatal("expected 3 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedBy != "test_user" { + t.Fatal("expected CreatedBy 'test_user', found: ", item.CreatedBy) + } + } +} + +func TestFilterByDeletedBy(t *testing.T) { + actual := k8sItems.FilterByDeletedBy("test_user") + + if len(actual.Data) != 0 { + t.Fatal("expected 0 found, actual: ", len(actual.Data)) + } +} + +func TestFilterFunc(t *testing.T) { + actual := k8sItems.FilterFunc(func(iks ItemK8SCluster) bool { + return iks.AccountName == "test_2" + }). + FindOne() + + if actual.AccountName != "test_2" { + t.Fatal("expected AccountName 'test_2', found: ", actual.AccountName) + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := k8sItems.SortByCreatedTime(false) + + if actual.Data[0].CreatedTime != 132454563 || actual.Data[2].CreatedTime != 132454682 { + t.Fatal("expected ascending sort, seems to be inversed") + } +} diff --git a/pkg/cloudapi/k8s/find_group_by_label.go b/pkg/cloudapi/k8s/find_group_by_label.go new file mode 100644 index 0000000..3e8c515 --- /dev/null +++ b/pkg/cloudapi/k8s/find_group_by_label.go @@ -0,0 +1,49 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FindGroupByLabelRequest struct to get information about group of kubernetes cluster +type FindGroupByLabelRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // List of labels to search + // Required: true + Labels []string `url:"labels" json:"labels" validate:"min=1"` + + // If true and more than one label provided, select only groups that have all provided labels. + // If false - groups that have at least one label + // Required: false + Strict bool `url:"strict,omitempty" json:"strict,omitempty"` +} + +// FindGroupByLabel find worker group information by one on more labels +func (k8s K8S) FindGroupByLabel(ctx context.Context, req FindGroupByLabelRequest) (ListK8SGroups, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/findGroupByLabel" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListK8SGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/k8s/get.go b/pkg/cloudapi/k8s/get.go new file mode 100644 index 0000000..91f71b9 --- /dev/null +++ b/pkg/cloudapi/k8s/get.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about kubernetes cluster +type GetRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Get gets information about Kubernetes cluster as a RecordK8S struct +func (k8s K8S) Get(ctx context.Context, req GetRequest) (*RecordK8S, error) { + res, err := k8s.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordK8S{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about Kubernetes cluster as an array of bytes +func (k8s K8S) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/get" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/k8s/get_config.go b/pkg/cloudapi/k8s/get_config.go new file mode 100644 index 0000000..7088e7b --- /dev/null +++ b/pkg/cloudapi/k8s/get_config.go @@ -0,0 +1,32 @@ +package k8s + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetConfigRequest struct to get configuration of kubernetes cluster +type GetConfigRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// GetConfig gets configuration data to access Kubernetes cluster +func (k8s K8S) GetConfig(ctx context.Context, req GetConfigRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/getConfig" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/k8s/get_node_annotations.go b/pkg/cloudapi/k8s/get_node_annotations.go new file mode 100644 index 0000000..8f2a259 --- /dev/null +++ b/pkg/cloudapi/k8s/get_node_annotations.go @@ -0,0 +1,36 @@ +package k8s + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetNodeAnnotationsRequest struct to get node annotations +type GetNodeAnnotationsRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Node ID + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// GetNodeAnnotations gets kubernetes cluster worker node annotations +func (k8s K8S) GetNodeAnnotations(ctx context.Context, req GetNodeAnnotationsRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/getNodeAnnotations" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/k8s/get_node_labels.go b/pkg/cloudapi/k8s/get_node_labels.go new file mode 100644 index 0000000..a58b795 --- /dev/null +++ b/pkg/cloudapi/k8s/get_node_labels.go @@ -0,0 +1,36 @@ +package k8s + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetNodeLabelsRequest struct to get node labels +type GetNodeLabelsRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Node ID + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// GetNodeLabels gets kubernetes cluster worker node labels +func (k8s K8S) GetNodeLabels(ctx context.Context, req GetNodeLabelsRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/getNodeLabels" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/k8s/get_node_taints.go b/pkg/cloudapi/k8s/get_node_taints.go new file mode 100644 index 0000000..12faacb --- /dev/null +++ b/pkg/cloudapi/k8s/get_node_taints.go @@ -0,0 +1,36 @@ +package k8s + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetNodeTaintsRequest struct to get node taints +type GetNodeTaintsRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Node ID + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// GetNodeTaints gets kubernetes cluster worker node taints +func (k8s K8S) GetNodeTaints(ctx context.Context, req GetNodeTaintsRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/getNodeTaints" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/k8s/get_worker_nodes_meta_data.go b/pkg/cloudapi/k8s/get_worker_nodes_meta_data.go new file mode 100644 index 0000000..39560ea --- /dev/null +++ b/pkg/cloudapi/k8s/get_worker_nodes_meta_data.go @@ -0,0 +1,36 @@ +package k8s + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetWorkerNodesMetaDataRequest struct to get worker group metadata by ID +type GetWorkerNodesMetaDataRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` +} + +// GetWorkerNodesMetaData gets worker group metadata by ID +func (k K8S) GetWorkerNodesMetaData(ctx context.Context, req GetWorkerNodesMetaDataRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/getWorkerNodesMetaData" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/k8s/ids.go b/pkg/cloudapi/k8s/ids.go new file mode 100644 index 0000000..bcb569f --- /dev/null +++ b/pkg/cloudapi/k8s/ids.go @@ -0,0 +1,28 @@ +package k8s + +// IDs gets array of K8SIDs from ListK8SClusters struct +func (lk ListK8SClusters) IDs() []uint64 { + res := make([]uint64, 0, len(lk.Data)) + for _, k := range lk.Data { + res = append(res, k.ID) + } + return res +} + +// IDs gets array of K8SWorkerGroupIDs from ListK8SGroups struct +func (lwg ListK8SGroups) IDs() []uint64 { + res := make([]uint64, 0, len(lwg)) + for _, wg := range lwg { + res = append(res, wg.ID) + } + return res +} + +// IDs gets array of Worker or Master ComputesIDs from ListDetailedInfo struct +func (ldi ListDetailedInfo) IDs() []uint64 { + res := make([]uint64, 0, len(ldi)) + for _, di := range ldi { + res = append(res, di.ID) + } + return res +} diff --git a/pkg/cloudapi/k8s/k8s.go b/pkg/cloudapi/k8s/k8s.go new file mode 100644 index 0000000..d5770e0 --- /dev/null +++ b/pkg/cloudapi/k8s/k8s.go @@ -0,0 +1,18 @@ +// API for Kubernetes clusters management +package k8s + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to K8S +type K8S struct { + client interfaces.Caller +} + +// Builder for K8S endpoints +func New(client interfaces.Caller) *K8S { + return &K8S{ + client, + } +} diff --git a/pkg/cloudapi/k8s/list.go b/pkg/cloudapi/k8s/list.go new file mode 100644 index 0000000..5cc62a7 --- /dev/null +++ b/pkg/cloudapi/k8s/list.go @@ -0,0 +1,91 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list information K8S +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by IP address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by lbId + // Required: false + LBID uint64 `url:"lbId,omitempty" json:"lbId,omitempty"` + + // Find by basicServiceId + // Required: false + BasicServiceID uint64 `url:"basicServiceId,omitempty" json:"basicServiceId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by techStatus + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Include deleted clusters in result + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all kubernetes clusters the user has access to as a ListK8SClusters +func (k8s K8S) List(ctx context.Context, req ListRequest) (*ListK8SClusters, error) { + + res, err := k8s.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListK8SClusters{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all kubernetes clusters the user has access to as an array of bytes +func (k8s K8S) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/list" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/k8s/list_deleted.go b/pkg/cloudapi/k8s/list_deleted.go new file mode 100644 index 0000000..f2a8028 --- /dev/null +++ b/pkg/cloudapi/k8s/list_deleted.go @@ -0,0 +1,76 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted kubernetes cluster +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by IP address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by lbId + // Required: false + LBID uint64 `url:"lbId,omitempty" json:"lbId,omitempty"` + + // Find by basicServiceId + // Required: false + BasicServiceID uint64 `url:"basicServiceId,omitempty" json:"basicServiceId,omitempty"` + + // Find by techStatus + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets all deleted kubernetes clusters the user has access to +func (k8s K8S) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListK8SClusters, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/listDeleted" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListK8SClusters{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/k8s/models.go b/pkg/cloudapi/k8s/models.go new file mode 100644 index 0000000..d6df394 --- /dev/null +++ b/pkg/cloudapi/k8s/models.go @@ -0,0 +1,326 @@ +package k8s + +// Main information about kubernetes cluster +type ItemK8SGroup struct { + // List of Annotations + Annotations []string `json:"annotations"` + + // Number of CPU + CPU uint64 `json:"cpu"` + + // List detailed information + DetailedInfo ListDetailedInfo `json:"detailedInfo"` + + // Disk ID + Disk uint64 `json:"disk"` + + // GUID + GUID string `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // List of Labels + Labels []string `json:"labels"` + + // Name + Name string `json:"name"` + + // Num + Num uint64 `json:"num"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // List of taints + Taints []string `json:"taints"` +} + +// List kubernetes cluster groups +type ListK8SGroups []ItemK8SGroup + +// Detailed information +type ItemDetailedInfo struct { + // Externalip + Externalip string `json:"externalip"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` +} + +// List detailed information +type ListDetailedInfo []ItemDetailedInfo + +// Deteal information about kubernetes cluster +type RecordK8S struct { + // Access Control List + ACL RecordACL `json:"ACL"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Basic Service ID + BServiceID uint64 `json:"bserviceId"` + + // CIID + CIID uint64 `json:"ciId"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Only external network + ExtnetOnly bool `json:"extnetOnly"` + + // Highly available LB + HighlyAvailableLB bool `json:"highlyAvailableLB"` + + // Address Virtual Internet Protocol + AddressVIP K8SAddressVIP `json:"addressVip"` + + // ID + ID uint64 `json:"id"` + + // K8CI name + K8CIName string `json:"k8ciName"` + + // Kubernetes cluster groups information + K8SGroups RecordK8SGroups `json:"k8sGroups"` + + // Load balancer ID + LBID uint64 `json:"lbId"` + + // Name + Name string `json:"name"` + + // Network plugin + NetworkPlugin string `json:"networkPlugin"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // With LB + WithLB bool `json:"withLB"` +} + +// Detailed information about address of the Virtual Internet Protocol +type K8SAddressVIP struct { + // Backend IP + BackendIP string `json:"backendIp"` + + // Frontend IP + FrontendIP string `json:"frontendIp"` +} + +// Detailed information about kubernetes cluster groups +type RecordK8SGroups struct { + + // Master information + Masters MasterGroup `json:"masters"` + + // Worker information + Workers ListK8SGroups `json:"workers"` +} + +// Master group information +type MasterGroup struct { + // Number of CPU + CPU uint64 `json:"cpu"` + + // Detailed information + DetailedInfo ListDetailedInfo `json:"detailedInfo"` + + // Disk ID + Disk uint64 `json:"disk"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Num + Num uint64 `json:"num"` + + // Number of RAM + RAM uint64 `json:"ram"` +} + +// Access Control List +type RecordACL struct { + // Account ACL + AccountACL ListACL `json:"accountAcl"` + + // K8S ACL + K8SACL ListACL `json:"k8sAcl"` + + // RG ACL + RGACL ListACL `json:"rgAcl"` +} + +// Main information of Access Control List +type ItemACL struct { + // Explicit + Explicit bool `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Right + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// List of ACL +type ListACL []ItemACL + +// Main information about kubernetes cluster +type ItemK8SCluster struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control List + ACL []interface{} `json:"acl"` + + // Basic Service ID + BServiceID uint64 `json:"bserviceId"` + + // CIID + CIID uint64 `json:"ciId"` + + // Config + Config interface{} `json:"config"` + + // Create by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // External network ID + ExtNetID uint64 `json:"extnetId"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Load balancer ID + LBID uint64 `json:"lbId"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network plugin + NetworkPlugin string `json:"networkPlugin"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Information about service account + ServiceAccount RecordServiceAccount `json:"serviceAccount"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // VINS ID + VINSID uint64 `json:"vinsId"` + + // List workers group + WorkersGroup ListK8SGroups `json:"workersGroups"` +} + +// Information about service account +type RecordServiceAccount struct { + // GUID + GUID string `json:"guid"` + + // Password + Password string `json:"password"` + + // Username + Username string `json:"username"` +} + +// List of kubernetes clusters +type ListK8SClusters struct { + // Data + Data []ItemK8SCluster `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/k8s/restore.go b/pkg/cloudapi/k8s/restore.go new file mode 100644 index 0000000..c07a4b7 --- /dev/null +++ b/pkg/cloudapi/k8s/restore.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore kubernetes cluster +type RestoreRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Restore restores kubernetes cluster from Recycle Bin +func (k8s K8S) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/restore" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/serialize.go b/pkg/cloudapi/k8s/serialize.go new file mode 100644 index 0000000..0ad7dfa --- /dev/null +++ b/pkg/cloudapi/k8s/serialize.go @@ -0,0 +1,43 @@ +package k8s + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lkc ListK8SClusters) Serialize(params ...string) (serialization.Serialized, error) { + if len(lkc.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lkc, prefix, indent) + } + + return json.Marshal(lkc) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ikc ItemK8SCluster) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ikc, prefix, indent) + } + + return json.Marshal(ikc) +} diff --git a/pkg/cloudapi/k8s/sorting.go b/pkg/cloudapi/k8s/sorting.go new file mode 100644 index 0000000..9103137 --- /dev/null +++ b/pkg/cloudapi/k8s/sorting.go @@ -0,0 +1,60 @@ +package k8s + +import "sort" + +// SortByCreatedTime sorts ListK8SClusters by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lkc ListK8SClusters) SortByCreatedTime(inverse bool) ListK8SClusters { + if len(lkc.Data) < 2 { + return lkc + } + + sort.Slice(lkc.Data, func(i, j int) bool { + if inverse { + return lkc.Data[i].CreatedTime > lkc.Data[j].CreatedTime + } + + return lkc.Data[i].CreatedTime < lkc.Data[j].CreatedTime + }) + + return lkc +} + +// SortByUpdatedTime sorts ListK8SClusters by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lkc ListK8SClusters) SortByUpdatedTime(inverse bool) ListK8SClusters { + if len(lkc.Data) < 2 { + return lkc + } + + sort.Slice(lkc.Data, func(i, j int) bool { + if inverse { + return lkc.Data[i].UpdatedTime > lkc.Data[j].UpdatedTime + } + + return lkc.Data[i].UpdatedTime < lkc.Data[j].UpdatedTime + }) + + return lkc +} + +// SortByDeletedTime sorts ListK8SClusters by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lkc ListK8SClusters) SortByDeletedTime(inverse bool) ListK8SClusters { + if len(lkc.Data) < 2 { + return lkc + } + + sort.Slice(lkc.Data, func(i, j int) bool { + if inverse { + return lkc.Data[i].DeletedTime > lkc.Data[j].DeletedTime + } + + return lkc.Data[i].DeletedTime < lkc.Data[j].DeletedTime + }) + + return lkc +} diff --git a/pkg/cloudapi/k8s/start.go b/pkg/cloudapi/k8s/start.go new file mode 100644 index 0000000..7fdca52 --- /dev/null +++ b/pkg/cloudapi/k8s/start.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StartRequest struct to start kubernetes cluster +type StartRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Start starts kubernetes cluster by ID +func (k8s K8S) Start(ctx context.Context, req StartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/start" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/stop.go b/pkg/cloudapi/k8s/stop.go new file mode 100644 index 0000000..a93c5e0 --- /dev/null +++ b/pkg/cloudapi/k8s/stop.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StopRequest struct to stop kubernetes cluster +type StopRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Stop stops kubernetes cluster by ID +func (k8s K8S) Stop(ctx context.Context, req StopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/stop" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/update.go b/pkg/cloudapi/k8s/update.go new file mode 100644 index 0000000..881753d --- /dev/null +++ b/pkg/cloudapi/k8s/update.go @@ -0,0 +1,48 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update kubernetes cluster +type UpdateRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // New name to set. + // If empty string is passed, name is not updated + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New description to set. + // If empty string is passed, description is not updated + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// Update updates name or description of Kubernetes cluster +func (k8s K8S) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/update" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/update_worker_nodes_meta_data.go b/pkg/cloudapi/k8s/update_worker_nodes_meta_data.go new file mode 100644 index 0000000..e428646 --- /dev/null +++ b/pkg/cloudapi/k8s/update_worker_nodes_meta_data.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateWorkerNodesMetaDataRequest struct to add worker to a kubernetes cluster +type UpdateWorkerNodesMetaDataRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // Meta data for working group computes, format YAML "user_data": 1111 + // Required: true + UserData string `url:"userData" json:"userData" validate:"required"` +} + +// UpdateWorkerNodesMetaData adds worker nodes to a kubernetes cluster +func (k K8S) UpdateWorkerNodesMetaData(ctx context.Context, req UpdateWorkerNodesMetaDataRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/updateWorkerNodesMetaData" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/worker_add.go b/pkg/cloudapi/k8s/worker_add.go new file mode 100644 index 0000000..cab07d4 --- /dev/null +++ b/pkg/cloudapi/k8s/worker_add.go @@ -0,0 +1,52 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkerAddRequest struct to add worker to a kubernetes cluster +type WorkerAddRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // How many worker nodes to add + // Required: false + Num uint64 `url:"num,omitempty" json:"num,omitempty"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` +} + +// WorkerAdd adds worker nodes to a Kubernetes cluster +func (k8s K8S) WorkerAdd(ctx context.Context, req WorkerAddRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/workerAdd" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + result := make([]uint64, 0) + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/worker_reset.go b/pkg/cloudapi/k8s/worker_reset.go new file mode 100644 index 0000000..03b1b7f --- /dev/null +++ b/pkg/cloudapi/k8s/worker_reset.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkerResetRequest struct for hard reset kubernetes cluster +type WorkerResetRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // Compute ID of worker node to reset + // Required: true + WorkerID uint64 `url:"workerId" json:"workerId" validate:"required"` +} + +// WorkerReset hard reset (compute start + stop) worker node of the Kubernetes cluster +func (k8s K8S) WorkerReset(ctx context.Context, req WorkerResetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/workerReset" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/worker_restart.go b/pkg/cloudapi/k8s/worker_restart.go new file mode 100644 index 0000000..b3af975 --- /dev/null +++ b/pkg/cloudapi/k8s/worker_restart.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkerRestartRequest struct to restart worker node +type WorkerRestartRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // Compute ID of worker node to restart + // Required: true + WorkerID uint64 `url:"workerId" json:"workerId" validate:"required"` +} + +// WorkerRestart soft restart (reboot OS) worker node of the Kubernetes cluster +func (k8s K8S) WorkerRestart(ctx context.Context, req WorkerRestartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/workerRestart" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/workers_group_add.go b/pkg/cloudapi/k8s/workers_group_add.go new file mode 100644 index 0000000..08cb832 --- /dev/null +++ b/pkg/cloudapi/k8s/workers_group_add.go @@ -0,0 +1,93 @@ +package k8s + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkersGroupAddRequest struct to add workers group +type WorkersGroupAddRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Worker group name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of SEP to create boot disks for default worker nodes group. Uses images SEP ID if not set + // Required: false + WorkerSEPID uint64 `url:"workerSepId,omitempty" json:"workerSepId,omitempty"` + + // Pool to use if worker SEP ID is set, can be also empty if needed to be chosen by system + // Required: false + WorkerSEPPool string `url:"workerSepPool,omitempty" json:"workerSepPool,omitempty"` + + // List of strings with labels for worker group + // i.e: ["label1=value1", "label2=value2"] + // Required: false + Labels []string `url:"labels,omitempty" json:"labels,omitempty"` + + // List of strings with taints for worker group + // i.e: ["key1=value1:NoSchedule", "key2=value2:NoExecute"] + // Required: false + Taints []string `url:"taints,omitempty" json:"taints,omitempty"` + + // List of strings with annotations for worker group + // i.e: ["key1=value1", "key2=value2"] + // Required: false + Annotations []string `url:"annotations,omitempty" json:"annotations,omitempty"` + + // Number of worker nodes to create + // Required: false + WorkerNum uint64 `url:"workerNum,omitempty" json:"workerNum,omitempty"` + + // Worker node CPU count + // Required: false + WorkerCPU uint64 `url:"workerCpu,omitempty" json:"workerCpu,omitempty"` + + // Worker node RAM volume in MB + // Required: false + WorkerRAM uint64 `url:"workerRam,omitempty" json:"workerRam,omitempty"` + + // Worker node boot disk size in GB If 0 is specified, size is defined by the OS image size + // Required: false + WorkerDisk uint64 `url:"workerDisk,omitempty" json:"workerDisk,omitempty"` + + // Meta data for working group computes, format YAML "user_data": 1111 + // Required: false + UserData string `url:"userData,omitempty" json:"userData,omitempty"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` +} + +// GetRAM returns RAM field values +func (r WorkersGroupAddRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["WorkerRAM"] = r.WorkerRAM + + return res +} + +// WorkersGroupAdd adds workers group to Kubernetes cluster +func (k8s K8S) WorkersGroupAdd(ctx context.Context, req WorkersGroupAddRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/workersGroupAdd" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/k8s/workers_group_delete.go b/pkg/cloudapi/k8s/workers_group_delete.go new file mode 100644 index 0000000..2e3bb83 --- /dev/null +++ b/pkg/cloudapi/k8s/workers_group_delete.go @@ -0,0 +1,42 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkersGroupDeleteRequest struct to delete workers group +type WorkersGroupDeleteRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Worker group ID + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` +} + +// WorkersGroupDelete deletes workers group from Kubernetes cluster +func (k8s K8S) WorkersGroupDelete(ctx context.Context, req WorkersGroupDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/workersGroupDelete" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/k8s/workers_group_get_by_name.go b/pkg/cloudapi/k8s/workers_group_get_by_name.go new file mode 100644 index 0000000..f27bf76 --- /dev/null +++ b/pkg/cloudapi/k8s/workers_group_get_by_name.go @@ -0,0 +1,44 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkersGroupGetByNameRequest struct to get information about worker group +type WorkersGroupGetByNameRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Worker group name + // Required: true + GroupName string `url:"groupName" json:"groupName" validate:"required"` +} + +// WorkersGroupGetByName gets worker group metadata by name +func (k8s K8S) WorkersGroupGetByName(ctx context.Context, req WorkersGroupGetByNameRequest) (*ItemK8SGroup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/workersGroupGetByName" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := ItemK8SGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/kvmx86.go b/pkg/cloudapi/kvmx86.go new file mode 100644 index 0000000..a61f89c --- /dev/null +++ b/pkg/cloudapi/kvmx86.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/kvmx86" +) + +// Accessing the KVMX86 method group +func (ca *CloudAPI) KVMX86() *kvmx86.KVMX86 { + return kvmx86.New(ca.client) +} diff --git a/pkg/cloudapi/kvmx86/create.go b/pkg/cloudapi/kvmx86/create.go new file mode 100644 index 0000000..480a3b0 --- /dev/null +++ b/pkg/cloudapi/kvmx86/create.go @@ -0,0 +1,243 @@ +package kvmx86 + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type Interface struct { + // Network type + // Should be one of: + // - VINS + // - EXTNET + // - VFNIC + // - DPDK + NetType string `url:"netType" json:"netType" validate:"required,kvmx86NetType"` + + // Network ID for connect to, + // for EXTNET - external network ID, + // for VINS - VINS ID, + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // IP address to assign to this VM when connecting to the specified network + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // Maximum transmission unit, must be 1-9216 + // Used only to DPDK net type + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` +} + +// DataDisk detailed struct for DataDisks field in CreateRequest and CreateBlankRequest +type DataDisk struct { + // Name for disk + // Required: true + DiskName string `url:"diskName" json:"diskName" validate:"required"` + + // Disk size in GB + // Required: true + Size uint64 `url:"size" json:"size" validate:"required"` + + // Storage endpoint provider ID + // By default the same with boot disk + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool name + // By default will be chosen automatically + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Optional description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Specify image id for create disk from template + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` +} + +// CreateRequest struct to create KVM x86 VM +type CreateRequest struct { + // ID of the resource group, which will own this VM + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of this VM. + // Must be unique among all VMs (including those in DELETED state) in target resource group + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Number CPUs to allocate to this VM + // Required: true + CPU uint64 `url:"cpu" json:"cpu" validate:"required"` + + // Volume of RAM in MB to allocate to this VM + // Required: true + RAM uint64 `url:"ram" json:"ram" validate:"required"` + + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + + // ID of the OS image to base this VM on; + // Could be boot disk image or CD-ROM image + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` + + // Size of the boot disk in GB + // Required: false + BootDisk uint64 `url:"bootDisk,omitempty" json:"bootDisk,omitempty"` + + // ID of SEP to create boot disk on. + // Uses images SEP ID if not set + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool to use if sepId is set, can be also empty if needed to be chosen by system + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Slice of structs with data disk description. Each disk has parameters: required - diskName, size; optional - sepId, pool, desc and imageId. + // If not specified, compute will be created without disks. + // To create compute without disks, pass initialized empty slice . + // Required: false + DataDisks []DataDisk `url:"-" json:"dataDisks,omitempty" validate:"omitempty,dive"` + + // Slice of structs with net interface description. + // If not specified, compute will be created with default interface from RG. + // To create compute without interfaces, pass initialized empty slice . + // Required: false + Interfaces []Interface `url:"-" json:"interfaces,omitempty" validate:"omitempty,dive"` + + // Input data for cloud-init facility + // Required: false + Userdata string `url:"userdata,omitempty" json:"userdata,omitempty"` + + // Text description of this VM + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Start VM upon success + // Required: false + Start bool `url:"start" json:"start"` + + // System name + // Required: false + IS string `url:"IS,omitempty" json:"IS,omitempty"` + + // Compute purpose + // Required: false + IPAType string `url:"ipaType,omitempty" json:"ipaType,omitempty"` + + // Custom fields for compute. Must be a dict + // Required: false + CustomFields string `url:"customFields,omitempty" json:"customFields,omitempty"` + + // Type of compute Stateful (KVM_X86) or Stateless (SVA_KVM_X86) + // Required: false + Driver string `url:"driver,omitempty" json:"driver,omitempty" validate:"omitempty,computeDriver"` + + // Rule for VM placement with NUMA affinity. + // Possible values - none (placement without NUMA affinity), + // strict (strictly with NUMA affinity, if not possible - do not start VM), + // loose (use NUMA affinity if possible) + // Required: false + // Default: none + NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"` + + // Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node + // Required: false + // Default: false + CPUPin bool `url:"cpupin" json:"cpupin"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node + // Required: false + // Default: false + HPBacked bool `url:"hpBacked" json:"hpBacked"` +} + +// GetRAM returns RAM field values +func (r CreateRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +type wrapperCreateRequest struct { + CreateRequest + Interfaces []string `url:"interfaces,omitempty"` + DataDisks []string `url:"dataDisks,omitempty"` +} + +// Create creates KVM x86 VM based on specified OS image +func (k KVMX86) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var interfaces []string + + if req.Interfaces != nil && len(req.Interfaces) != 0 { + interfaces = make([]string, 0, len(req.Interfaces)) + + for i := range req.Interfaces { + b, err := json.Marshal(req.Interfaces[i]) + if err != nil { + return 0, err + } + + interfaces = append(interfaces, string(b)) + } + } else if req.Interfaces != nil && len(req.Interfaces) == 0 { + interfaces = []string{"[]"} + } + + var dataDisks []string + + if req.DataDisks != nil && len(req.DataDisks) != 0 { + dataDisks = make([]string, 0, len(req.DataDisks)) + + for i := range req.DataDisks { + b, err := json.Marshal(req.DataDisks[i]) + if err != nil { + return 0, err + } + + dataDisks = append(dataDisks, string(b)) + } + } + + reqWrapped := wrapperCreateRequest{ + CreateRequest: req, + Interfaces: interfaces, + DataDisks: dataDisks, + } + + url := "/cloudapi/kvmx86/create" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/kvmx86/create_blank.go b/pkg/cloudapi/kvmx86/create_blank.go new file mode 100644 index 0000000..bb729f2 --- /dev/null +++ b/pkg/cloudapi/kvmx86/create_blank.go @@ -0,0 +1,147 @@ +package kvmx86 + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateBlankRequest struct to create KVM x86 VM from scratch +type CreateBlankRequest struct { + // ID of the resource group, which will own this VM + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of this VM. + // Must be unique among all VMs (including those in DELETED state) in target resource group + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Number CPUs to allocate to this VM + // Required: true + CPU uint64 `url:"cpu" json:"cpu" validate:"required"` + + // Volume of RAM in MB to allocate to this VM + // Required: true + RAM uint64 `url:"ram" json:"ram" validate:"required"` + + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + + // Size of the boot disk in GB + // Required: false + BootDisk uint64 `url:"bootDisk,omitempty" json:"bootDisk,omitempty"` + + // ID of SEP to create boot disk on. + // Uses images SEP ID if not set + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool to use if sepId is set, can be also empty if needed to be chosen by system + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Slice of structs with data disk description. Each disk has parameters: required - diskName, size; optional - sepId, pool, desc and imageId. + // If not specified, compute will be created without disks. + // To create compute without disks, pass initialized empty slice . + // Required: false + DataDisks []DataDisk `url:"-" json:"dataDisks,omitempty" validate:"omitempty,dive"` + + // Slice of structs with net interface description. + // If not specified, compute will be created with default interface from RG. + // To create compute without interfaces, pass initialized empty slice . + // Required: false + Interfaces []Interface `url:"-" json:"interfaces,omitempty" validate:"omitempty,dive"` + + // Type of compute Stateful (KVM_X86) or Stateless (SVA_KVM_X86) + // Required: false + Driver string `url:"driver,omitempty" json:"driver,omitempty" validate:"omitempty,computeDriver"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Text description of this VM + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// GetRAM returns RAM field values +func (r CreateBlankRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +type wrapperCreateBlankRequest struct { + CreateBlankRequest + Interfaces []string `url:"interfaces,omitempty"` + DataDisks []string `url:"dataDisks,omitempty"` +} + +// CreateBlank creates KVM x86 VM from scratch +func (k KVMX86) CreateBlank(ctx context.Context, req CreateBlankRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var interfaces []string + + if req.Interfaces != nil && len(req.Interfaces) != 0 { + interfaces = make([]string, 0, len(req.Interfaces)) + + for i := range req.Interfaces { + b, err := json.Marshal(req.Interfaces[i]) + if err != nil { + return 0, err + } + + interfaces = append(interfaces, string(b)) + } + } else if req.Interfaces != nil && len(req.Interfaces) == 0 { + interfaces = []string{"[]"} + } + + var dataDisks []string + + if req.DataDisks != nil && len(req.DataDisks) != 0 { + dataDisks = make([]string, 0, len(req.DataDisks)) + + for i := range req.DataDisks { + b, err := json.Marshal(req.DataDisks[i]) + if err != nil { + return 0, err + } + + dataDisks = append(dataDisks, string(b)) + } + } + + reqWrapped := wrapperCreateBlankRequest{ + CreateBlankRequest: req, + Interfaces: interfaces, + DataDisks: dataDisks, + } + + url := "/cloudapi/kvmx86/createBlank" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/kvmx86/kvmx86.go b/pkg/cloudapi/kvmx86/kvmx86.go new file mode 100644 index 0000000..7868d20 --- /dev/null +++ b/pkg/cloudapi/kvmx86/kvmx86.go @@ -0,0 +1,18 @@ +// API to manage KVM x86 compute instances (x86 VMs) +package kvmx86 + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to KVMX86 +type KVMX86 struct { + client interfaces.Caller +} + +// Builder for KVMX86 endpoints +func New(client interfaces.Caller) *KVMX86 { + return &KVMX86{ + client, + } +} diff --git a/pkg/cloudapi/lb.go b/pkg/cloudapi/lb.go new file mode 100644 index 0000000..2fbe33e --- /dev/null +++ b/pkg/cloudapi/lb.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/lb" + +// Accessing the LB method group +func (ca *CloudAPI) LB() *lb.LB { + return lb.New(ca.client) +} diff --git a/pkg/cloudapi/lb/backend_create.go b/pkg/cloudapi/lb/backend_create.go new file mode 100644 index 0000000..d23c351 --- /dev/null +++ b/pkg/cloudapi/lb/backend_create.go @@ -0,0 +1,90 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendCreateRequest struct to create backend +type BackendCreateRequest struct { + // ID of the load balancer instance to backendCreate + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must be unique among all backends of this load balancer - name of the new backend to create + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Algorithm + // Should be one of: + // - roundrobin + // - static-rr + // - leastconn + // Required: false + Algorithm string `url:"algorithm,omitempty" json:"algorithm,omitempty" validate:"omitempty,lbAlgorithm"` + + // Interval in milliseconds between two consecutive availability + // checks of the server that is considered available + // Required: false + Inter uint64 `url:"inter,omitempty" json:"inter,omitempty"` + + // Interval in milliseconds between two consecutive checks to + // restore the availability of a server that is currently considered unavailable + // Required: false + DownInter uint64 `url:"downinter,omitempty" json:"downinter,omitempty"` + + // Number of checks that the server must pass in order to get the available status + // and be included in the balancing scheme again + // Required: false + Rise uint64 `url:"rise,omitempty" json:"rise,omitempty"` + + // Number of consecutive failed availability checks, + // after which the previously considered available server receives the status of + // unavailable and is temporarily excluded from the balancing scheme + // Required: false + Fall uint64 `url:"fall,omitempty" json:"fall,omitempty"` + + // Interval in milliseconds from the moment the server receives the available status, + // after which the number of actually allowed connections to this server will be returned to 100% of the set limit + // Required: false + SlowStart uint64 `url:"slowstart,omitempty" json:"slowstart,omitempty"` + + // Limit of simultaneous connections to the server. When this limit is reached, + // the server is temporarily excluded from the balancing scheme + // Required: false + MaxConn uint64 `url:"maxconn,omitempty" json:"maxconn,omitempty"` + + // Limit of connections waiting in the queue. + // When this limit is reached, all subsequent connections will be forwarded to other servers + // Required: false + MaxQueue uint64 `url:"maxqueue,omitempty" json:"maxqueue,omitempty"` + + // Server weight for use in weight balancing algorithms + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` +} + +// BackendCreate creates new backend on the specified load balancer +func (l LB) BackendCreate(ctx context.Context, req BackendCreateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/backendCreate" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/backend_delete.go b/pkg/cloudapi/lb/backend_delete.go new file mode 100644 index 0000000..113978c --- /dev/null +++ b/pkg/cloudapi/lb/backend_delete.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendDeleteRequest struct to delete backend +type BackendDeleteRequest struct { + // ID of the load balancer instance to BackendDelete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Cannot be emtpy string - name of the backend to delete + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` +} + +// BackendDelete deletes backend from the specified load balancer. +// Warning: you cannot undo this action! +func (l LB) BackendDelete(ctx context.Context, req BackendDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/backendDelete" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/backend_server_add.go b/pkg/cloudapi/lb/backend_server_add.go new file mode 100644 index 0000000..bfbe557 --- /dev/null +++ b/pkg/cloudapi/lb/backend_server_add.go @@ -0,0 +1,95 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendServerAddRequest struct to add server definition to the backend +type BackendServerAddRequest struct { + // ID of the load balancer instance to BackendServerAdd + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must match one of the existing backens - name of the backend to add servers to + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Must be unique among all servers defined for this backend - name of the server definition to add + // Required: true + ServerName string `url:"serverName" json:"serverName" validate:"required"` + + // IP address of the server + // Required: true + Address string `url:"address" json:"address" validate:"required"` + + // Port number on the server + // Required: true + Port uint64 `url:"port" json:"port" validate:"required"` + + // Set to disabled if this server should be used regardless of its state + // Required: false + Check string `url:"check,omitempty" json:"check,omitempty"` + + // Interval in milliseconds between two consecutive availability checks of the server that is considered available + // Required: false + Inter uint64 `url:"inter,omitempty" json:"inter,omitempty"` + + // Interval in milliseconds between two consecutive checks to restore + // the availability of a server that is currently considered unavailable + // Required: false + DownInter uint64 `url:"downinter,omitempty" json:"downinter,omitempty"` + + // Number of checks that the server must pass in order to get + // the available status and be included in the balancing scheme again + // Required: false + Rise uint64 `url:"rise,omitempty" json:"rise,omitempty"` + + // Number of consecutive failed availability checks, + // after which the previously considered available server receives the status of unavailable and + // is temporarily excluded from the balancing scheme + // Required: false + Fall uint64 `url:"fall,omitempty" json:"fall,omitempty"` + + // Interval in milliseconds from the moment the server receives the available status, + // after which the number of actually allowed connections to this server will be returned to 100% of the set limit + // Required: false + SlowStart uint64 `url:"slowstart,omitempty" json:"slowstart,omitempty"` + + // Limit of simultaneous connections to the server. When this limit is reached, the server is temporarily excluded from the balancing scheme + // Required: false + MaxConn uint64 `url:"maxconn,omitempty" json:"maxconn,omitempty"` + + // Limit of connections waiting in the queue. When this limit is reached, all subsequent connections will be forwarded to other servers + // Required: false + MaxQueue uint64 `url:"maxqueue,omitempty" json:"maxqueue,omitempty"` + + // Server weight for use in weight balancing algorithms + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` +} + +// BackendServerAdd adds server definition to the backend on the specified load balancer +func (l LB) BackendServerAdd(ctx context.Context, req BackendServerAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/backendServerAdd" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/backend_server_delete.go b/pkg/cloudapi/lb/backend_server_delete.go new file mode 100644 index 0000000..0231146 --- /dev/null +++ b/pkg/cloudapi/lb/backend_server_delete.go @@ -0,0 +1,47 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendServerDeleteRequest struct to delete server definition +type BackendServerDeleteRequest struct { + // ID of the load balancer instance to BackendServerDelete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must match one of the existing backends - name of the backend to add servers to + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Must be unique among all servers defined for this backend - name of the server definition to add + // Required: true + ServerName string `url:"serverName" json:"serverName" validate:"required"` +} + +// BackendServerDelete deletes server definition from the backend on the specified load balancer. +// Warning: you cannot undo this action! +func (l LB) BackendServerDelete(ctx context.Context, req BackendServerDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/backendServerDelete" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/backend_server_update.go b/pkg/cloudapi/lb/backend_server_update.go new file mode 100644 index 0000000..2f867f1 --- /dev/null +++ b/pkg/cloudapi/lb/backend_server_update.go @@ -0,0 +1,95 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendServerUpdateRequest struct to update server +type BackendServerUpdateRequest struct { + // ID of the load balancer instance to BackendServerAdd + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must match one of the existing backends - name of the backend to add servers to + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Must be unique among all servers defined for this backend - name of the server definition to add + // Required: true + ServerName string `url:"serverName" json:"serverName" validate:"required"` + + // IP address of the server + // Required: true + Address string `url:"address" json:"address" validate:"required"` + + // Port number on the server + // Required: true + Port uint64 `url:"port" json:"port" validate:"required"` + + // Set to disabled if this server should be used regardless of its state + // Required: false + Check string `url:"check,omitempty" json:"check,omitempty"` + + // Interval in milliseconds between two consecutive availability checks of the server that is considered available + // Required: false + Inter uint64 `url:"inter,omitempty" json:"inter,omitempty"` + + // Interval in milliseconds between two consecutive checks to restore + // the availability of a server that is currently considered unavailable + // Required: false + DownInter uint64 `url:"downinter,omitempty" json:"downinter,omitempty"` + + // Number of checks that the server must pass in order to get + // the available status and be included in the balancing scheme again + // Required: false + Rise uint64 `url:"rise,omitempty" json:"rise,omitempty"` + + // Number of consecutive failed availability checks, + // after which the previously considered available server receives the status of unavailable and + // is temporarily excluded from the balancing scheme + // Required: false + Fall uint64 `url:"fall,omitempty" json:"fall,omitempty"` + + // Interval in milliseconds from the moment the server receives the available status, + // after which the number of actually allowed connections to this server will be returned to 100% of the set limit + // Required: false + SlowStart uint64 `url:"slowstart,omitempty" json:"slowstart,omitempty"` + + // Limit of simultaneous connections to the server. When this limit is reached, the server is temporarily excluded from the balancing scheme + // Required: false + MaxConn uint64 `url:"maxconn,omitempty" json:"maxconn,omitempty"` + + // Limit of connections waiting in the queue. When this limit is reached, all subsequent connections will be forwarded to other servers + // Required: false + MaxQueue uint64 `url:"maxqueue,omitempty" json:"maxqueue,omitempty"` + + // Server weight for use in weight balancing algorithms + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` +} + +// BackendServerUpdate updates server definition on the backend of load balancer +func (l LB) BackendServerUpdate(ctx context.Context, req BackendServerUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/backendServerUpdate" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/backend_update.go b/pkg/cloudapi/lb/backend_update.go new file mode 100644 index 0000000..84b997c --- /dev/null +++ b/pkg/cloudapi/lb/backend_update.go @@ -0,0 +1,90 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendUpdateRequest struct to update backend +type BackendUpdateRequest struct { + // ID of the load balancer instance to backendCreate + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must be unique among all backends of this load balancer - name of the new backend to create + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Algorithm + // Should be one of: + // - roundrobin + // - static-rr + // - leastconn + // Required: false + Algorithm string `url:"algorithm,omitempty" json:"algorithm,omitempty" validate:"omitempty,lbAlgorithm"` + + // Interval in milliseconds between two consecutive availability + // checks of the server that is considered available + // Required: false + Inter uint64 `url:"inter,omitempty" json:"inter,omitempty"` + + // Interval in milliseconds between two consecutive checks to + // restore the availability of a server that is currently considered unavailable + // Required: false + DownInter uint64 `url:"downinter,omitempty" json:"downinter,omitempty"` + + // Number of checks that the server must pass in order to get the available status + // and be included in the balancing scheme again + // Required: false + Rise uint64 `url:"rise,omitempty" json:"rise,omitempty"` + + // Number of consecutive failed availability checks, + // after which the previously considered available server receives the status of + // unavailable and is temporarily excluded from the balancing scheme + // Required: false + Fall uint64 `url:"fall,omitempty" json:"fall,omitempty"` + + // Interval in milliseconds from the moment the server receives the available status, + // after which the number of actually allowed connections to this server will be returned to 100% of the set limit + // Required: false + SlowStart uint64 `url:"slowstart,omitempty" json:"slowstart,omitempty"` + + // Limit of simultaneous connections to the server. When this limit is reached, + // the server is temporarily excluded from the balancing scheme + // Required: false + MaxConn uint64 `url:"maxconn,omitempty" json:"maxconn,omitempty"` + + // Limit of connections waiting in the queue. + // When this limit is reached, all subsequent connections will be forwarded to other servers + // Required: false + MaxQueue uint64 `url:"maxqueue,omitempty" json:"maxqueue,omitempty"` + + // Server weight for use in weight balancing algorithms + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` +} + +// BackendUpdate updates existing backend on the specified load balancer. Note that backend name cannot be changed +func (l LB) BackendUpdate(ctx context.Context, req BackendUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/backendUpdate" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/config_reset.go b/pkg/cloudapi/lb/config_reset.go new file mode 100644 index 0000000..48e25be --- /dev/null +++ b/pkg/cloudapi/lb/config_reset.go @@ -0,0 +1,39 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ConfigResetRequest struct for reset config +type ConfigResetRequest struct { + // ID of the load balancer instance to ConfigReset + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// ConfigReset reset current software configuration of the specified load balancer. +// Warning: this action cannot be undone! +func (l LB) ConfigReset(ctx context.Context, req ConfigResetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/configReset" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/create.go b/pkg/cloudapi/lb/create.go new file mode 100644 index 0000000..a25009f --- /dev/null +++ b/pkg/cloudapi/lb/create.go @@ -0,0 +1,98 @@ +package lb + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create load balancer +type CreateRequest struct { + // ID of the resource group where this load balancer instance will be located + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of the load balancer. + // Must be unique among all load balancers in this Resource Group + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // External network to connect this load balancer to + // Required: true, can be 0 + ExtNetID uint64 `url:"extnetId" json:"extnetId"` + + // Internal network (VINS) to connect this load balancer to + // Required: true, can be 0 + VINSID uint64 `url:"vinsId" json:"vinsId"` + + // Custom sysctl values for Load Balancer instance. Applied on boot + // Required: false + SysctlParams []map[string]interface{} `url:"-" json:"sysctlParams,omitempty" validate:"omitempty,dive"` + + // Use Highly Available schema for LB deploy + // Required: false + HighlyAvailable bool `url:"highlyAvailable,omitempty" json:"highlyAvailable,omitempty"` + + // Start now Load balancer + // Required: false + Start bool `url:"start" json:"start"` + + // Text description of this load balancer + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +type wrapperCreateRequest struct { + CreateRequest + Params []string `url:"sysctlParams,omitempty"` +} + +// Create method will create a new load balancer instance +func (l LB) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + if req.ExtNetID == 0 && req.VINSID == 0 { + return 0, errors.New("vinsId and extNetId cannot be both in the value 0") + } + + var params []string + + if len(req.SysctlParams) != 0 { + params = make([]string, 0, len(req.SysctlParams)) + for _, m := range req.SysctlParams { + encodeStr, err := json.Marshal(m) + if err != nil { + return 0, err + } + params = append(params, string(encodeStr)) + } + } else { + params = []string{} + } + + reqWrapped := wrapperCreateRequest{ + CreateRequest: req, + Params: params, + } + + url := "/cloudapi/lb/create" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/delete.go b/pkg/cloudapi/lb/delete.go new file mode 100644 index 0000000..ed3bbcf --- /dev/null +++ b/pkg/cloudapi/lb/delete.go @@ -0,0 +1,42 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete load balancer +type DeleteRequest struct { + // ID of the load balancer instance to delete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Set to true to delete load balancer immediately bypassing recycle bin + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes specified load balancer +func (l LB) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/delete" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/disable_enable.go b/pkg/cloudapi/lb/disable_enable.go new file mode 100644 index 0000000..dc42aeb --- /dev/null +++ b/pkg/cloudapi/lb/disable_enable.go @@ -0,0 +1,60 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableEnableRequest struct for disable/enable load balancer +type DisableEnableRequest struct { + // ID of the load balancer instance to disable/enable + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Disable disables specified load balancer instance +func (l LB) Disable(ctx context.Context, req DisableEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/disable" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// Enable enables specified load balancer instance +func (l LB) Enable(ctx context.Context, req DisableEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/enable" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/filter.go b/pkg/cloudapi/lb/filter.go new file mode 100644 index 0000000..062734a --- /dev/null +++ b/pkg/cloudapi/lb/filter.go @@ -0,0 +1,91 @@ +package lb + +import ( + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/k8s" +) + +// FilterByID returns ListLB with specified ID. +func (ll ListLB) FilterByID(id uint64) ListLB { + predicate := func(ill ItemLoadBalancer) bool { + return ill.ID == id + } + + return ll.FilterFunc(predicate) +} + +// FilterByName returns ListLB with specified Name. +func (ll ListLB) FilterByName(name string) ListLB { + predicate := func(ill ItemLoadBalancer) bool { + return ill.Name == name + } + + return ll.FilterFunc(predicate) +} + +// FilterByExtNetID returns ListLB with specified ExtNetID. +func (ll ListLB) FilterByExtNetID(extNetID int64) ListLB { + predicate := func(ill ItemLoadBalancer) bool { + return ill.ExtNetID == extNetID + } + + return ll.FilterFunc(predicate) +} + +// FilterByImageID returns ListLB with specified ImageID. +func (ll ListLB) FilterByImageID(imageID uint64) ListLB { + predicate := func(ill ItemLoadBalancer) bool { + return ill.ImageID == imageID + } + + return ll.FilterFunc(predicate) +} + +// FilterByK8SID returns ListLB used by specified K8S cluster. +func (ll ListLB) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListLB, error) { + caller := k8s.New(decortClient) + + req := k8s.GetRequest{ + K8SID: k8sID, + } + + cluster, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + predicate := func(ill ItemLoadBalancer) bool { + return cluster.LBID == ill.ID + } + + result := ll.FilterFunc(predicate) + + return &result, nil +} + +// FilterFunc allows filtering ListLB based on a user-specified predicate. +func (ll ListLB) FilterFunc(predicate func(ItemLoadBalancer) bool) ListLB { + var result ListLB + + for _, item := range ll.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(ll.Data)) + + return result +} + +// FindOne returns first found ItemLoadBalancer +// If none was found, returns an empty struct. +func (ll ListLB) FindOne() ItemLoadBalancer { + if len(ll.Data) == 0 { + return ItemLoadBalancer{} + } + + return ll.Data[0] +} diff --git a/pkg/cloudapi/lb/filter_test.go b/pkg/cloudapi/lb/filter_test.go new file mode 100644 index 0000000..38ce508 --- /dev/null +++ b/pkg/cloudapi/lb/filter_test.go @@ -0,0 +1,148 @@ +package lb + +import "testing" + +var lbs = ListLB{ + Data: []ItemLoadBalancer{ + { + DPAPIPassword: "0000", + RecordLB: RecordLB{ + HAMode: true, + ACL: []interface{}{}, + Backends: []ItemBackend{}, + CreatedBy: "test_user_1", + CreatedTime: 1636667448, + DeletedBy: "", + DeletedTime: 0, + Description: "", + DPAPIUser: "api_user", + ExtNetID: 2522, + Frontends: []ItemFrontend{}, + GID: 212, + GUID: 1, + ID: 1, + ImageID: 2121, + Milestones: 129000, + Name: "k8s-lb-test-1", + RGID: 25090, + RGName: "", + Status: "ENABLED", + TechStatus: "STARTED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 101, + }, + }, + { + DPAPIPassword: "0000", + RecordLB: RecordLB{ + HAMode: false, + ACL: []interface{}{}, + Backends: []ItemBackend{}, + CreatedBy: "test_user_2", + CreatedTime: 1636667506, + DeletedBy: "", + DeletedTime: 0, + Description: "", + DPAPIUser: "api_user_2", + ExtNetID: 2524, + Frontends: []ItemFrontend{}, + GID: 212, + GUID: 2, + ID: 2, + ImageID: 2129, + Milestones: 129013, + Name: "k8s-lb-test-2", + RGID: 25092, + RGName: "", + Status: "ENABLED", + TechStatus: "STOPPED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 102, + }, + }, + { + DPAPIPassword: "0000", + RecordLB: RecordLB{ + HAMode: true, + ACL: []interface{}{}, + Backends: []ItemBackend{}, + CreatedBy: "te2t_user_3", + CreatedTime: 1636667534, + DeletedBy: "", + DeletedTime: 0, + Description: "", + DPAPIUser: "api_user_3", + ExtNetID: 2536, + Frontends: []ItemFrontend{}, + GID: 212, + GUID: 3, + ID: 3, + ImageID: 2139, + Milestones: 129025, + Name: "k8s-lb-test-3", + RGID: 25106, + RGName: "", + Status: "DISABLED", + TechStatus: "STOPPED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 118, + }, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := lbs.FilterByID(2).FindOne() + + if actual.ID != 2 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := lbs.FilterByName("k8s-lb-test-3").FindOne() + + if actual.Name != "k8s-lb-test-3" { + t.Fatal("expected Name 'k8s-lb-test-3', found: ", actual.Name) + } +} + +func TestFilterByExtNetID(t *testing.T) { + actual := lbs.FilterByExtNetID(2522).FindOne() + + if actual.ExtNetID != 2522 { + t.Fatal("expected ExtNetID 2522, found: ", actual.ExtNetID) + } +} + +func TestFilterByImageID(t *testing.T) { + actual := lbs.FilterByImageID(2139).FindOne() + + if actual.ImageID != 2139 { + t.Fatal("expected ImageID 2139, found: ", actual.ImageID) + } +} + +func TestFilterFunc(t *testing.T) { + actual := lbs.FilterFunc(func(rl ItemLoadBalancer) bool { + return rl.Status == "DISABLED" + }) + + for _, item := range actual.Data { + if item.Status != "DISABLED" { + t.Fatal("expected Status 'DISABLED', found: ", item.Status) + } + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := lbs.SortByCreatedTime(true) + + if actual.Data[0].CreatedTime != 1636667534 || actual.Data[2].CreatedTime != 1636667448 { + t.Fatal("expected descending order, found ascending") + } +} diff --git a/pkg/cloudapi/lb/frontend_bind.go b/pkg/cloudapi/lb/frontend_bind.go new file mode 100644 index 0000000..271ab8f --- /dev/null +++ b/pkg/cloudapi/lb/frontend_bind.go @@ -0,0 +1,54 @@ +package lb + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendBindRequest struct for frontend bind +type FrontendBindRequest struct { + // ID of the load balancer instance to FrontendBind + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Name of the frontend to update + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` + + // Name of the binding to update + // Required: true + BindingName string `url:"bindingName" json:"bindingName" validate:"required"` + + // If specified must be within the IP range of either Ext Net or ViNS, + // where this load balancer is connected - new IP address to use for this binding. + // If omitted, current IP address is retained + // Required: true + BindingAddress string `url:"bindingAddress" json:"bindingAddress" validate:"required"` + + // New port number to use for this binding. + // If omitted, current port number is retained + // Required: true + BindingPort uint64 `url:"bindingPort" json:"bindingPort" validate:"required"` +} + +// FrontendBind bind frontend from specified load balancer instance +func (l LB) FrontendBind(ctx context.Context, req FrontendBindRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/frontendBind" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/lb/frontend_bind_delete.go b/pkg/cloudapi/lb/frontend_bind_delete.go new file mode 100644 index 0000000..fb29206 --- /dev/null +++ b/pkg/cloudapi/lb/frontend_bind_delete.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendBindDeleteRequest struct to delete bind +type FrontendBindDeleteRequest struct { + // ID of the load balancer instance to FrontendBindDelete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Name of the frontend to delete + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` + + // Name of the binding to delete + // Required: true + BindingName string `url:"bindingName" json:"bindingName" validate:"required"` +} + +// FrontendBindDelete deletes binding from the specified load balancer frontend +func (l LB) FrontendBindDelete(ctx context.Context, req FrontendBindDeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/frontendBindDelete" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/lb/frontend_bind_update.go b/pkg/cloudapi/lb/frontend_bind_update.go new file mode 100644 index 0000000..49b39d7 --- /dev/null +++ b/pkg/cloudapi/lb/frontend_bind_update.go @@ -0,0 +1,54 @@ +package lb + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendBindUpdateRequest struct to update binding +type FrontendBindUpdateRequest struct { + // ID of the load balancer instance to FrontendBindUpdate + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Name of the frontend to update + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` + + // Name of the binding to update + // Required: true + BindingName string `url:"bindingName" json:"bindingName" validate:"required"` + + // If specified must be within the IP range of either Ext Net or ViNS, + // where this load balancer is connected - new IP address to use for this binding. + // If omitted, current IP address is retained + // Required: true + BindingAddress string `url:"bindingAddress" json:"bindingAddress" validate:"required"` + + // New port number to use for this binding. + // If omitted, current port number is retained + // Required: true + BindingPort uint64 `url:"bindingPort" json:"bindingPort" validate:"required"` +} + +// FrontendBindUpdate updates binding for the specified load balancer frontend +func (l LB) FrontendBindUpdate(ctx context.Context, req FrontendBindUpdateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/frontendBindingUpdate" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudapi/lb/frontend_create.go b/pkg/cloudapi/lb/frontend_create.go new file mode 100644 index 0000000..cb49f67 --- /dev/null +++ b/pkg/cloudapi/lb/frontend_create.go @@ -0,0 +1,48 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendCreateRequest struct to create frontend +type FrontendCreateRequest struct { + // ID of the load balancer instance to FrontendCreate + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must be unique among all frontends of + // this load balancer - name of the new frontend to create + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` + + // Should be one of the backends existing on + // this load balancer - name of the backend to use + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` +} + +// FrontendCreate creates new frontend on the specified load balancer +func (l LB) FrontendCreate(ctx context.Context, req FrontendCreateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/frontendCreate" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/frontend_delete.go b/pkg/cloudapi/lb/frontend_delete.go new file mode 100644 index 0000000..5082c74 --- /dev/null +++ b/pkg/cloudapi/lb/frontend_delete.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendDeleteRequest struct to delete frontend +type FrontendDeleteRequest struct { + // ID of the load balancer instance to FrontendDelete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Name of the frontend to delete + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` +} + +// FrontendDelete deletes frontend from the specified load balancer. +// Warning: you cannot undo this action! +func (l LB) FrontendDelete(ctx context.Context, req FrontendDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/frontendDelete" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/get.go b/pkg/cloudapi/lb/get.go new file mode 100644 index 0000000..6b4c66e --- /dev/null +++ b/pkg/cloudapi/lb/get.go @@ -0,0 +1,46 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about load balancer +type GetRequest struct { + // ID of the load balancer to get details for + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Get gets detailed information about load balancer as a RecordLB struct +func (l LB) Get(ctx context.Context, req GetRequest) (*RecordLB, error) { + res, err := l.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordLB{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed information about load balancer as an array of bytes +func (l LB) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/get" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/lb/ids.go b/pkg/cloudapi/lb/ids.go new file mode 100644 index 0000000..a610613 --- /dev/null +++ b/pkg/cloudapi/lb/ids.go @@ -0,0 +1,10 @@ +package lb + +// IDs gets array of LBIDs from ListLB struct +func (llb ListLB) IDs() []uint64 { + res := make([]uint64, 0, len(llb.Data)) + for _, lb := range llb.Data { + res = append(res, lb.ID) + } + return res +} diff --git a/pkg/cloudapi/lb/lb.go b/pkg/cloudapi/lb/lb.go new file mode 100644 index 0000000..992b322 --- /dev/null +++ b/pkg/cloudapi/lb/lb.go @@ -0,0 +1,18 @@ +// API to manage load balancer instance +package lb + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to load balancer +type LB struct { + client interfaces.Caller +} + +// Builder for load balancer +func New(client interfaces.Caller) *LB { + return &LB{ + client, + } +} diff --git a/pkg/cloudapi/lb/list.go b/pkg/cloudapi/lb/list.go new file mode 100644 index 0000000..38bbfc3 --- /dev/null +++ b/pkg/cloudapi/lb/list.go @@ -0,0 +1,91 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of load balancers +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by frontend Ip + // Required: false + FrontIP string `url:"frontIp,omitempty" json:"frontIp,omitempty"` + + // Find by backend Ip + // Required: false + BackIP string `url:"backIp,omitempty" json:"backIp,omitempty"` + + // Included deleted load balancers + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all load balancers as a ListLB struct +func (l LB) List(ctx context.Context, req ListRequest) (*ListLB, error) { + + res, err := l.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListLB{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all load balancers as an array of bytes +func (l LB) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/list" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/lb/list_deleted.go b/pkg/cloudapi/lb/list_deleted.go new file mode 100644 index 0000000..46c1065 --- /dev/null +++ b/pkg/cloudapi/lb/list_deleted.go @@ -0,0 +1,76 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted load balancers +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by frontend Ip + // Required: false + FrontIP string `url:"frontIp,omitempty" json:"frontIp,omitempty"` + + // Find by backend Ip + // Required: false + BackIP string `url:"backIp,omitempty" json:"backIp,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: true + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list of deleted load balancers +func (l LB) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListLB, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/listDeleted" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListLB{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/lb/make_highly_available.go b/pkg/cloudapi/lb/make_highly_available.go new file mode 100644 index 0000000..495852a --- /dev/null +++ b/pkg/cloudapi/lb/make_highly_available.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// HighlyAvailableRequest struct to make Load Balancer Highly available +type HighlyAvailableRequest struct { + // ID of the LB instance + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// HighlyAvailable makes load balancer highly available +func (l LB) HighlyAvailable(ctx context.Context, req HighlyAvailableRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/makeHighlyAvailable" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/models.go b/pkg/cloudapi/lb/models.go new file mode 100644 index 0000000..519348e --- /dev/null +++ b/pkg/cloudapi/lb/models.go @@ -0,0 +1,253 @@ +package lb + +// Detailed information about load balancer +type RecordLB struct { + //HAMode + HAMode bool `json:"HAmode"` + + // Access Control List + ACL interface{} `json:"acl"` + + // BackendHAIP + BackendHAIP string `json:"backendHAIP"` + + // List of load balancer backends + Backends ListBackends `json:"backends"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // DPAPIUser + DPAPIUser string `json:"dpApiUser"` + + // External network ID + ExtNetID int64 `json:"extnetId"` + + // FrontendHAIP + FrontendHAIP string `json:"frontendHAIP"` + + // List of load balancer frontends + Frontends ListFrontends `json:"frontends"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // ManagerId + ManagerId uint64 `json:"managerId"` + + // ManagerType + ManagerType string `json:"managerType"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Part K8s + PartK8s bool `json:"partK8s"` + + // Primary node + PrimaryNode RecordNode `json:"primaryNode"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Secondary node + SecondaryNode RecordNode `json:"secondaryNode"` + + // Status + Status string `json:"status"` + + // Sysctl Params + SysctlParams interface{} `json:"sysctlParams"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // UserManaged + UserManaged bool `json:"userManaged"` + + // VINS ID + VINSID uint64 `json:"vinsId"` +} + +// Main information about load balancer +type ItemLoadBalancer struct { + // DPAPIPassword + DPAPIPassword string `json:"dpApiPassword"` + // Detailed information about load balancer + RecordLB +} + +// List of load balancers +type ListLB struct { + // Data + Data []ItemLoadBalancer `json:"data"` + + // EntryCount + EntryCount uint64 `json:"entryCount"` +} + +// Main information about backend +type ItemBackend struct { + // Algorithm + Algorithm string `json:"algorithm"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Server settings + ServerDefaultSettings RecordServerSettings `json:"serverDefaultSettings"` + + // List of servers + Servers ListServers `json:"servers"` +} + +// List of backends +type ListBackends []ItemBackend + +// Server settings +type RecordServerSettings struct { + // Inter + Inter uint64 `json:"inter"` + + // GUID + GUID string `json:"guid"` + + // DownInter + DownInter uint64 `json:"downinter"` + + // Rise + Rise uint64 `json:"rise"` + + // Fall + Fall uint64 `json:"fall"` + + // SlowStart + SlowStart uint64 `json:"slowstart"` + + // Max connections + MaxConn uint64 `json:"maxconn"` + + // Max queue + MaxQueue uint64 `json:"maxqueue"` + + // Weight + Weight uint64 `json:"weight"` +} + +// Main information about server +type ItemServer struct { + + // Address + Address string `json:"address"` + + // Check + Check string `json:"check"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Port + Port uint64 `json:"port"` + + // Server settings + ServerSettings RecordServerSettings `json:"serverSettings"` +} + +// List of servers +type ListServers []ItemServer + +// Main information about node +type RecordNode struct { + // Backend IP + BackendIP string `json:"backendIp"` + + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Frontend IP + FrontendIP string `json:"frontendIp"` + + // GUID + GUID string `json:"guid"` + + // MGMTIP + MGMTIP string `json:"mgmtIp"` + + // Network ID + NetworkID uint64 `json:"networkId"` +} + +// Main information about frontend +type ItemFrontend struct { + // Backend + Backend string `json:"backend"` + + // List of bindings + Bindings ListBindings `json:"bindings"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` +} + +// List of frontends +type ListFrontends []ItemFrontend + +// Main information about bindings +type ItemBinding struct { + // Address + Address string `json:"address"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Port + Port uint64 `json:"port"` +} + +// List of bindings +type ListBindings []ItemBinding diff --git a/pkg/cloudapi/lb/restart.go b/pkg/cloudapi/lb/restart.go new file mode 100644 index 0000000..4db4674 --- /dev/null +++ b/pkg/cloudapi/lb/restart.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestartRequest struct to restart load balancer +type RestartRequest struct { + // ID of the load balancer instance to restart + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // restart secondary and primary nodes sequentially in HA mode + // Default is true + // Required: false + Safe bool `url:"safe" json:"safe"` +} + +// Restart restarts specified load balancer instance +func (l LB) Restart(ctx context.Context, req RestartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/restart" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/restore.go b/pkg/cloudapi/lb/restore.go new file mode 100644 index 0000000..5ce3313 --- /dev/null +++ b/pkg/cloudapi/lb/restore.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore load balancer +type RestoreRequest struct { + // ID of the load balancer instance to restore + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Restore restores load balancer from recycle bin +func (l LB) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/restore" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/serialize.go b/pkg/cloudapi/lb/serialize.go new file mode 100644 index 0000000..5ba76f5 --- /dev/null +++ b/pkg/cloudapi/lb/serialize.go @@ -0,0 +1,43 @@ +package lb + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ll ListLB) Serialize(params ...string) (serialization.Serialized, error) { + if len(ll.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ll, prefix, indent) + } + + return json.Marshal(ll) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ill ItemLoadBalancer) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ill, prefix, indent) + } + + return json.Marshal(ill) +} diff --git a/pkg/cloudapi/lb/sorting.go b/pkg/cloudapi/lb/sorting.go new file mode 100644 index 0000000..addadeb --- /dev/null +++ b/pkg/cloudapi/lb/sorting.go @@ -0,0 +1,60 @@ +package lb + +import "sort" + +// SortByCreatedTime sorts ListLB by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ll ListLB) SortByCreatedTime(inverse bool) ListLB { + if len(ll.Data) < 2 { + return ll + } + + sort.Slice(ll.Data, func(i, j int) bool { + if inverse { + return ll.Data[i].CreatedTime > ll.Data[j].CreatedTime + } + + return ll.Data[i].CreatedTime < ll.Data[j].CreatedTime + }) + + return ll +} + +// SortByUpdatedTime sorts ListLB by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ll ListLB) SortByUpdatedTime(inverse bool) ListLB { + if len(ll.Data) < 2 { + return ll + } + + sort.Slice(ll.Data, func(i, j int) bool { + if inverse { + return ll.Data[i].UpdatedTime > ll.Data[j].UpdatedTime + } + + return ll.Data[i].UpdatedTime < ll.Data[j].UpdatedTime + }) + + return ll +} + +// SortByDeletedTime sorts ListLB by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ll ListLB) SortByDeletedTime(inverse bool) ListLB { + if len(ll.Data) < 2 { + return ll + } + + sort.Slice(ll.Data, func(i, j int) bool { + if inverse { + return ll.Data[i].DeletedTime > ll.Data[j].DeletedTime + } + + return ll.Data[i].DeletedTime < ll.Data[j].DeletedTime + }) + + return ll +} diff --git a/pkg/cloudapi/lb/start.go b/pkg/cloudapi/lb/start.go new file mode 100644 index 0000000..b087fd2 --- /dev/null +++ b/pkg/cloudapi/lb/start.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StartRequest struct to start load balancer +type StartRequest struct { + // ID of the load balancer instance to start + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Start starts specified load balancer instance +func (l LB) Start(ctx context.Context, req StartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/start" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/stop.go b/pkg/cloudapi/lb/stop.go new file mode 100644 index 0000000..5483a67 --- /dev/null +++ b/pkg/cloudapi/lb/stop.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StopRequest struct to stop load balancer +type StopRequest struct { + // ID of the load balancer instance to stop + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Stop stops specified load balancer instance +func (l LB) Stop(ctx context.Context, req StopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/stop" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/update.go b/pkg/cloudapi/lb/update.go new file mode 100644 index 0000000..7c94012 --- /dev/null +++ b/pkg/cloudapi/lb/update.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update load balancer +type UpdateRequest struct { + // ID of the load balancer to update + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // New description of this load balancer. + // If omitted, current description is retained + // Required: true + Description string `url:"desc" json:"desc" validate:"required"` +} + +// Update updates some of load balancer attributes +func (l LB) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/update" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/lb/update_sysctl_params.go b/pkg/cloudapi/lb/update_sysctl_params.go new file mode 100644 index 0000000..d02eaf7 --- /dev/null +++ b/pkg/cloudapi/lb/update_sysctl_params.go @@ -0,0 +1,68 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateSysctParamsRequest struct to update sysct params for lb +type UpdateSysctParamsRequest struct { + // ID of the LB instance + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Custom sysctl values for Load Balancer instance. Applied on boot + // Required: true + SysctlParams []map[string]interface{} `url:"-" json:"sysctlParams" validate:"required,dive"` +} + +type wrapperUpdateSysctParamsRequest struct { + UpdateSysctParamsRequest + Params []string `url:"sysctlParams" validate:"required"` +} + +// UpdateSysctParams updates sysct paarams for lb +func (l LB) UpdateSysctlParams(ctx context.Context, req UpdateSysctParamsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + var params []string + + if len(req.SysctlParams) != 0 { + params = make([]string, 0, len(req.SysctlParams)) + for _, m := range req.SysctlParams { + encodeStr, err := json.Marshal(m) + if err != nil { + return false, err + } + params = append(params, string(encodeStr)) + } + } else { + params = []string{} + } + + reqWrapped := wrapperUpdateSysctParamsRequest{ + UpdateSysctParamsRequest: req, + Params: params, + } + + url := "/cloudapi/lb/updateSysctlParams" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/locations/filter.go b/pkg/cloudapi/locations/filter.go new file mode 100644 index 0000000..cc46e8a --- /dev/null +++ b/pkg/cloudapi/locations/filter.go @@ -0,0 +1,53 @@ +package locations + +// FilterByID returns ListLocations with specified ID. +func (ll ListLocations) FilterByID(id uint64) ListLocations { + predicate := func(il ItemLocation) bool { + return il.ID == id + } + + return ll.FilterFunc(predicate) +} + +// FilterByName returns ListLocations with specified Name. +func (ll ListLocations) FilterByName(name string) ListLocations { + predicate := func(il ItemLocation) bool { + return il.Name == name + } + + return ll.FilterFunc(predicate) +} + +// FilterByGID returns ListLocations with specified GID. +func (ll ListLocations) FilterByGID(gid uint64) ListLocations { + predicate := func(il ItemLocation) bool { + return il.GID == gid + } + + return ll.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListLocations based on a user-specified predicate. +func (ll ListLocations) FilterFunc(predicate func(ItemLocation) bool) ListLocations { + var result ListLocations + + for _, item := range ll.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemLocation +// If none was found, returns an empty struct. +func (ll ListLocations) FindOne() ItemLocation { + if len(ll.Data) == 0 { + return ItemLocation{} + } + + return ll.Data[0] +} diff --git a/pkg/cloudapi/locations/filter_test.go b/pkg/cloudapi/locations/filter_test.go new file mode 100644 index 0000000..d1c5cb4 --- /dev/null +++ b/pkg/cloudapi/locations/filter_test.go @@ -0,0 +1,78 @@ +package locations + +import "testing" + +var locationItems = ListLocations{ + Data: []ItemLocation{ + { + GID: 212, + ID: 1, + GUID: 1, + LocationCode: "alfa", + Name: "alfa", + Flag: "", + Meta: []interface{}{ + "cloudbroker", + "location", + 1, + }, + CKey: "", + }, + { + GID: 222, + ID: 2, + GUID: 2, + LocationCode: "beta", + Name: "beta", + Flag: "", + Meta: []interface{}{ + "cloudbroker", + "location", + 1, + }, + CKey: "", + }, + { + GID: 232, + ID: 3, + GUID: 3, + LocationCode: "gamma", + Name: "gamma", + Flag: "", + Meta: []interface{}{ + "cloudbroker", + "location", + 1, + }, + CKey: "", + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := locationItems.FilterByID(1).FindOne() + + if actual.ID != 1 { + t.Fatal("expected ID 1, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := locationItems.FilterByName("gamma").FindOne() + + if actual.Name != "gamma" { + t.Fatal("expected Name 'gamma', found: ", actual.Name) + } +} + +func TestFilterFunc(t *testing.T) { + actual := locationItems.FilterFunc(func(il ItemLocation) bool { + return il.GID == 212 + }). + FindOne() + + if actual.GID != 212 { + t.Fatal("expected GID 212, found: ", actual.GID) + } +} diff --git a/pkg/cloudapi/locations/get_url.go b/pkg/cloudapi/locations/get_url.go new file mode 100644 index 0000000..5ecc982 --- /dev/null +++ b/pkg/cloudapi/locations/get_url.go @@ -0,0 +1,18 @@ +package locations + +import ( + "context" + "net/http" +) + +// GetURL gets the portal URL +func (l Locations) GetURL(ctx context.Context) (string, error) { + url := "/cloudapi/locations/getUrl" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/locations/ids.go b/pkg/cloudapi/locations/ids.go new file mode 100644 index 0000000..0120234 --- /dev/null +++ b/pkg/cloudapi/locations/ids.go @@ -0,0 +1,10 @@ +package locations + +// IDs gets array of LocationIDs from ListLocations struct +func (ll ListLocations) IDs() []uint64 { + res := make([]uint64, 0, len(ll.Data)) + for _, l := range ll.Data { + res = append(res, l.GID) + } + return res +} diff --git a/pkg/cloudapi/locations/list.go b/pkg/cloudapi/locations/list.go new file mode 100644 index 0000000..af140a4 --- /dev/null +++ b/pkg/cloudapi/locations/list.go @@ -0,0 +1,71 @@ +package locations + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of locations +type ListRequest struct { + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by flag + // Required: false + Flag string `url:"flag,omitempty" json:"flag,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by code location + // Required: false + LocationCode string `url:"locationCode,omitempty" json:"locationCode,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` +} + +// List gets list of all locations as a ListLocations struct +func (l Locations) List(ctx context.Context, req ListRequest) (*ListLocations, error) { + + res, err := l.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListLocations{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all locations as an array of bytes +func (l Locations) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/locations/list" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/locations/locations.go b/pkg/cloudapi/locations/locations.go new file mode 100644 index 0000000..6872fb7 --- /dev/null +++ b/pkg/cloudapi/locations/locations.go @@ -0,0 +1,18 @@ +// API Actor api for managing locations +package locations + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to locations +type Locations struct { + client interfaces.Caller +} + +// Builder for locations endpoints +func New(client interfaces.Caller) *Locations { + return &Locations{ + client, + } +} diff --git a/pkg/cloudapi/locations/models.go b/pkg/cloudapi/locations/models.go new file mode 100644 index 0000000..87f7b3c --- /dev/null +++ b/pkg/cloudapi/locations/models.go @@ -0,0 +1,40 @@ +package locations + +// Main information about locations +type ItemLocation struct { + // AuthBroker + AuthBroker []string `json:"authBroker"` + + // Grid ID + GID uint64 `json:"gid"` + + // ID + ID uint64 `json:"id"` + + // GUID + GUID uint64 `json:"guid"` + + // Location code + LocationCode string `json:"locationCode"` + + // Name + Name string `json:"name"` + + // Flag + Flag string `json:"flag"` + + // Meta + Meta []interface{} `json:"_meta"` + + // CKey + CKey string `json:"_ckey"` +} + +// List of locations +type ListLocations struct { + // Data + Data []ItemLocation `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/locations/serialize.go b/pkg/cloudapi/locations/serialize.go new file mode 100644 index 0000000..1a60b30 --- /dev/null +++ b/pkg/cloudapi/locations/serialize.go @@ -0,0 +1,43 @@ +package locations + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ll ListLocations) Serialize(params ...string) (serialization.Serialized, error) { + if len(ll.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ll, prefix, indent) + } + + return json.Marshal(ll) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (il ItemLocation) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(il, prefix, indent) + } + + return json.Marshal(il) +} diff --git a/pkg/cloudapi/locatons.go b/pkg/cloudapi/locatons.go new file mode 100644 index 0000000..a0a2e90 --- /dev/null +++ b/pkg/cloudapi/locatons.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/locations" + +// Accessing the Locations method group +func (ca *CloudAPI) Locations() *locations.Locations { + return locations.New(ca.client) +} diff --git a/pkg/cloudapi/pcidevice.go b/pkg/cloudapi/pcidevice.go new file mode 100644 index 0000000..ca98d50 --- /dev/null +++ b/pkg/cloudapi/pcidevice.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/pcidevice" + +// Accessing the PCI Device method group +func (ca *CloudAPI) PCIDevice() *pcidevice.PCIDevice { + return pcidevice.New(ca.client) +} diff --git a/pkg/cloudapi/pcidevice/ids.go b/pkg/cloudapi/pcidevice/ids.go new file mode 100644 index 0000000..f558dbb --- /dev/null +++ b/pkg/cloudapi/pcidevice/ids.go @@ -0,0 +1,10 @@ +package pcidevice + +// IDs gets array of PCIDeviceIDs from ListPCIDevices struct +func (lpd ListPCIDevices) IDs() []uint64 { + res := make([]uint64, 0, len(lpd.Data)) + for _, lb := range lpd.Data { + res = append(res, lb.ID) + } + return res +} diff --git a/pkg/cloudapi/pcidevice/list.go b/pkg/cloudapi/pcidevice/list.go new file mode 100644 index 0000000..21acc43 --- /dev/null +++ b/pkg/cloudapi/pcidevice/list.go @@ -0,0 +1,76 @@ +package pcidevice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of pci devices +type ListRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by computeId + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by rgId + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all pci devices as a ListPCIDevices struct +func (p PCIDevice) List(ctx context.Context, req ListRequest) (*ListPCIDevices, error) { + + res, err := p.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListPCIDevices{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all pci devices as an array of bytes +func (p PCIDevice) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/pcidevice/list" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/pcidevice/models.go b/pkg/cloudapi/pcidevice/models.go new file mode 100644 index 0000000..0a125b0 --- /dev/null +++ b/pkg/cloudapi/pcidevice/models.go @@ -0,0 +1,50 @@ +package pcidevice + +// Main information about PCI device +type ItemPCIDevice struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Description + Description string `json:"description"` + + // GUID + GUID uint64 `json:"guid"` + + // HwPath + HwPath string `json:"hwPath"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // Status + Status string `json:"status"` + + // System name + SystemName string `json:"systemName"` +} + +// List PCI devices +type ListPCIDevices struct { + // Data + Data []ItemPCIDevice `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + diff --git a/pkg/cloudapi/pcidevice/pcidevice.go b/pkg/cloudapi/pcidevice/pcidevice.go new file mode 100644 index 0000000..82b3d3a --- /dev/null +++ b/pkg/cloudapi/pcidevice/pcidevice.go @@ -0,0 +1,15 @@ +package pcidevice + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to PCI device +type PCIDevice struct { + client interfaces.Caller +} + +// Builder for PCI device endpoints +func New(client interfaces.Caller) *PCIDevice { + return &PCIDevice{ + client: client, + } +} diff --git a/pkg/cloudapi/pcidevice/serialize.go b/pkg/cloudapi/pcidevice/serialize.go new file mode 100644 index 0000000..7616b54 --- /dev/null +++ b/pkg/cloudapi/pcidevice/serialize.go @@ -0,0 +1,43 @@ +package pcidevice + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (l ListPCIDevices) Serialize(params ...string) (serialization.Serialized, error) { + if len(l.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(l, prefix, indent) + } + + return json.Marshal(l) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (i ItemPCIDevice) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(i, prefix, indent) + } + + return json.Marshal(i) +} diff --git a/pkg/cloudapi/rg.go b/pkg/cloudapi/rg.go new file mode 100644 index 0000000..4e6af0c --- /dev/null +++ b/pkg/cloudapi/rg.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/rg" + +// Accessing the RG method group +func (ca *CloudAPI) RG() *rg.RG { + return rg.New(ca.client) +} diff --git a/pkg/cloudapi/rg/access_grant.go b/pkg/cloudapi/rg/access_grant.go new file mode 100644 index 0000000..8a9b081 --- /dev/null +++ b/pkg/cloudapi/rg/access_grant.go @@ -0,0 +1,49 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessGrantRequest struct to grant access to resource group +type AccessGrantRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // User or group name to grant access + // Required: true + User string `url:"user" json:"user" validate:"required"` + + // Access rights to set, one of: + // - "R" + // - "RCX" + // - "ARCXDU" + // Required: true + Right string `url:"right" json:"right" validate:"accessType"` +} + +// AccessGrant grants user or group access to the resource group as specified +func (r RG) AccessGrant(ctx context.Context, req AccessGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/accessGrant" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/access_revoke.go b/pkg/cloudapi/rg/access_revoke.go new file mode 100644 index 0000000..64f545c --- /dev/null +++ b/pkg/cloudapi/rg/access_revoke.go @@ -0,0 +1,42 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessRevokeRequest struct to revoke access +type AccessRevokeRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // User or group name to revoke access + // Required: true + User string `url:"user" json:"user" validate:"required"` +} + +// AccessRevoke revokes specified user or group access from the resource group +func (r RG) AccessRevoke(ctx context.Context, req AccessRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/accessRevoke" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/affinity_group_computes.go b/pkg/cloudapi/rg/affinity_group_computes.go new file mode 100644 index 0000000..ca877eb --- /dev/null +++ b/pkg/cloudapi/rg/affinity_group_computes.go @@ -0,0 +1,44 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityGroupComputesRequest struct to get list of all computes with their relationships +type AffinityGroupComputesRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Affinity group label + // Required: true + AffinityGroup string `url:"affinityGroup" json:"affinityGroup" validate:"required"` +} + +// AffinityGroupComputes gets list of all computes with their relationships to another computes +func (r RG) AffinityGroupComputes(ctx context.Context, req AffinityGroupComputesRequest) (ListAffinityGroupsComputes, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/affinityGroupComputes" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAffinityGroupsComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/rg/affinity_groups_get.go b/pkg/cloudapi/rg/affinity_groups_get.go new file mode 100644 index 0000000..be5c6ff --- /dev/null +++ b/pkg/cloudapi/rg/affinity_groups_get.go @@ -0,0 +1,44 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityGroupsGetRequest struct to get list computes from affinity group +type AffinityGroupsGetRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Label affinity group + // Required: true + AffinityGroup string `url:"affinityGroup" json:"affinityGroup" validate:"required"` +} + +// AffinityGroupsGet gets list computes in the specified affinity group +func (r RG) AffinityGroupsGet(ctx context.Context, req AffinityGroupsGetRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/affinityGroupsGet" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := []uint64{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/rg/affinity_groups_list.go b/pkg/cloudapi/rg/affinity_groups_list.go new file mode 100644 index 0000000..1094797 --- /dev/null +++ b/pkg/cloudapi/rg/affinity_groups_list.go @@ -0,0 +1,48 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityGroupsListRequest struct to get list of affinity groups from resource group +type AffinityGroupsListRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// AffinityGroupsList gets all currently defined affinity groups in this resource group with compute IDs +func (r RG) AffinityGroupsList(ctx context.Context, req AffinityGroupsListRequest) (*ListAffinityGroups, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/affinityGroupsList" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := &ListAffinityGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/rg/audits.go b/pkg/cloudapi/rg/audits.go new file mode 100644 index 0000000..4a5d0c7 --- /dev/null +++ b/pkg/cloudapi/rg/audits.go @@ -0,0 +1,40 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuditsRequest struct to get audit +type AuditsRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Audits gets audit records for the specified resource group object +func (r RG) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/audits" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/rg/create.go b/pkg/cloudapi/rg/create.go new file mode 100644 index 0000000..93f7f57 --- /dev/null +++ b/pkg/cloudapi/rg/create.go @@ -0,0 +1,100 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create resource group +type CreateRequest struct { + // Account, which will own this resource group + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Grid ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Name of this resource group. Must be unique within the account + // Required: true + Name string `url:"name" json:"name" validate:"required,min=2"` + + // Max size of memory in MB + // Required: false + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + + // Max size of aggregated virtual disks in GB + // Required: false + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + + // Max number of CPU cores + // Required: false + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + + // Max sent/received network transfer peering + // Required: false + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + + // Max number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + + // Username - owner of this resource group. + // Leave blank to set current user as owner + // Required: false + Owner string `url:"owner,omitempty" json:"owner,omitempty"` + + // Type of the default network for this resource group. + // virtual machines created in this resource group will be by default connected to this network. + // Allowed values are: + // - PRIVATE + // - PUBLIC + // - NONE + // Required: false + DefNet string `url:"def_net,omitempty" json:"def_net,omitempty" validate:"omitempty,rgDefNet"` + + // Private network IP CIDR if default network PRIVATE + // Required: false + IPCIDR string `url:"ipcidr,omitempty" json:"ipcidr,omitempty"` + + // Text description of this resource group + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // External network ID + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // External IP address + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Register computes in registration system + // Required: false + RegisterComputes bool `url:"registerComputes,omitempty" json:"registerComputes,omitempty"` +} + +// Create creates resource group +func (r RG) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/create" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/delete.go b/pkg/cloudapi/rg/delete.go new file mode 100644 index 0000000..796f847 --- /dev/null +++ b/pkg/cloudapi/rg/delete.go @@ -0,0 +1,47 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete resource group +type DeleteRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Set to True if you want force delete non-empty resource group + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` + + // Set to True if you want to destroy resource group and all linked resources, if any, immediately. + // Otherwise, they will be placed into recycle bin and could be restored later within recycle bin's purge period + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes resource group +func (r RG) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/delete" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/disable.go b/pkg/cloudapi/rg/disable.go new file mode 100644 index 0000000..d46770b --- /dev/null +++ b/pkg/cloudapi/rg/disable.go @@ -0,0 +1,38 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable resource group +type DisableRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Disable disables resource group +func (r RG) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/disable" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/enable.go b/pkg/cloudapi/rg/enable.go new file mode 100644 index 0000000..1e0c40d --- /dev/null +++ b/pkg/cloudapi/rg/enable.go @@ -0,0 +1,38 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable resource group +type EnableRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Enable enables resource group +func (r RG) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/enable" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/filter.go b/pkg/cloudapi/rg/filter.go new file mode 100644 index 0000000..bf6130b --- /dev/null +++ b/pkg/cloudapi/rg/filter.go @@ -0,0 +1,80 @@ +package rg + +// FilterByID returns ListResourceGroups with specified ID. +func (lrg ListResourceGroups) FilterByID(id uint64) ListResourceGroups { + predicate := func(irg ItemResourceGroup) bool { + return irg.ID == id + } + + return lrg.FilterFunc(predicate) +} + +// FilterByName returns ListResourceGroups with specified Name. +func (lrg ListResourceGroups) FilterByName(name string) ListResourceGroups { + predicate := func(irg ItemResourceGroup) bool { + return irg.Name == name + } + + return lrg.FilterFunc(predicate) +} + +// FilterByCreatedBy return ListResourceGroups created by specified user. +func (lrg ListResourceGroups) FilterByCreatedBy(createdBy string) ListResourceGroups { + predicate := func(irg ItemResourceGroup) bool { + return irg.CreatedBy == createdBy + } + + return lrg.FilterFunc(predicate) +} + +// FilterByStatus returns ListResourceGroups with specified Status. +func (lrg ListResourceGroups) FilterByStatus(status string) ListResourceGroups { + predicate := func(irg ItemResourceGroup) bool { + return irg.Status == status + } + + return lrg.FilterFunc(predicate) +} + +// FilterByLockStatus return ListResourceGroups with specified LockStatus. +func (lrg ListResourceGroups) FilterByLockStatus(lockStatus string) ListResourceGroups { + predicate := func(irg ItemResourceGroup) bool { + return irg.LockStatus == lockStatus + } + + return lrg.FilterFunc(predicate) +} + +// FilterByDefNetType returns ListResourceGroups with specified DefNetType. +func (lrg ListResourceGroups) FilterByDefNetType(defNetType string) ListResourceGroups { + predicate := func(irg ItemResourceGroup) bool { + return irg.DefNetType == defNetType + } + + return lrg.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListResourceGroups based on a user-specified predicate. +func (lrg ListResourceGroups) FilterFunc(predicate func(irg ItemResourceGroup) bool) ListResourceGroups { + var result ListResourceGroups + + for _, rgItem := range lrg.Data { + if predicate(rgItem) { + result.Data = append(result.Data, rgItem) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemResourceGroup. +// If none was found, returns an empty struct. +func (lrg ListResourceGroups) FindOne() ItemResourceGroup { + if len(lrg.Data) == 0 { + return ItemResourceGroup{} + } + + return lrg.Data[0] +} diff --git a/pkg/cloudapi/rg/filter_test.go b/pkg/cloudapi/rg/filter_test.go new file mode 100644 index 0000000..5caab07 --- /dev/null +++ b/pkg/cloudapi/rg/filter_test.go @@ -0,0 +1,236 @@ +package rg + +import "testing" + +var rgs = ListResourceGroups{ + Data: []ItemResourceGroup{ + { + AccountID: 1, + AccountName: "std", + ACL: ListACL{ + { + Explicit: true, + GUID: "", + Right: "ARCXDU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "sample_user_1@decs3o", + }, + }, + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1676645305, + DefNetID: 1, + DefNetType: "NONE", + DeletedBy: "", + DeletedTime: 0, + Description: "", + GID: 212, + GUID: 7971, + ID: 7971, + LockStatus: "UNLOCKED", + Milestones: 363459, + Name: "rg_1", + RegisterComputes: false, + ResourceLimits: ResourceLimits{ + CUC: -1, + CUI: -1, + CUM: -1, + CUNP: -1, + GPUUnits: -1, + }, + Secret: "", + Status: "CREATED", + UpdatedBy: "", + UpdatedTime: 0, + VINS: []uint64{}, + Computes: []uint64{}, + ResTypes: []string{}, + UniqPools: []string{}, + }, + { + AccountID: 2, + AccountName: "std_2", + ACL: ListACL{ + { + Explicit: true, + GUID: "", + Right: "ARCXDU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "sample_user_1@decs3o", + }, + }, + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1676645461, + DefNetID: 2, + DefNetType: "NONE", + DeletedBy: "", + DeletedTime: 0, + Description: "", + GID: 212, + GUID: 7972, + ID: 7972, + LockStatus: "UNLOCKED", + Milestones: 363468, + Name: "rg_2", + RegisterComputes: false, + ResourceLimits: ResourceLimits{ + CUC: -1, + CUI: -1, + CUM: -1, + CUNP: -1, + GPUUnits: -1, + }, + Secret: "", + Status: "CREATED", + UpdatedBy: "", + UpdatedTime: 0, + VINS: []uint64{}, + Computes: []uint64{}, + ResTypes: []string{}, + UniqPools: []string{}, + }, + { + AccountID: 3, + AccountName: "std_3", + ACL: ListACL{ + { + Explicit: true, + GUID: "", + Right: "ARCXDU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "sample_user_2@decs3o", + }, + }, + CreatedBy: "sample_user_2@decs3o", + CreatedTime: 1676645548, + DefNetID: 3, + DefNetType: "NONE", + DeletedBy: "", + DeletedTime: 0, + Description: "", + GID: 212, + GUID: 7973, + ID: 7973, + LockStatus: "kjLOCKED", + Milestones: 363471, + Name: "rg_3", + RegisterComputes: false, + ResourceLimits: ResourceLimits{ + CUC: -1, + CUI: -1, + CUM: -1, + CUNP: -1, + GPUUnits: -1, + }, + Secret: "", + Status: "DISABLED", + UpdatedBy: "", + UpdatedTime: 0, + VINS: []uint64{}, + Computes: []uint64{ + 48500, + }, + ResTypes: []string{}, + UniqPools: []string{}, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := rgs.FilterByID(7972).FindOne() + + if actual.ID != 7972 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := rgs.FilterByName("rg_1").FindOne() + + if actual.Name != "rg_1" { + t.Fatal("expected Name 'rg_1', found: ", actual.Name) + } +} + +func TestFilterByCreatedBy(t *testing.T) { + actual := rgs.FilterByCreatedBy("sample_user_1@decs3o") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedBy != "sample_user_1@decs3o" { + t.Fatal("expected CreatedBy 'sample_user_1@decs3o', found: ", item.CreatedBy) + } + } +} + +func TestFilterByStatus(t *testing.T) { + actual := rgs.FilterByStatus("CREATED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "CREATED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterByLockStatus(t *testing.T) { + actual := rgs.FilterByLockStatus("UNLOCKED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.LockStatus != "UNLOCKED" { + t.Fatal("expected LockStatus 'UNLOCKED', found: ", item.LockStatus) + } + } +} + +func TestFilterByDefNetType(t *testing.T) { + actual := rgs.FilterByDefNetType("NONE") + + if len(actual.Data) != 3 { + t.Fatal("expected 3 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.DefNetType != "NONE" { + t.Fatal("expected DefNetType 'NONE', found: ", item.DefNetType) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := rgs.FilterFunc(func(ir ItemResourceGroup) bool { + return len(ir.Computes) > 0 + }) + + if len(actual.Data) < 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if len(item.Computes) < 1 { + t.Fatal("expected VMs to contain at least 1 element, found empty") + } + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := rgs.SortByCreatedTime(true) + + if actual.Data[0].CreatedTime != 1676645548 || actual.Data[2].CreatedTime != 1676645305 { + t.Fatal("expected descending order, found ascending") + } +} diff --git a/pkg/cloudapi/rg/get.go b/pkg/cloudapi/rg/get.go new file mode 100644 index 0000000..e3e3b83 --- /dev/null +++ b/pkg/cloudapi/rg/get.go @@ -0,0 +1,46 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about resource group +type GetRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Get gets current configuration of the resource group as a RecordResourceGroup struct +func (r RG) Get(ctx context.Context, req GetRequest) (*RecordResourceGroup, error) { + res, err := r.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordResourceGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets current configuration of the resource group as an array of bytes +func (r RG) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/get" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/rg/get_resource_consumption.go b/pkg/cloudapi/rg/get_resource_consumption.go new file mode 100644 index 0000000..309c086 --- /dev/null +++ b/pkg/cloudapi/rg/get_resource_consumption.go @@ -0,0 +1,40 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetResourceConsumptionRequest struct to get detailed information about resource consumption for ResGroup +type GetResourceConsumptionRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// GetResourceConsumption gets resource consumption of the resource group +func (r RG) GetResourceConsumption(ctx context.Context, req GetResourceConsumptionRequest) (*ItemResourceConsumption, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/getResourceConsumption" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := ItemResourceConsumption{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/rg/ids.go b/pkg/cloudapi/rg/ids.go new file mode 100644 index 0000000..457535c --- /dev/null +++ b/pkg/cloudapi/rg/ids.go @@ -0,0 +1,64 @@ +package rg + +// IDs gets array of ResourceGroupIDs from ListResourceGroups struct +func (lrg ListResourceGroups) IDs() []uint64 { + res := make([]uint64, 0, len(lrg.Data)) + for _, rg := range lrg.Data { + res = append(res, rg.ID) + } + return res +} + +// IDs gets array of ComputeIDs from ListAffinityGroupsComputes struct +func (lag ListAffinityGroupsComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lag)) + for _, ag := range lag { + res = append(res, ag.ComputeID) + } + return res +} + +// IDs gets array of ComputeIDs from ListComputes struct +func (lc ListComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lc.Data)) + for _, c := range lc.Data { + res = append(res, c.ID) + } + return res +} + +// IDs gets array of LBIDs from ListLB struct +func (llb ListLB) IDs() []uint64 { + res := make([]uint64, 0, len(llb.Data)) + for _, lb := range llb.Data { + res = append(res, lb.ID) + } + return res +} + +// IDs gets array of VINSIDs from ListVINS struct +func (llb ListVINS) IDs() []uint64 { + res := make([]uint64, 0, len(llb.Data)) + for _, lb := range llb.Data { + res = append(res, lb.ID) + } + return res +} + +// IDs gets array of ResourceGroupIDs from ListResourceConsumption struct +func (lrc ListResourceConsumption) IDs() []uint64 { + res := make([]uint64, 0, len(lrc.Data)) + for _, rc := range lrc.Data { + res = append(res, rc.RGID) + } + return res +} + +// IDs gets array of ResourceGroupIDs from ListAffinityGroup struct +func (lag ListAffinityGroup) IDs() []uint64 { + res := make([]uint64, 0, len(lag)) + for _, ag := range lag { + res = append(res, ag.ID) + } + return res +} diff --git a/pkg/cloudapi/rg/list.go b/pkg/cloudapi/rg/list.go new file mode 100644 index 0000000..5490616 --- /dev/null +++ b/pkg/cloudapi/rg/list.go @@ -0,0 +1,91 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of resource groups +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by name account + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by created after time (unix timestamp) + // Required: false + CreatedAfter uint64 `url:"createdAfter,omitempty" json:"createdAfter,omitempty"` + + // Find by created before time (unix timestamp) + // Required: false + CreatedBefore uint64 `url:"createdBefore,omitempty" json:"createdBefore,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by status lock + // Required: false + LockStatus string `url:"lockStatus,omitempty" json:"lockStatus,omitempty"` + + // Included deleted resource groups + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all resource groups the user has access to as a ListResourceGroups struct +func (r RG) List(ctx context.Context, req ListRequest) (*ListResourceGroups, error) { + + res, err := r.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListResourceGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all resource groups the user has access to as an array of bytes +func (r RG) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/list" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/rg/list_computes.go b/pkg/cloudapi/rg/list_computes.go new file mode 100644 index 0000000..8dc54e9 --- /dev/null +++ b/pkg/cloudapi/rg/list_computes.go @@ -0,0 +1,85 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListComputesRequest struct to get list of computes +type ListComputesRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Find by compute id + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // ID an account + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by ip address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by external network name + // Required: false + ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"` + + // Find by external network id + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListComputes gets list of all compute instances under specified resource group, accessible by the user +func (r RG) ListComputes(ctx context.Context, req ListComputesRequest) (*ListComputes, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/listComputes" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/rg/list_deleted.go b/pkg/cloudapi/rg/list_deleted.go new file mode 100644 index 0000000..8547d36 --- /dev/null +++ b/pkg/cloudapi/rg/list_deleted.go @@ -0,0 +1,76 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list deleted resource groups +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by name account + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by created after time (unix timestamp) + // Required: false + CreatedAfter uint64 `url:"createdAfter,omitempty" json:"createdAfter,omitempty"` + + // Find by created before time (unix timestamp) + // Required: false + CreatedBefore uint64 `url:"createdBefore,omitempty" json:"createdBefore,omitempty"` + + // Find by status lock + // Required: false + LockStatus string `url:"lockStatus,omitempty" json:"lockStatus,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list all deleted resource groups the user has access to +func (r RG) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListResourceGroups, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/listDeleted" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListResourceGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/rg/list_lb.go b/pkg/cloudapi/rg/list_lb.go new file mode 100644 index 0000000..effafda --- /dev/null +++ b/pkg/cloudapi/rg/list_lb.go @@ -0,0 +1,77 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListLBRequest struct to get list of load balancers +type ListLBRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by frontend Ip + // Required: false + FrontIP string `url:"frontIp,omitempty" json:"frontIp,omitempty"` + + // Find by backend Ip + // Required: false + BackIP string `url:"backIp,omitempty" json:"backIp,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListLB gets list all load balancers in the specified resource group, accessible by the user +func (r RG) ListLB(ctx context.Context, req ListLBRequest) (*ListLB, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/listLb" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListLB{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/rg/list_pfw.go b/pkg/cloudapi/rg/list_pfw.go new file mode 100644 index 0000000..5ab6aef --- /dev/null +++ b/pkg/cloudapi/rg/list_pfw.go @@ -0,0 +1,41 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListPFWRequest struct to get list of port forward rules +type ListPFWRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// ListPFW gets list port forward rules for the specified resource group +func (r RG) ListPFW(ctx context.Context, req ListPFWRequest) (*ListPortForwards, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/listPFW" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListPortForwards{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/rg/list_resource_consumption.go b/pkg/cloudapi/rg/list_resource_consumption.go new file mode 100644 index 0000000..badf325 --- /dev/null +++ b/pkg/cloudapi/rg/list_resource_consumption.go @@ -0,0 +1,26 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" +) + +// ListResourceConsumption gets resource consumptions of the resource groups +func (r RG) ListResourceConsumption(ctx context.Context) (*ListResourceConsumption, error) { + url := "/cloudapi/rg/listResourceConsumption" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + list := ListResourceConsumption{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/rg/list_vins.go b/pkg/cloudapi/rg/list_vins.go new file mode 100644 index 0000000..c364a6d --- /dev/null +++ b/pkg/cloudapi/rg/list_vins.go @@ -0,0 +1,69 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListVINSRequest struct to get list of VINSes +type ListVINSRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // ID an account + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by ip extnet address + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Find by vins id + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListVINS gets list all ViNSes under specified resource group, accessible by the user +func (r RG) ListVINS(ctx context.Context, req ListVINSRequest) (*ListVINS, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/listVins" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/rg/models.go b/pkg/cloudapi/rg/models.go new file mode 100644 index 0000000..4f418ab --- /dev/null +++ b/pkg/cloudapi/rg/models.go @@ -0,0 +1,854 @@ +package rg + +// Resources used +type Resource struct { + // Number of cores + CPU int64 `json:"cpu"` + + // Disk size + DiskSize float64 `json:"disksize"` + + // Max disk size + DiskSizeMax float64 `json:"disksizemax"` + + // Number of External IPs + ExtIPs int64 `json:"extips"` + + // External traffic + ExtTraffic int64 `json:"exttraffic"` + + // Number of grafic cores + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +// Disk usage +type DiskUsage struct { + // Disk size + DiskSize float64 `json:"disksize"` + + // Disk size max + DiskSizeMax float64 `json:"disksizemax"` +} + +// Information about resources +type Resources struct { + // Current information about resources + Current Resource `json:"Current"` + + // Reserved information about resources + Reserved Resource `json:"Reserved"` +} + +// Detailed information about resource consumption +type ItemResourceConsumption struct { + // Consumed information about resources + Consumed Resource `json:"consumed"` + + // Reserved information about resources + Reserved Resource `json:"reserved"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // Resource group ID + RGID uint64 `json:"rgid"` +} + +type ListResourceConsumption struct { + Data []ItemResourceConsumption `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about resource group +type RecordResourceGroup struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control List + ACL ListACL `json:"acl"` + + // Compute Features + ComputeFeatures []string `json:"computeFeatures"` + + // CPU allocation parameter + CPUAllocationParameter string `json:"cpu_allocation_parameter"` + + // CPU allocation ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // DefNetID + DefNetID int64 `json:"def_net_id"` + + // DefNetType + DefNetType string `json:"def_net_type"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Dirty + Dirty bool `json:"dirty"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // RegisterComputes + RegisterComputes bool `json:"registerComputes"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // List of resource types + ResTypes []string `json:"resourceTypes"` + + // Secret + Secret string `json:"secret"` + + // Status + Status string `json:"status"` + + // UniqPools + UniqPools []string `json:"uniqPools"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // List of VINS IDs + VINS []uint64 `json:"vins"` + + // List of compute IDs + Computes []uint64 `json:"vms"` +} + +// Main information about resource group +type ItemResourceGroup struct { + // + AccountACL ItemACL `json:"accountAcl"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control List + ACL ListACL `json:"acl"` + + // Compute Features + ComputeFeatures []string `json:"computeFeatures"` + + // CPU allocation parameter + CPUAllocationParameter string `json:"cpu_allocation_parameter"` + + // CPU allocation ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // DefNetID + DefNetID int64 `json:"def_net_id"` + + // DefNetType + DefNetType string `json:"def_net_type"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Dirty + Dirty bool `json:"dirty"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // RegisterComputes + RegisterComputes bool `json:"registerComputes"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // List of resource types + ResTypes []string `json:"resourceTypes"` + + // Secret + Secret string `json:"secret"` + + // Status + Status string `json:"status"` + + // UniqPools + UniqPools []string `json:"uniqPools"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // List of VINS IDs + VINS []uint64 `json:"vins"` + + // List of compute IDs + Computes []uint64 `json:"vms"` +} + +// List of resource groups +type ListResourceGroups struct { + Data []ItemResourceGroup `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// Main information about Access Control List +type ItemACL struct { + // Explicit + Explicit bool `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Right + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// List ACL +type ListACL []ItemACL + +// Resource limits +type ResourceLimits struct { + // CUC + CUC float64 `json:"CU_C"` + + // CUD + CUD float64 `json:"CU_D"` + + // CUDM + CUDM float64 `json:"CU_DM"` + + // CUI + CUI float64 `json:"CU_I"` + + // CUM + CUM float64 `json:"CU_M"` + + // CUNP + CUNP float64 `json:"CU_NP"` + + // GPU units + GPUUnits float64 `json:"gpu_units"` +} + +// Main information about affinity group +type ItemAffinityGroupComputes struct { + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Other node + OtherNode []uint64 `json:"otherNode"` + + // Other node indirect + OtherNodeIndirect []uint64 `json:"otherNodeIndirect"` + + // Other node indirect soft + OtherNodeIndirectSoft []uint64 `json:"otherNodeIndirectSoft"` + + // Other node soft + OtherNodeSoft []uint64 `json:"otherNodeSoft"` + + // Same node + SameNode []uint64 `json:"sameNode"` + + // Same node soft + SameNodeSoft []uint64 `json:"sameNodeSoft"` +} + +// List of affinity groups +type ListAffinityGroupsComputes []ItemAffinityGroupComputes + +// Main information about +type ItemAffinityGroup struct { + ID uint64 `json:"id"` + NodeID uint64 `json:"node_id"` +} + +// List of affinity group +type ListAffinityGroup []ItemAffinityGroup + +// List of affinity groups +type ListAffinityGroups struct { + // Data + Data []map[string]ListAffinityGroup `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about audit +type ItemAudit struct { + // Call + Call string `json:"call"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List of audits +type ListAudits []ItemAudit + +// Main information about affinity rules +type ItemRule struct { + // GUID + GUID string `json:"guid"` + + // Key + Key string `json:"key"` + + // Mode + Mode string `json:"mode"` + + // Policy + Policy string `json:"policy"` + + // Topology + Topology string `json:"topology"` + + // Value + Value string `json:"value"` +} + +// List of rules +type ListRules []ItemRule + +// Main information about compute +type ItemCompute struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Affinity label + AffinityLabel string `json:"affinityLabel"` + + // List of affinity rules + AffinityRules ListRules `json:"affinityRules"` + + // Affinity weight + AffinityWeight uint64 `json:"affinityWeight"` + + // List of anti affinity rules + AntiAffinityRules ListRules `json:"antiAffinityRules"` + + // Number of CPU + CPUs uint64 `json:"cpus"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Registered + Registered bool `json:"registered"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Total disks size + TotalDisksSize uint64 `json:"totalDisksSize"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed + UserManaged bool `json:"userManaged"` + + // Number of ViNS connected + VINSConnected uint64 `json:"vinsConnected"` +} + +// List of computes +type ListComputes struct { + //Data + Data []ItemCompute `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about load balancer +type RecordLoadBalancer struct { + // HAMode + HAMode bool `json:"HAmode"` + + // Access Control List + ACL interface{} `json:"acl"` + + // BackendHAIP + BackendHAIP string `json:"backendHAIP"` + + // List of Backends + Backends ListBackends `json:"backends"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // DPAPIUser + DPAPIUser string `json:"dpApiUser"` + + // External network ID + ExtNetID uint64 `json:"extnetId"` + + // FrontendHAIP + FrontendHAIP string `json:"frontendHAIP"` + + // List of frontends + Frontends ListFrontends `json:"frontends"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Primary node + PrimaryNode RecordNode `json:"primaryNode"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Secondary node + SecondaryNode RecordNode `json:"secondaryNode"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // VINS ID + VINSID uint64 `json:"vinsId"` +} + +// Detailed information about load balancer +type ItemLoadBalancer struct { + // DPAPI password + DPAPIPassword string `json:"dpApiPassword"` + + // Main information about load balancer + RecordLoadBalancer +} + +// Main information about backend +type ItemBackend struct { + // Algorithm + Algorithm string `json:"algorithm"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Server settings + ServerDefaultSettings RecordServerSettings `json:"serverDefaultSettings"` + + // List of servers + Servers ListServers `json:"servers"` +} + +// List of backends +type ListBackends []ItemBackend + +// List of load balancers +type ListLB struct { + // Data + Data []ItemLoadBalancer `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Server settings +type RecordServerSettings struct { + // Inter + Inter uint64 `json:"inter"` + + // GUID + GUID string `json:"guid"` + + // Down inter + DownInter uint64 `json:"downinter"` + + // Rise + Rise uint64 `json:"rise"` + + // Fall + Fall uint64 `json:"fall"` + + // Slow start + SlowStart uint64 `json:"slowstart"` + + // Max connections + MaxConn uint64 `json:"maxconn"` + + // Max queue + MaxQueue uint64 `json:"maxqueue"` + + // Weight + Weight uint64 `json:"weight"` +} + +// Main information about server +type ItemServer struct { + // Address + Address string `json:"address"` + + // Check + Check string `json:"check"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Port + Port uint64 `json:"port"` + + // Server settings + ServerSettings RecordServerSettings `json:"serverSettings"` +} + +// List of servers +type ListServers []ItemServer + +// Main information about node +type RecordNode struct { + // Backend IP + BackendIP string `json:"backendIp"` + + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Frontend IP + FrontendIP string `json:"frontendIp"` + + // GUID + GUID string `json:"guid"` + + // MGMT IP + MGMTIP string `json:"mgmtIp"` + + // Network ID + NetworkID uint64 `json:"networkId"` +} + +// Main information about frontend +type ItemFrontend struct { + // Backend + Backend string `json:"backend"` + + // List of bindings + Bindings ListBindings `json:"bindings"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` +} + +// List of frontends +type ListFrontends []ItemFrontend + +// Main information of binding +type ItemBinding struct { + // Address + Address string `json:"address"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Port + Port uint64 `json:"port"` +} + +// List of bindings +type ListBindings []ItemBinding + +// Main information about port forward +type ItemPortForward struct { + // Public port end + PublicPortEnd uint64 `json:"Public Port End"` + + // Public port start + PublicPortStart uint64 `json:"Public Port Start"` + + // Virtual machine ID + VMID uint64 `json:"VM ID"` + + // Virtual machine IP + VMIP string `json:"VM IP"` + + // Virtual machine name + VMName string `json:"VM Name"` + + // Virtual machine port + VMPort uint64 `json:"VM Port"` + + // VINS ID + VINSID uint64 `json:"ViNS ID"` + + // VINS name + VINSName string `json:"ViNS Name"` +} + +// List of port forwards +type ListPortForwards struct { + //Data + Data []ItemPortForward `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about VINS +type ItemVINS struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Computes + Computes uint64 `json:"computes"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // External IP + ExternalIP string `json:"externalIP"` + + // Extnet ID + ExtnetId uint64 `json:"extnetId"` + + // Free IPs + FreeIPs int64 `json:"freeIPs"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // PriVNFDev ID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// List of VINSes +type ListVINS struct { + // Data + Data []ItemVINS `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about usage of resource +type RecordResourceUsage struct { + // Number of CPU + CPU uint64 `json:"cpu"` + + // Disk size + DiskSize float64 `json:"disksize"` + + // Max disk size + DiskSizeMax uint64 `json:"disksizemax"` + + // Number of external IPs + ExtIPs uint64 `json:"extips"` + + // ExtraTraffic + ExtraTraffic uint64 `json:"exttraffic"` + + // Number of GPU + GPU uint64 `json:"gpu"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` +} diff --git a/pkg/cloudapi/rg/remove_def_net.go b/pkg/cloudapi/rg/remove_def_net.go new file mode 100644 index 0000000..d606208 --- /dev/null +++ b/pkg/cloudapi/rg/remove_def_net.go @@ -0,0 +1,38 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RemoveDefNetRequest struct to remove default network +type RemoveDefNetRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// RemoveDefNet removes default network from resource group +func (r RG) RemoveDefNet(ctx context.Context, req RemoveDefNetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/removeDefNet" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/restore.go b/pkg/cloudapi/rg/restore.go new file mode 100644 index 0000000..2c30e0d --- /dev/null +++ b/pkg/cloudapi/rg/restore.go @@ -0,0 +1,38 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore resource group +type RestoreRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Restore restores resource group from recycle bin +func (r RG) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/restore" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/rg.go b/pkg/cloudapi/rg/rg.go new file mode 100644 index 0000000..9582724 --- /dev/null +++ b/pkg/cloudapi/rg/rg.go @@ -0,0 +1,18 @@ +// API Actors for managing resource groups. These actors are the final API for end users to manage resource groups +package rg + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to resource group +type RG struct { + client interfaces.Caller +} + +// Builder for resource group endpoints +func New(client interfaces.Caller) *RG { + return &RG{ + client, + } +} diff --git a/pkg/cloudapi/rg/serialize.go b/pkg/cloudapi/rg/serialize.go new file mode 100644 index 0000000..e6f62d9 --- /dev/null +++ b/pkg/cloudapi/rg/serialize.go @@ -0,0 +1,43 @@ +package rg + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lrg ListResourceGroups) Serialize(params ...string) (serialization.Serialized, error) { + if len(lrg.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lrg, prefix, indent) + } + + return json.Marshal(lrg) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (irg ItemResourceGroup) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(irg, prefix, indent) + } + + return json.Marshal(irg) +} diff --git a/pkg/cloudapi/rg/set_def_net.go b/pkg/cloudapi/rg/set_def_net.go new file mode 100644 index 0000000..abf6001 --- /dev/null +++ b/pkg/cloudapi/rg/set_def_net.go @@ -0,0 +1,49 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetDefNetRequest struct to set default network +type SetDefNetRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Network type + // Should be one of: + // - "PUBLIC" + // - "PRIVATE" + // Required: true + NetType string `url:"netType" json:"netType" validate:"rgNetType"` + + // Network ID + // Required: false + NetID uint64 `url:"netId,omitempty" json:"netId,omitempty"` +} + +// SetDefNet sets default network for attach associated virtual machines +func (r RG) SetDefNet(ctx context.Context, req SetDefNetRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/setDefNet" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/sorting.go b/pkg/cloudapi/rg/sorting.go new file mode 100644 index 0000000..a723a32 --- /dev/null +++ b/pkg/cloudapi/rg/sorting.go @@ -0,0 +1,60 @@ +package rg + +import "sort" + +// SortByCreatedTime sorts ListResourceGroups by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lrg ListResourceGroups) SortByCreatedTime(inverse bool) ListResourceGroups { + if len(lrg.Data) < 2 { + return lrg + } + + sort.Slice(lrg.Data, func(i, j int) bool { + if inverse { + return lrg.Data[i].CreatedTime > lrg.Data[j].CreatedTime + } + + return lrg.Data[i].CreatedTime < lrg.Data[j].CreatedTime + }) + + return lrg +} + +// SortByUpdatedTime sorts ListResourceGroups by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lrg ListResourceGroups) SortByUpdatedTime(inverse bool) ListResourceGroups { + if len(lrg.Data) < 2 { + return lrg + } + + sort.Slice(lrg.Data, func(i, j int) bool { + if inverse { + return lrg.Data[i].UpdatedTime > lrg.Data[j].UpdatedTime + } + + return lrg.Data[i].UpdatedTime < lrg.Data[j].UpdatedTime + }) + + return lrg +} + +// SortByDeletedTime sorts ListResourceGroups by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lrg ListResourceGroups) SortByDeletedTime(inverse bool) ListResourceGroups { + if len(lrg.Data) < 2 { + return lrg + } + + sort.Slice(lrg.Data, func(i, j int) bool { + if inverse { + return lrg.Data[i].DeletedTime > lrg.Data[j].DeletedTime + } + + return lrg.Data[i].DeletedTime < lrg.Data[j].DeletedTime + }) + + return lrg +} diff --git a/pkg/cloudapi/rg/update.go b/pkg/cloudapi/rg/update.go new file mode 100644 index 0000000..0e4c595 --- /dev/null +++ b/pkg/cloudapi/rg/update.go @@ -0,0 +1,79 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update resource group +type UpdateRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // New name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Max size of memory in MB + // Required: false + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + + // Max size of aggregated virtual disks in GB + // Required: false + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + + // Max number of CPU cores + // Required: false + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + + // Max sent/received network transfer peering + // Required: false + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + + // Max number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + + // Register computes in registration system + // Required: false + RegisterComputes bool `url:"registerComputes,omitempty" json:"registerComputes,omitempty"` + + // List of strings with pools i.e.: ["sep1_poolName1", "sep2_poolName2", etc] + // Required: false + UniqPools []string `url:"uniqPools,omitempty" json:"uniqPools,omitempty"` + + // if True the field will be cleared + // Default: false + // Required: false + ClearUniqPools bool `url:"clearUniqPools" json:"clearUniqPools"` +} + +// Update updates resource group +func (r RG) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/update" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/rg/usage.go b/pkg/cloudapi/rg/usage.go new file mode 100644 index 0000000..1a2069a --- /dev/null +++ b/pkg/cloudapi/rg/usage.go @@ -0,0 +1,40 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UsageRequest struct to get report of resource usage +type UsageRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Usage gets report resource usage on the resource group +func (r RG) Usage(ctx context.Context, req UsageRequest) (*RecordResourceUsage, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/usage" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordResourceUsage{} + err = json.Unmarshal(res, &info) + + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/stack.go b/pkg/cloudapi/stack.go new file mode 100644 index 0000000..ea4a48f --- /dev/null +++ b/pkg/cloudapi/stack.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/stack" +) + +// Accessing the Stack method group +func (ca *CloudAPI) Stack() *stack.Stack { + return stack.New(ca.client) +} diff --git a/pkg/cloudapi/stack/get.go b/pkg/cloudapi/stack/get.go new file mode 100644 index 0000000..38d56e9 --- /dev/null +++ b/pkg/cloudapi/stack/get.go @@ -0,0 +1,46 @@ +package stack + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get info of stack +type GetRequest struct { + // Find by ID + // Required: true + StackId uint64 `url:"stackId" json:"stackId" validate:"required"` +} + +// Get gets stack details by ID as an InfoStack struct +func (i Stack) Get(ctx context.Context, req GetRequest) (*InfoStack, error) { + res, err := i.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := InfoStack{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets stack details by ID as an array of bytes +func (i Stack) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/stack/get" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/stack/ids.go b/pkg/cloudapi/stack/ids.go new file mode 100644 index 0000000..0a6cc9d --- /dev/null +++ b/pkg/cloudapi/stack/ids.go @@ -0,0 +1,10 @@ +package stack + +// IDs gets array of StackIDs from ListStacks struct +func (ls ListStacks) IDs() []uint64 { + res := make([]uint64, 0, len(ls.Data)) + for _, s := range ls.Data { + res = append(res, s.ID) + } + return res +} diff --git a/pkg/cloudapi/stack/list.go b/pkg/cloudapi/stack/list.go new file mode 100644 index 0000000..90d6d95 --- /dev/null +++ b/pkg/cloudapi/stack/list.go @@ -0,0 +1,71 @@ +package stack + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of stacks +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of stacks as a ListStacks struct +func (i Stack) List(ctx context.Context, req ListRequest) (*ListStacks, error) { + + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListStacks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of stacks as an array of bytes +func (i Stack) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/stack/list" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/stack/models.go b/pkg/cloudapi/stack/models.go new file mode 100644 index 0000000..895344d --- /dev/null +++ b/pkg/cloudapi/stack/models.go @@ -0,0 +1,53 @@ +package stack + +// Main information about stack +type InfoStack struct { + // CPU allocation ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // Descr + Descr string `json:"descr"` + + // Drivers + Drivers []string `json:"drivers"` + + // ID + ID uint64 `json:"id"` + + // Mem allocation ratio + MemAllocationRatio float64 `json:"mem_allocation_ratio"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` +} + +// Information about stack in list +type ItemStack struct { + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` +} + +// List of stacks +type ListStacks struct { + + //List + Data []ItemStack `json:"data"` + + //Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/stack/stack.go b/pkg/cloudapi/stack/stack.go new file mode 100644 index 0000000..11e3b5d --- /dev/null +++ b/pkg/cloudapi/stack/stack.go @@ -0,0 +1,16 @@ +// Lists all the stack. +package stack + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to stack +type Stack struct { + client interfaces.Caller +} + +// Builder for stack endpoint +func New(client interfaces.Caller) *Stack { + return &Stack{ + client: client, + } +} diff --git a/pkg/cloudapi/tasks.go b/pkg/cloudapi/tasks.go new file mode 100644 index 0000000..474c3f6 --- /dev/null +++ b/pkg/cloudapi/tasks.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/tasks" +) + +// Accessing the Tasks method group +func (ca *CloudAPI) Tasks() *tasks.Tasks { + return tasks.New(ca.client) +} diff --git a/pkg/cloudapi/tasks/get.go b/pkg/cloudapi/tasks/get.go new file mode 100644 index 0000000..fdbdeea --- /dev/null +++ b/pkg/cloudapi/tasks/get.go @@ -0,0 +1,47 @@ +package tasks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about task +type GetRequest struct { + // ID of audit + // Required: true + AuditID string `url:"auditId" json:"auditId" validate:"required"` +} + +// Get gets background API task status and result as a RecordAsyncTask struct +func (t Tasks) Get(ctx context.Context, req GetRequest) (*RecordAsyncTask, error) { + res, err := t.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordAsyncTask{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets background API task status and result as an array of bytes +func (t Tasks) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/tasks/get" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/tasks/list.go b/pkg/cloudapi/tasks/list.go new file mode 100644 index 0000000..50f717e --- /dev/null +++ b/pkg/cloudapi/tasks/list.go @@ -0,0 +1,87 @@ +package tasks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of tasks +type ListRequest struct { + // Find by guId + // Required: false + TaskID string `url:"taskId,omitempty" json:"taskId,omitempty"` + + // Find by auditId + // Required: false + AuditID string `url:"auditId,omitempty" json:"auditId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by completed True or False + // Required: false + Completed interface{} `url:"completed,omitempty" json:"completed,omitempty" validate:"omitempty,isBool"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Find all tasks after point in time (unixtime) + // Required: false + UpdateTimeAt uint64 `url:"updateTimeAt,omitempty" json:"updateTimeAt,omitempty"` + + // Find all tasks before point in time (unixtime) + // Required: false + UpdateTimeTo uint64 `url:"updateTimeTo,omitempty" json:"updateTimeTo,omitempty"` + + // Page number + // Default: 0 + // Default: 0 + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Default: 0 + // Default: 0 + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of user API tasks with status PROCESSING as a ListTasks struct +func (t Tasks) List(ctx context.Context, req ListRequest) (*ListTasks, error) { + + res, err := t.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListTasks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of user API tasks with status PROCESSING as an array of bytes +func (t Tasks) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/tasks/list" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/tasks/models.go b/pkg/cloudapi/tasks/models.go new file mode 100644 index 0000000..c9133ab --- /dev/null +++ b/pkg/cloudapi/tasks/models.go @@ -0,0 +1,146 @@ +package tasks + +import ( + "errors" + "fmt" +) + +// Result structure of the task to provide methods +type Result struct { + Result interface{} `json:"result"` +} + +// Detailed information about task +type RecordAsyncTask struct { + // Audit ID + AuditID string `json:"auditId"` + + // Completed + Completed bool `json:"completed"` + + // Error + Error string `json:"error"` + + // List of logs + Log []string `json:"log"` + + // Final Result + Result + + // Stage + Stage string `json:"stage"` + + // Status + Status string `json:"status"` + + // Update time + UpdateTime uint64 `json:"updateTime"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// Detailed information about task +type ItemAsyncTask struct { + RecordAsyncTask + + // GUID + GUID string `json:"guid"` +} + +// List of tasks +type ListTasks struct { + Data []ItemAsyncTask `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// ID returns ID of cluster or WG or any other resource successfully created as a Result of the task. +// It returns error if Result does not contain any resource ID. +func (r Result) ID() (int, error) { + // check id from cluster - it comes as slice, like [1234, "cluster-name"] + slice, ok := r.Result.([]interface{}) + if ok { + if len(slice) == 0 { + return 0, fmt.Errorf("could not get ID from empty slice") + } + + idFloat64, ok := slice[0].(float64) + if !ok { + return 0, fmt.Errorf("could not get ID from first slice element (%v)", slice[0]) + } + + return int(idFloat64), nil + } + + // check id from other resources - it comes as id + idFloat64, ok := r.Result.(float64) + if ok { + return int(idFloat64), nil + } + + return 0, errors.New("could not get ID because result is neither slice nor number (%v)") +} + +// Name returns name of cluster or WG successfully created as a Result of the task. +// It returns error if Result does not contain k8s name. +func (r Result) Name() (string, error) { + slice, ok := r.Result.([]interface{}) + if !ok { + return "", fmt.Errorf("could not convert Result (%v) to slice", r.Result) + } + + if len(slice) < 2 { + return "", fmt.Errorf("could not get name from second slice element") + } + + var name string + name, ok = slice[1].(string) + if !ok { + return "", fmt.Errorf("could not get name from second slice element (%v)", slice[1]) + } + + return name, nil +} + +// ToMaps converts Result to a slice of maps containing back-up information as a result of the task. +// It returns error if Result does not contain back-up information. +func (r Result) ToMaps() ([]map[string]interface{}, error) { + slice, ok := r.Result.([]interface{}) + if !ok { + return nil, fmt.Errorf("could not convert Result (%v) to slice", r.Result) + } + + if len(slice) == 0 { + return nil, fmt.Errorf("could not get maps from empty slice") + } + + result := make([]map[string]interface{}, 0, len(slice)) + for _, s := range slice { + elem, ok := s.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("could not get map[string]interface{} from slice element (%v)", s) + } + result = append(result, elem) + } + + return result, nil +} + +// ToString converts Result to non-empty string. +// It returns error if Result is not a string or is an empty string. +func (r Result) ToString() (string, error) { + status, ok := r.Result.(string) + if !ok { + return "", fmt.Errorf("could not convert Result (%v) to string", r.Result) + } + + if status == "" { + return "", fmt.Errorf("info contains empty string") + } + + return status, nil +} diff --git a/pkg/cloudapi/tasks/tasks.go b/pkg/cloudapi/tasks/tasks.go new file mode 100644 index 0000000..67b9321 --- /dev/null +++ b/pkg/cloudapi/tasks/tasks.go @@ -0,0 +1,18 @@ +// User API tasks interface +package tasks + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to tasks +type Tasks struct { + client interfaces.Caller +} + +// Builder for tasks endpoints +func New(client interfaces.Caller) *Tasks { + return &Tasks{ + client, + } +} diff --git a/pkg/cloudapi/user.go b/pkg/cloudapi/user.go new file mode 100644 index 0000000..c5d7d02 --- /dev/null +++ b/pkg/cloudapi/user.go @@ -0,0 +1,7 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/user" + +func (ca *CloudAPI) User() *user.User { + return user.New(ca.client) +} diff --git a/pkg/cloudapi/user/api_list.go b/pkg/cloudapi/user/api_list.go new file mode 100644 index 0000000..55eadd1 --- /dev/null +++ b/pkg/cloudapi/user/api_list.go @@ -0,0 +1,41 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// APIListRequest struct for getting API list. +type APIListRequest struct { + // ID of the user. + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` +} + +// APIList gets a list of all API functions that a given user has +// access to according to their apiaccess group membership. +func (u User) APIList(ctx context.Context, req APIListRequest) (*APIsEndpoints, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/user/apiList" + + info := APIsEndpoints{} + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/user/authenticate.go b/pkg/cloudapi/user/authenticate.go new file mode 100644 index 0000000..4a64865 --- /dev/null +++ b/pkg/cloudapi/user/authenticate.go @@ -0,0 +1,38 @@ +package user + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuthenticateRequest struct to authenticate user. +type AuthenticateRequest struct { + // Username + // Required: true + Username string `url:"username" json:"username" validate:"required"` + + // Password + // Required: true + Password string `url:"password" json:"password" validate:"required"` +} + +// Authenticate evaluates the provided username and password and returns a session key. +// The session key can be used for doing api requests. E.g this is the authkey parameter in every actor request. +// A session key is only vallid for a limited time. +func (u User) Authenticate(ctx context.Context, req AuthenticateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/user/authenticate" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/user/brief.go b/pkg/cloudapi/user/brief.go new file mode 100644 index 0000000..dd3ac65 --- /dev/null +++ b/pkg/cloudapi/user/brief.go @@ -0,0 +1,26 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" +) + +// Brief gets information about user's enabled and disabled resources. +func (u User) Brief(ctx context.Context) (*BriefResources, error) { + url := "/cloudapi/user/brief" + + info := BriefResources{} + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudapi/user/get.go b/pkg/cloudapi/user/get.go new file mode 100644 index 0000000..0c74998 --- /dev/null +++ b/pkg/cloudapi/user/get.go @@ -0,0 +1,46 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get user details. +type GetRequest struct { + // Username + // Required: true + Username string `url:"username" json:"username" validate:"required"` +} + +// Get gets user details as an ItemUser struct. +func (u User) Get(ctx context.Context, req GetRequest) (*ItemUser, error) { + res, err := u.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + item := ItemUser{} + + err = json.Unmarshal(res, &item) + if err != nil { + return nil, err + } + + return &item, nil +} + +// GetRaw gets user details as an array of bytes +func (u User) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/user/get" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/user/get_audit.go b/pkg/cloudapi/user/get_audit.go new file mode 100644 index 0000000..43287dc --- /dev/null +++ b/pkg/cloudapi/user/get_audit.go @@ -0,0 +1,57 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" +) + +// GetAuditRequest struct for getting user's audits. +type GetAuditRequest struct { + // Find by api call. + // Required: false + Call string `url:"call,omitempty" json:"call,omitempty"` + + // Find all audits after point in time (unixtime) + // Required: false + TimestampAt uint64 `url:"timestampAt,omitempty" json:"timestampAt,omitempty"` + + // Find all audits before point in time (unixtime) + // Required: false + TimestampTo uint64 `url:"timestampTo,omitempty" json:"timestampTo,omitempty"` + + // find by HTTP max status code + // Required: false + MaxStatusCode uint64 `url:"maxStatusCode,omitempty" json:"maxStatusCode,omitempty"` + + // find by HTTP min status code + // Required: false + MinStatusCode uint64 `url:"minStatusCode,omitempty" json:"minStatusCode,omitempty"` + + // Page number. + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size, maximum - 100. + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// GetAudit gets user's audits. +func (u User) GetAudit(ctx context.Context, req GetAuditRequest) (ListAudits, error) { + url := "/cloudapi/user/getAudit" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return ListAudits{}, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return ListAudits{}, err + } + + return list, nil +} diff --git a/pkg/cloudapi/user/get_resource_consumption.go b/pkg/cloudapi/user/get_resource_consumption.go new file mode 100644 index 0000000..c4e79b0 --- /dev/null +++ b/pkg/cloudapi/user/get_resource_consumption.go @@ -0,0 +1,26 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" +) + +// GetResourceConsumption gets amount of consumed and reserved resources (cpu, ram, disk) by current user +func (u User) GetResourceConsumption(ctx context.Context) (*ResourceConsumption, error) { + url := "/cloudapi/user/getResourceConsumption" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + item := ResourceConsumption{} + + err = json.Unmarshal(res, &item) + if err != nil { + return nil, err + } + + return &item, nil +} diff --git a/pkg/cloudapi/user/is_valid_invite_user_token.go b/pkg/cloudapi/user/is_valid_invite_user_token.go new file mode 100644 index 0000000..f8e8f37 --- /dev/null +++ b/pkg/cloudapi/user/is_valid_invite_user_token.go @@ -0,0 +1,44 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to check if the inviteusertoken and emailaddress pair are valid and matching. +type IsValidInviteUserTokenRequest struct { + // InviteUserToken + // The token that was previously sent to the invited user email + // Required: true + InviteUserToken string `url:"inviteusertoken" json:"inviteusertoken" validate:"required"` + + // EmailAddress + // Email address for the user + // Required: true + EmailAddress string `url:"emailaddress" json:"emailaddress" validate:"required"` +} + +// IsValidInviteUserToken checks if the inviteusertoken and emailaddress pair are valid and matching. +func (u User) IsValidInviteUserToken(ctx context.Context, req IsValidInviteUserTokenRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/user/isValidInviteUserToken" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/user/models.go b/pkg/cloudapi/user/models.go new file mode 100644 index 0000000..03f746c --- /dev/null +++ b/pkg/cloudapi/user/models.go @@ -0,0 +1,181 @@ +package user + +import "strconv" + +type ItemUser struct { + // Data + Data interface{} `json:"data"` + + // EmailAddresses + EmailAddresses []string `json:"emailaddresses"` + + // Username + Username string `json:"username"` +} + +type ItemAudit struct { + // Call + Call string `json:"Call"` + + // Response time + ResponseTime ResponseTime `json:"Response Time"` + + // StatusCode + StatusCode StatusCode `json:"Status Code"` + + // Guid + GUID string `json:"Guid"` + + // Time + Time float64 `json:"Time"` +} + +type ListAudits struct { + // Data + Data []ItemAudit `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type ResponseTime float64 + +func (r *ResponseTime) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + *r = ResponseTime(-1) + + return nil + } + + res, err := strconv.ParseFloat(string(b), 64) + if err != nil { + return err + } + + *r = ResponseTime(res) + + return nil +} + +type StatusCode int64 + +func (s *StatusCode) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + *s = StatusCode(-1) + + return nil + } + + res, err := strconv.ParseInt(string(b), 10, 64) + if err != nil { + return err + } + + *s = StatusCode(res) + + return nil +} + +type BriefResources struct { + Accounts Accounts `json:"Accounts,omitempty"` + CSs CSs `json:"CSs,omitempty"` + Computes Computes `json:"Computes,omitempty"` + RGs RGs `json:"RGs,omitempty"` + VMs VMs `json:"VMs,omitempty"` +} + +type Accounts struct { + Disabled uint64 `json:"DISABLED,omitempty"` + Enabled uint64 `json:"ENABLED,omitempty"` +} + +type CSs struct { + Disabled uint64 `json:"DISABLED,omitempty"` + Enabled uint64 `json:"ENABLED,omitempty"` +} + +type Computes struct { + Started uint64 `json:"Started,omitempty"` + Stopped uint64 `json:"Stopped,omitempty"` +} + +type RGs struct { + Disabled uint64 `json:"DISABLED,omitempty"` + Enabled uint64 `json:"ENABLED,omitempty"` +} + +type VMs struct { + Halted uint64 `json:"Halted,omitempty"` + Running uint64 `json:"Running,omitempty"` +} + +type APIsEndpoints struct { + CloudAPI CloudAPIEndpoints `json:"cloudapi,omitempty"` + CloudBroker CloudBrokerEndpoints `json:"cloudbroker,omitempty"` + LibCloud LibCloudEndpoints `json:"libcloud,omitempty"` + System SystemEndpoints `json:"system,omitempty"` +} + +type CloudAPIEndpoints struct { + All bool `json:"ALL,omitempty"` +} + +type CloudBrokerEndpoints struct { + All bool `json:"ALL,omitempty"` +} + +type LibCloudEndpoints struct { + All bool `json:"ALL,omitempty"` +} + +type SystemEndpoints struct { + All bool `json:"ALL,omitempty"` +} + +type ResourceConsumption struct { + // Consumed + Consumed Resources `json:"Consumed"` + + // Reserved + Reserved Resources `json:"Reserved"` + + // Username + Username string `json:"username"` +} + +type Resources struct { + // CPU + CPU uint64 `json:"cpu"` + + // Disksize + DiskSize uint64 `json:"disksize"` + + // DiskSizeMax + DiskSizeMax uint64 `json:"disksizemax"` + + // ExtIPs + ExtIPs uint64 `json:"extips"` + + // ExtTraffic + ExtTraffic uint64 `json:"exttraffic"` + + // GPU + GPU uint64 `json:"gpu"` + + // RAM + RAM uint64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +// Disk usage +type DiskUsage struct { + // Disk size + DiskSize float64 `json:"disksize"` + + // Disk size max + DiskSizeMax float64 `json:"disksizemax"` +} + +type FoundElements []interface{} diff --git a/pkg/cloudapi/user/search.go b/pkg/cloudapi/user/search.go new file mode 100644 index 0000000..18182f6 --- /dev/null +++ b/pkg/cloudapi/user/search.go @@ -0,0 +1,33 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" +) + +// SearchRequest struct for searching user's elements. +type SearchRequest struct { + // Text to search + // Required: true + Text string `url:"text" json:"text" validate:"required"` +} + +// Search searches for user's elements. +func (u User) Search(ctx context.Context, req SearchRequest) (*FoundElements, error) { + url := "/cloudapi/user/search" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := FoundElements{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/user/set_data.go b/pkg/cloudapi/user/set_data.go new file mode 100644 index 0000000..d67cc0e --- /dev/null +++ b/pkg/cloudapi/user/set_data.go @@ -0,0 +1,38 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetDataRequest struct for setting extra user information. +type SetDataRequest struct { + // Data to set to user in json format + // Required: true + Data string `url:"data" json:"data" validation:"required"` +} + +// SetData sets extra user information. +func (u User) SetData(ctx context.Context, req SetDataRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/user/setData" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/user/user.go b/pkg/cloudapi/user/user.go new file mode 100644 index 0000000..9a1ff2c --- /dev/null +++ b/pkg/cloudapi/user/user.go @@ -0,0 +1,15 @@ +package user + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to User +type User struct { + client interfaces.Caller +} + +// Builder for User endpoints +func New(client interfaces.Caller) *User { + return &User{ + client: client, + } +} diff --git a/pkg/cloudapi/vfpool.go b/pkg/cloudapi/vfpool.go new file mode 100644 index 0000000..2d7a912 --- /dev/null +++ b/pkg/cloudapi/vfpool.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/vfpool" + +// Accessing the VFPool method group +func (ca *CloudAPI) VFPool() *vfpool.VFPool { + return vfpool.New(ca.client) +} diff --git a/pkg/cloudapi/vfpool/filter.go b/pkg/cloudapi/vfpool/filter.go new file mode 100644 index 0000000..78cbbf3 --- /dev/null +++ b/pkg/cloudapi/vfpool/filter.go @@ -0,0 +1,99 @@ +package vfpool + +// FilterByID returns ListVFPool with specified ID. +func (lvfp ListVFPool) FilterByID(id uint64) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.ID == id + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByGID returns ListVFPool with specified GID. +func (lvfp ListVFPool) FilterByGID(gid uint64) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.GID == gid + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByName returns ListVFPool with specified Name. +func (lvfp ListVFPool) FilterByName(name string) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.Name == name + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByDescription returns ListVFPool with specified Description. +func (lvfp ListVFPool) FilterByDescription(description string) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.Description == description + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByStatus returns ListVFPool with specified Status. +func (lvfp ListVFPool) FilterByStatus(status string) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.Status == status + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByAccountAccess returns ListVFPool with specified AccountAccess. +func (lvfp ListVFPool) FilterByAccountAccess(accountAccess uint64) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + for _, i := range ivfp.AccountAccess { + if i == accountAccess { + return true + } + } + return false + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByRGAccess returns ListVFPool with specified RGAccess. +func (lvfp ListVFPool) FilterByRGAccess(rgAccess uint64) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + for _, i := range ivfp.RGAccess { + if i == rgAccess { + return true + } + } + return false + } + + return lvfp.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListVFPool based on a user-specified predicate. +func (lvfp ListVFPool) FilterFunc(predicate func(ItemVFPool) bool) ListVFPool { + var result ListVFPool + + for _, item := range lvfp.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemVFPool +// If none was found, returns an empty struct. +func (lvfp ListVFPool) FindOne() ItemVFPool { + if lvfp.EntryCount == 0 { + return ItemVFPool{} + } + + return lvfp.Data[0] +} diff --git a/pkg/cloudapi/vfpool/filter_test.go b/pkg/cloudapi/vfpool/filter_test.go new file mode 100644 index 0000000..66d78a3 --- /dev/null +++ b/pkg/cloudapi/vfpool/filter_test.go @@ -0,0 +1,138 @@ +package vfpool + +import "testing" + +var vfpools = ListVFPool{ + Data: []ItemVFPool{ + { + AccountAccess: []uint64{1, 2}, + Description: "descr", + GID: 1, + ID: 1, + Name: "name", + RGAccess: []uint64{3, 4}, + Status: "ENABLED", + }, + { + AccountAccess: []uint64{}, + Description: "", + GID: 2, + ID: 2, + Name: "name2", + RGAccess: []uint64{}, + Status: "DISABLED", + }, + { + AccountAccess: []uint64{7, 8}, + Description: "", + GID: 215, + ID: 3, + Name: "name3", + RGAccess: []uint64{5, 6}, + Status: "DISABLED", + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := vfpools.FilterByID(1).FindOne() + + if actual.ID != 1 { + t.Fatal("expected ID 1, found: ", actual.ID) + } +} + +func TestFilterByGID(t *testing.T) { + var gid uint64 = 1 + actual := vfpools.FilterByGID(gid).FindOne() + + if actual.GID != gid { + t.Fatal("expected ", gid, " found: ", actual.GID) + } +} + +func TestFilterByName(t *testing.T) { + name := "name" + actual := vfpools.FilterByName(name).FindOne() + + if actual.Name != name { + t.Fatal("expected ", name, " found: ", actual.Name) + } +} + +func TestFilterByDescription(t *testing.T) { + description := "descr" + actual := vfpools.FilterByDescription(description).FindOne() + + if actual.Description != description { + t.Fatal("expected ", description, " found: ", actual.Description) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := vfpools.FilterByStatus("ENABLED") + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterByAccountAccess(t *testing.T) { + var account uint64 = 1 + actual := vfpools.FilterByAccountAccess(account) + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + var found bool + for _, a := range item.AccountAccess { + if a == account { + found = true + } + } + + if !found { + t.Fatalf("expected account access %d, found: %v", account, item.AccountAccess) + } + } +} + +func TestFilterByRGAccess(t *testing.T) { + var rg uint64 = 3 + actual := vfpools.FilterByRGAccess(rg) + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + var found bool + for _, r := range item.RGAccess { + if r == rg { + found = true + } + } + + if !found { + t.Fatalf("expected account access %d, found: %v", rg, item.RGAccess) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := vfpools.FilterFunc(func(ivfpool ItemVFPool) bool { + return ivfpool.GID == ivfpool.ID + }) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 elements, found: ", len(actual.Data)) + } +} diff --git a/pkg/cloudapi/vfpool/get.go b/pkg/cloudapi/vfpool/get.go new file mode 100644 index 0000000..e5fc736 --- /dev/null +++ b/pkg/cloudapi/vfpool/get.go @@ -0,0 +1,46 @@ +package vfpool + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about vfpool device +type GetRequest struct { + // ID of vfpool device + // Required: true + VFPoolID uint64 `url:"id" json:"id" validate:"required"` +} + +// Get gets detailed information about vfpool device as a RecordVFPool struct +func (v VFPool) Get(ctx context.Context, req GetRequest) (*RecordVFPool, error) { + res, err := v.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordVFPool{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed information about vfpool device as an array of bytes +func (v VFPool) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vfpool/get" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/vfpool/ids.go b/pkg/cloudapi/vfpool/ids.go new file mode 100644 index 0000000..1a58061 --- /dev/null +++ b/pkg/cloudapi/vfpool/ids.go @@ -0,0 +1,19 @@ +package vfpool + +// IDs gets array of VFPool IDs from ListVFPool struct +func (lv ListVFPool) IDs() []uint64 { + res := make([]uint64, 0, len(lv.Data)) + for _, e := range lv.Data { + res = append(res, e.ID) + } + return res +} + +// IDs gets array of VF IDs from VFSInfoList struct +func (lv VFSInfoList) IDs() []uint64 { + res := make([]uint64, 0, len(lv)) + for _, e := range lv { + res = append(res, e.ID) + } + return res +} diff --git a/pkg/cloudapi/vfpool/list.go b/pkg/cloudapi/vfpool/list.go new file mode 100644 index 0000000..2782112 --- /dev/null +++ b/pkg/cloudapi/vfpool/list.go @@ -0,0 +1,83 @@ +package vfpool + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of vfpool devices +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by Grid ID + // Required: false + GID uint64 `url:"gid,omitempty" json:"gid,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by account Access + // Required: false + AccountAccess uint64 `url:"accountAccess,omitempty" json:"accountAccess,omitempty"` + + // Find by resource group Access + // Required: false + RGAccess uint64 `url:"rgAccess,omitempty" json:"rgAccess,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all available vfpool devices as a ListVFPool struct +func (v VFPool) List(ctx context.Context, req ListRequest) (*ListVFPool, error) { + + res, err := v.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListVFPool{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all available vfpool devices as an array of bytes +func (v VFPool) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vfpool/list" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/vfpool/models.go b/pkg/cloudapi/vfpool/models.go new file mode 100644 index 0000000..3ecd53b --- /dev/null +++ b/pkg/cloudapi/vfpool/models.go @@ -0,0 +1,116 @@ +package vfpool + +// Main information about vfpool device +type ItemVFPool struct { + // AccountAccess + AccountAccess []uint64 `json:"accountAccess"` + + // CreatedTime + CreatedTime uint64 `json:"createdTime"` + + // Description + Description string `json:"description"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // RGAccess + RGAccess []uint64 `json:"rgAccess"` + + // Status + Status string `json:"status"` + + // UpdatedTime + UpdatedTime uint64 `json:"updatedTime"` + + // VFS + VFS []VFS `json:"vfs"` +} + +// List of information about vfpool devices +type ListVFPool struct { + Data []ItemVFPool `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about vfpool device +type RecordVFPool struct { + // AccountAccess + AccountAccess []uint64 `json:"accountAccess"` + + // CreatedTime + CreatedTime uint64 `json:"createdTime"` + + // Description + Description string `json:"description"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // RGAccess + RGAccess []uint64 `json:"rgAccess"` + + // Status + Status string `json:"status"` + + // UpdatedTime + UpdatedTime uint64 `json:"updatedTime"` + + // VFS + VFS []VFS `json:"vfs"` +} + +// VFS struct +type VFS struct { + // NodeID + NodeID uint64 `json:"nodeId"` + + // UpdatedTime + VFList VFList `json:"vfList"` +} + +// VFList struct +type VFList []VFItem + +// VFItem struct +type VFItem struct { + // NicName + NicName string `json:"nicName"` + + // VFSInfo list + VFSInfo VFSInfoList `json:"vfsInfo"` +} + +// VFSInfoList struct +type VFSInfoList []VFSInfoItem + +// VFSInfoItem struct +type VFSInfoItem struct { + // ID + ID uint64 `json:"id"` + + // Claimed + Claimed bool `json:"claimed"` + + // VM ID + VMID uint64 `json:"vmId"` +} diff --git a/pkg/cloudapi/vfpool/serialize.go b/pkg/cloudapi/vfpool/serialize.go new file mode 100644 index 0000000..d840502 --- /dev/null +++ b/pkg/cloudapi/vfpool/serialize.go @@ -0,0 +1,59 @@ +package vfpool + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lvfpool ListVFPool) Serialize(params ...string) (serialization.Serialized, error) { + if lvfpool.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lvfpool, prefix, indent) + } + + return json.Marshal(lvfpool) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (rvfpool RecordVFPool) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(rvfpool, prefix, indent) + } + + return json.Marshal(rvfpool) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ivfpool ItemVFPool) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ivfpool, prefix, indent) + } + + return json.Marshal(ivfpool) +} diff --git a/pkg/cloudapi/vfpool/vfpool.go b/pkg/cloudapi/vfpool/vfpool.go new file mode 100644 index 0000000..15f7996 --- /dev/null +++ b/pkg/cloudapi/vfpool/vfpool.go @@ -0,0 +1,18 @@ +// API Actor for managing vfpool device +package vfpool + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to vfpool +type VFPool struct { + client interfaces.Caller +} + +// Builder for vfpool endpoints +func New(client interfaces.Caller) *VFPool { + return &VFPool{ + client, + } +} diff --git a/pkg/cloudapi/vins.go b/pkg/cloudapi/vins.go new file mode 100644 index 0000000..f6badd0 --- /dev/null +++ b/pkg/cloudapi/vins.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi/vins" +) + +// Accessing the VINS method group +func (ca *CloudAPI) VINS() *vins.VINS { + return vins.New(ca.client) +} diff --git a/pkg/cloudapi/vins/audits.go b/pkg/cloudapi/vins/audits.go new file mode 100644 index 0000000..4358b99 --- /dev/null +++ b/pkg/cloudapi/vins/audits.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuditsRequest struct to get audits +type AuditsRequest struct { + // ID of the VINS + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Audits gets audit records for the specified VINS object +func (v VINS) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/audits" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/vins/create_in_account.go b/pkg/cloudapi/vins/create_in_account.go new file mode 100644 index 0000000..6f24aa7 --- /dev/null +++ b/pkg/cloudapi/vins/create_in_account.go @@ -0,0 +1,105 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type Route struct { + // Destination network + Destination string `url:"destination" json:"destination" validate:"required"` + + //Destination network mask in 255.255.255.255 format + Netmask string `url:"netmask" json:"netmask" validate:"required"` + + //Next hop host, IP address from ViNS ID free IP pool + Gateway string `url:"gateway" json:"gateway" validate:"required"` +} + +// CreateInAccountRequest struct to create VINS in account +type CreateInAccountRequest struct { + // VINS name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Grid ID + // Required: false + GID uint64 `url:"gid,omitempty" json:"gid,omitempty"` + + // Private network IP CIDR + // Required: false + IPCIDR string `url:"ipcidr,omitempty" json:"ipcidr,omitempty"` + + // Description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // List of DNS ip address + // Required: false + DNSList []string `url:"dnsList" json:"dnsList,omitempty"` + + // Number of pre created reservations + // Required: false + PreReservationsNum uint64 `url:"preReservationsNum,omitempty" json:"preReservationsNum,omitempty"` + + // List of static routes, each item must have destination, netmask, and gateway fields + // Required: false + Routes []Route `url:"-" json:"routes,omitempty" validate:"omitempty,dive"` +} + +type wrapperCreateRequestInAcc struct { + CreateInAccountRequest + Routes []string `url:"routes,omitempty"` +} + +// CreateInAccount creates VINS in account level +func (v VINS) CreateInAccount(ctx context.Context, req CreateInAccountRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var routes []string + + if len(req.Routes) != 0 { + routes = make([]string, 0, len(req.Routes)) + + for r := range req.Routes { + b, err := json.Marshal(req.Routes[r]) + if err != nil { + return 0, err + } + + routes = append(routes, string(b)) + } + } else { + routes = []string{} + } + + reqWrapped := wrapperCreateRequestInAcc{ + CreateInAccountRequest: req, + Routes: routes, + } + + url := "/cloudapi/vins/createInAccount" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/create_in_rg.go b/pkg/cloudapi/vins/create_in_rg.go new file mode 100644 index 0000000..c23eeb1 --- /dev/null +++ b/pkg/cloudapi/vins/create_in_rg.go @@ -0,0 +1,99 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateInRGRequest struct to create VINS in resource group +type CreateInRGRequest struct { + // VINS name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Private network IP CIDR + // Required: false + IPCIDR string `url:"ipcidr,omitempty" json:"ipcidr,omitempty"` + + // External network ID + // Required: false + // -1 - not connect to extnet, 0 - auto select, 1+ - extnet ID + ExtNetID int64 `url:"extNetId" json:"extNetId"` + + // External IP, related only for extNetId >= 0 + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // List of DNS ip address + // Required: false + DNSList []string `url:"dnsList" json:"dnsList,omitempty"` + + // Number of pre created reservations + // Required: false + PreReservationsNum uint64 `url:"preReservationsNum,omitempty" json:"preReservationsNum,omitempty"` + + // List of static routes, each item must have destination, netmask, and gateway fields + // Required: false + Routes []Route `url:"-" json:"routes,omitempty" validate:"omitempty,dive"` +} + +type wrapperCreateRequestInRG struct { + CreateInRGRequest + Routes []string `url:"routes,omitempty"` +} + +// CreateInRG creates VINS in resource group level +func (v VINS) CreateInRG(ctx context.Context, req CreateInRGRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var routes []string + + if len(req.Routes) != 0 { + routes = make([]string, 0, len(req.Routes)) + + for r := range req.Routes { + b, err := json.Marshal(req.Routes[r]) + if err != nil { + return 0, err + } + + routes = append(routes, string(b)) + } + } else { + routes = []string{} + } + + reqWrapped := wrapperCreateRequestInRG{ + CreateInRGRequest: req, + Routes: routes, + } + + url := "/cloudapi/vins/createInRG" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/delete.go b/pkg/cloudapi/vins/delete.go new file mode 100644 index 0000000..64e93b6 --- /dev/null +++ b/pkg/cloudapi/vins/delete.go @@ -0,0 +1,50 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete VINS +type DeleteRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Set to True if you want force delete non-empty VINS. + // Primarily, VINS is considered non-empty if it has virtual machines connected to it, + // and force flag will detach them from the VINS being deleted. + // Otherwise method will return an error + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` + + // Set to True if you want to destroy VINS and all linked resources, if any, immediately. + // Otherwise, they will be placed into recycle bin and could be restored later within the recycle bin's purge period + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes VINS +func (v VINS) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/delete" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/disable_enable.go b/pkg/cloudapi/vins/disable_enable.go new file mode 100644 index 0000000..eaa0c71 --- /dev/null +++ b/pkg/cloudapi/vins/disable_enable.go @@ -0,0 +1,62 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableEnableRequest struct to disable/enable VINS +type DisableEnableRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Disable disables VINS +func (v VINS) Disable(ctx context.Context, req DisableEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/disable" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil + +} + +// Enable enables VINS +func (v VINS) Enable(ctx context.Context, req DisableEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/enable" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil + +} diff --git a/pkg/cloudapi/vins/dns_apply.go b/pkg/cloudapi/vins/dns_apply.go new file mode 100644 index 0000000..7ef8495 --- /dev/null +++ b/pkg/cloudapi/vins/dns_apply.go @@ -0,0 +1,43 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DNSApplyRequest struct to apply new DNS list in VINS +type DNSApplyRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // List of DNS ip address + // Required: false + DNSList []string `url:"dnsList" json:"dnsList"` +} + +// DNSApply applies new DNS list in VINS +func (v VINS) DNSApply(ctx context.Context, req DNSApplyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/dnsApply" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil + +} diff --git a/pkg/cloudapi/vins/extnet_connect.go b/pkg/cloudapi/vins/extnet_connect.go new file mode 100644 index 0000000..3ccb048 --- /dev/null +++ b/pkg/cloudapi/vins/extnet_connect.go @@ -0,0 +1,46 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ExtNetConnectRequest struct to connect to external network +type ExtNetConnectRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // External network ID + // Required: false + NetID uint64 `url:"netId,omitempty" json:"netId,omitempty"` + + // Directly set IP address + // Required: false + IP string `url:"ip,omitempty" json:"ip,omitempty"` +} + +// ExtNetConnect connect VINS to external network +func (v VINS) ExtNetConnect(ctx context.Context, req ExtNetConnectRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/extNetConnect" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/extnet_disconnect.go b/pkg/cloudapi/vins/extnet_disconnect.go new file mode 100644 index 0000000..492b6b9 --- /dev/null +++ b/pkg/cloudapi/vins/extnet_disconnect.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ExtNetDisconnectRequest struct to disconnect VINS from external network +type ExtNetDisconnectRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// ExtNetDisconnect disconnects VINS from external network +func (v VINS) ExtNetDisconnect(ctx context.Context, req ExtNetDisconnectRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/extNetDisconnect" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/extnet_list.go b/pkg/cloudapi/vins/extnet_list.go new file mode 100644 index 0000000..632ff3c --- /dev/null +++ b/pkg/cloudapi/vins/extnet_list.go @@ -0,0 +1,41 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ExtNetListRequest struct to get list of VINS external network connections +type ExtNetListRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// ExtNetList shows list of VINS external network connections +func (v VINS) ExtNetList(ctx context.Context, req ExtNetListRequest) (*ListExtNets, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/extNetList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := &ListExtNets{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil + +} diff --git a/pkg/cloudapi/vins/filter.go b/pkg/cloudapi/vins/filter.go new file mode 100644 index 0000000..d635887 --- /dev/null +++ b/pkg/cloudapi/vins/filter.go @@ -0,0 +1,80 @@ +package vins + +// FilterByID returns ListVINS with specified ID. +func (lv ListVINS) FilterByID(id uint64) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.ID == id + } + + return lv.FilterFunc(predicate) +} + +// FilterByName returns ListVINS with specified Name. +func (lv ListVINS) FilterByName(name string) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.Name == name + } + + return lv.FilterFunc(predicate) +} + +// FilterByAccountID returns ListVINS with specified AccountID. +func (lv ListVINS) FilterByAccountID(accountID uint64) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.AccountID == accountID + } + + return lv.FilterFunc(predicate) +} + +// FilterByCreatedBy returns ListVINS created by specified user. +func (lv ListVINS) FilterByCreatedBy(createdBy string) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.CreatedBy == createdBy + } + + return lv.FilterFunc(predicate) +} + +// FilterByUpdatedBy returns ListVINS updated by specified user. +func (lv ListVINS) FilterByUpdatedBy(updatedBy string) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.UpdatedBy == updatedBy + } + + return lv.FilterFunc(predicate) +} + +// FilterByDeletedBy returns ListVINS deleted by specified user. +func (lv ListVINS) FilterByDeletedBy(deletedBy string) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.DeletedBy == deletedBy + } + + return lv.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListVINS based on a user-specified predicate. +func (lv ListVINS) FilterFunc(predicate func(ItemVINS) bool) ListVINS { + var result ListVINS + + for _, item := range lv.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemVINS +// If none was found, returns an empty struct. +func (lv ListVINS) FindOne() ItemVINS { + if len(lv.Data) == 0 { + return ItemVINS{} + } + + return lv.Data[0] +} diff --git a/pkg/cloudapi/vins/filter_test.go b/pkg/cloudapi/vins/filter_test.go new file mode 100644 index 0000000..5d4ccba --- /dev/null +++ b/pkg/cloudapi/vins/filter_test.go @@ -0,0 +1,121 @@ +package vins + +import "testing" + +var vinsItems = ListVINS{ + Data: []ItemVINS{ + { + AccountID: 1, + AccountName: "std", + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1676898844, + DeletedBy: "", + DeletedTime: 0, + ExternalIP: "", + ID: 1, + Name: "vins01", + Network: "192.168.1.0/24", + RGID: 7971, + RGName: "rg_01", + Status: "ENABLED", + UpdatedBy: "", + UpdatedTime: 0, + VXLANID: 3544, + }, + { + AccountID: 2, + AccountName: "std2", + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1676898948, + DeletedBy: "", + DeletedTime: 0, + ExternalIP: "", + ID: 2, + Name: "vins02", + Network: "192.168.2.0/24", + RGID: 7972, + RGName: "rg_02", + Status: "ENABLED", + UpdatedBy: "", + UpdatedTime: 0, + VXLANID: 3545, + }, + { + AccountID: 3, + AccountName: "std3", + CreatedBy: "sample_user_2@decs3o", + CreatedTime: 1676899026, + DeletedBy: "", + DeletedTime: 0, + ExternalIP: "", + ID: 3, + Name: "vins03", + Network: "192.168.3.0/24", + RGID: 7973, + RGName: "rg_03", + Status: "DISABLED", + UpdatedBy: "", + UpdatedTime: 0, + VXLANID: 3546, + }, + }, + + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := vinsItems.FilterByID(2).FindOne() + + if actual.ID != 2 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := vinsItems.FilterByName("vins01").FindOne() + + if actual.Name != "vins01" { + t.Fatal("expected Name 'vins01', found: ", actual.Name) + } +} + +func TestFilterByAccountID(t *testing.T) { + actual := vinsItems.FilterByAccountID(3).FindOne() + + if actual.AccountID != 3 { + t.Fatal("expected AccountID 3, found: ", actual.AccountID) + } +} + +func TestFilterByCreatedBy(t *testing.T) { + actual := vinsItems.FilterByCreatedBy("sample_user_1@decs3o") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedBy != "sample_user_1@decs3o" { + t.Fatal("expected CreatedBy 'sample_user_1@decs3o', found: ", item.CreatedBy) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := vinsItems.FilterFunc(func(iv ItemVINS) bool { + return iv.RGID == 7971 + }). + FindOne() + + if actual.RGID != 7971 { + t.Fatal("expected RGID 7971, found: ", actual.RGID) + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := vinsItems.SortByCreatedTime(false) + + if actual.Data[0].CreatedTime != 1676898844 || actual.Data[2].CreatedTime != 1676899026 { + t.Fatal("expected ascending order, found descending") + } +} diff --git a/pkg/cloudapi/vins/get.go b/pkg/cloudapi/vins/get.go new file mode 100644 index 0000000..3e2e5b5 --- /dev/null +++ b/pkg/cloudapi/vins/get.go @@ -0,0 +1,46 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about VINS +type GetRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Get gets information about VINS by ID as a RecordVINS struct +func (v VINS) Get(ctx context.Context, req GetRequest) (*RecordVINS, error) { + res, err := v.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordVINS{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about VINS by ID as an array of bytes +func (v VINS) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/get" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/vins/ids.go b/pkg/cloudapi/vins/ids.go new file mode 100644 index 0000000..6ec9a3d --- /dev/null +++ b/pkg/cloudapi/vins/ids.go @@ -0,0 +1,64 @@ +package vins + +// IDs gets array of VINSIDs from ListVINS struct +func (lv ListVINS) IDs() []uint64 { + res := make([]uint64, 0, len(lv.Data)) + for _, v := range lv.Data { + res = append(res, v.ID) + } + return res +} + +// IDs gets array of ExtNetIDs from ListExtNets struct +func (le ListExtNets) IDs() []uint64 { + res := make([]uint64, 0, len(le.Data)) + for _, e := range le.Data { + res = append(res, e.ExtNetID) + } + return res +} + +// IDs gets array of ComputeIDs from ListVINSComputes struct +func (lvc ListVINSComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lvc)) + for _, vc := range lvc { + res = append(res, vc.ID) + } + return res +} + +// IDs gets array of NATRuleConfigIDs from ListNATRulesConfig struct +func (lnrc ListNATRulesConfig) IDs() []uint64 { + res := make([]uint64, 0, len(lnrc)) + for _, nrc := range lnrc { + res = append(res, nrc.ID) + } + return res +} + +// IDs gets array of NATRuleIDs from ListNATRules struct +func (lnr ListNATRules) IDs() []uint64 { + res := make([]uint64, 0, len(lnr.Data)) + for _, nr := range lnr.Data { + res = append(res, nr.ID) + } + return res +} + +// IDs gets array of StaticRouteIDs from ListStaticRoutes struct +func (lsr ListStaticRoutes) IDs() []uint64 { + res := make([]uint64, 0, len(lsr.Data)) + for _, sr := range lsr.Data { + res = append(res, sr.ID) + } + return res +} + +// IDs gets array of RouteIDs from ListRoutes struct +func (lr ListRoutes) IDs() []uint64 { + res := make([]uint64, 0, len(lr)) + for _, r := range lr { + res = append(res, r.ID) + } + return res +} diff --git a/pkg/cloudapi/vins/ip_list.go b/pkg/cloudapi/vins/ip_list.go new file mode 100644 index 0000000..950cabf --- /dev/null +++ b/pkg/cloudapi/vins/ip_list.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPListRequest struct for DHCP IP +type IPListRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// IPList shows DHCP IP reservations on VINS +func (v VINS) IPList(ctx context.Context, req IPListRequest) (*ListIPs, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/ipList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := &ListIPs{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/vins/ip_release.go b/pkg/cloudapi/vins/ip_release.go new file mode 100644 index 0000000..88df0fa --- /dev/null +++ b/pkg/cloudapi/vins/ip_release.go @@ -0,0 +1,48 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPReleaseRequest struct for IP release +type IPReleaseRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // IP address + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // MAC address + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` +} + +// IPRelese delete IP reservation matched by specified IP & MAC address combination. +// If both IP and MAC address are empty strings, all IP reservations will be deleted. +func (v VINS) IPRelese(ctx context.Context, req IPReleaseRequest) (bool, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/ipRelease" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/ip_reserve.go b/pkg/cloudapi/vins/ip_reserve.go new file mode 100644 index 0000000..a836f59 --- /dev/null +++ b/pkg/cloudapi/vins/ip_reserve.go @@ -0,0 +1,57 @@ +package vins + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPReserveRequest struct for IP reserve +type IPReserveRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Type of the reservation + // Should be one of: + // - DHCP + // - VIP + // - EXCLUDE + // Required: true + Type string `url:"type" json:"type" validate:"vinsType"` + + // IP address to use. Non-empty string is required for type "EXCLUDE". + // Ignored for types "DHCP" and "VIP". + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // MAC address to associate with IP reservation. + // Ignored for type "EXCLUDE", + // non-empty string is required for "DHCP" and "VIP" + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` + + // ID of the compute, associated with this reservation of type "DHCP". + // Ignored for other types + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` +} + +// IPReserve creates reservation on ViNS DHCP +func (v VINS) IPReserve(ctx context.Context, req IPReserveRequest) (string, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/ipReserve" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/vins/list.go b/pkg/cloudapi/vins/list.go new file mode 100644 index 0000000..59e7711 --- /dev/null +++ b/pkg/cloudapi/vins/list.go @@ -0,0 +1,83 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of VINSes +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by external network IP + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Find by VNF Device id + // Required: false + VNFDevId uint64 `url:"vnfdevId,omitempty" json:"vnfdevId,omitempty"` + + // Include deleted + // Required: false + IncludeDeleted bool `url:"includeDeleted,omitempty" json:"includeDeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of VINSes available for current user as a ListVINS struct +func (v VINS) List(ctx context.Context, req ListRequest) (*ListVINS, error) { + + res, err := v.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of VINSes available for current user as an array of bytes +func (v VINS) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/list" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/vins/list_deleted.go b/pkg/cloudapi/vins/list_deleted.go new file mode 100644 index 0000000..4e67093 --- /dev/null +++ b/pkg/cloudapi/vins/list_deleted.go @@ -0,0 +1,76 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted VINSes +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by external network IP + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Find by VNF Device id + // Required: false + VNFDevID uint64 `url:"vnfdevId,omitempty" json:"vnfdevId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list of deleted VINSes available for current user +func (v VINS) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListVINS, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/listDeleted" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/vins/models.go b/pkg/cloudapi/vins/models.go new file mode 100644 index 0000000..7c97216 --- /dev/null +++ b/pkg/cloudapi/vins/models.go @@ -0,0 +1,822 @@ +package vins + +// Main information about VINS +type ItemVINS struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // External IP + ExternalIP string `json:"externalIP"` + + // Extnet ID + ExtnetId uint64 `json:"extnetId"` + + // Free IPs + FreeIPs int64 `json:"freeIPs"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // VXLAN ID + VXLANID uint64 `json:"vxlanId"` +} + +// List of VINSes +type ListVINS struct { + // Data + Data []ItemVINS `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// List of VINSes search result +type SearchListVINS []ItemVINS + +// Main information about audit +type ItemAudit struct { + // Call + Call string `json:"call"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List of audits +type ListAudits []ItemAudit + +// Main information about external network +type ItemExtNet struct { + // Default GW + DefaultGW string `json:"default_gw"` + + // External network ID + ExtNetID uint64 `json:"ext_net_id"` + + // IP + IP string `json:"ip"` + + // Prefix len + PrefixLen uint64 `json:"prefixlen"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` +} + +// List of external networks +type ListExtNets struct { + // Data + Data []ItemExtNet `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about IP +type ItemIP struct { + // Client type + ClientType string `json:"clientType"` + + // Domain name + DomainName string `json:"domainname"` + + // Hostname + Hostname string `json:"hostname"` + + // IP + IP string `json:"ip"` + + // MAC + MAC string `json:"mac"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmId"` +} + +// List of IPs +type ListIPs struct { + // Data + Data []ItemIP `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about VNF device +type RecordVNFDev struct { + // CKey + CKey string `json:"_ckey"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Capabilities + Capabilities []string `json:"capabilities"` + + // Config + Config RecordVNFConfig `json:"config"` + + // Config saved + ConfigSaved bool `json:"configSaved"` + + // CustomPreConfig + CustomPreConfig bool `json:"customPrecfg"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // List of interfaces + Interfaces ListVNFInterfaces `json:"interfaces"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // List of VINS IDs + VINS []uint64 `json:"vins"` +} + +// VNF config +type RecordVNFConfig struct { + // MGMT + MGMT RecordMGMT `json:"mgmt"` + + // Resources + Resources RecordResources `json:"resources"` +} + +// Main information about MGMT +type RecordMGMT struct { + // IP address + IPAddress string `json:"ipaddr"` + + // Password + Password string `json:"password"` + + // SSH key + SSHKey string `json:"sshkey"` + + // User + User string `json:"user"` +} + +// Main information about resource +type RecordResources struct { + // Number of CPU + CPU uint64 `json:"cpu"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // UUID + UUID string `json:"uuid"` +} + +// Main information about VNF interface +type ItemVNFInterface struct { + // Bus number + BusNumber uint64 `json:"bus_number"` + + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Default GW + DefGW string `json:"defGw"` + + // Enabled + Enabled bool `json:"enabled"` + + // FLIPGroup ID + FLIPGroupID uint64 `json:"flipgroupId"` + + // GUID + GUID string `json:"guid"` + + // IP address + IPAddress string `json:"ipAddress"` + + // Listen SSH + ListenSSH bool `json:"listenSsh"` + + // MAC + MAC string `json:"mac"` + + // Maximum transmission unit + MTU uint64 `json:"mtu"` + + // Libvirt Settings + LibvirtSettings LibvirtSettings `json:"libvirtSettings"` + + // Name + Name string `json:"name"` + + // Network type + NetID uint64 `json:"netId"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network type + NetType string `json:"netType"` + + // NodeID + NodeID int64 `json:"nodeId"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // QOS + QOS QOS `json:"qos"` + + // Target + Target string `json:"target"` + + // Type + Type string `json:"type"` + + // List of VNF IDs + VNFs []uint64 `json:"vnfs"` +} + +// Main information about QOS +type QOS struct { + // ERate + ERate uint64 `json:"eRate"` + + // GUID + GUID string `json:"guid"` + + // InBurst + InBurst uint64 `json:"inBurst"` + + // InRate + InRate uint64 `json:"inRate"` +} + +// List of VNF interfaces +type ListVNFInterfaces []ItemVNFInterface + +// Main information about VINS compute +type ItemVINSCompute struct { + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` +} + +// List of VINS computes +type ListVINSComputes []ItemVINSCompute + +// Detailed information about VNF +type RecordVNFs struct { + // DHCP + DHCP RecordDHCP `json:"DHCP"` + + // GW + GW RecordGW `json:"GW"` + + // NAT + NAT RecordNAT `json:"NAT"` +} + +// Main information about NAT +type RecordNAT struct { + // CKey + CKey string `json:"_ckey"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Config + Config NATConfig `json:"config"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Detailed information about devices + Devices RecordDevices `json:"devices"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Owner ID + OwnerID uint64 `json:"ownerId"` + + // Owner type + OwnerType string `json:"ownerType"` + + // Pure virtual + PureVirtual bool `json:"pureVirtual"` + + // Routes + Routes ListRoutes `json:"routes"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` +} + +// NAT configuration +type NATConfig struct { + // Network mask + NetMask uint64 `json:"netmask"` + + // Network + Network string `json:"network"` + + // List NAT rules + Rules ListNATRulesConfig `json:"rules"` +} + +type ListNATRulesConfig []ItemNATRule + +// Main information about GW +type RecordGW struct { + // CKey + CKey string `json:"_ckey"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Config + Config RecordGWConfig `json:"config"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Detailed information about devices + Devices RecordDevices `json:"devices"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Owner ID + OwnerID uint64 `json:"ownerId"` + + // Owner type + OwnerType string `json:"ownerType"` + + // Pure virtual + PureVirtual bool `json:"pureVirtual"` + + // Routes + Routes ListRoutes `json:"routes"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` +} + +// GW configuration +type RecordGWConfig struct { + // Default GW + DefaultGW string `json:"default_gw"` + + // External network ID + ExtNetID uint64 `json:"ext_net_id"` + + // External network IP + ExtNetIP string `json:"ext_net_ip"` + + // External network mask + ExtNetMask uint64 `json:"ext_netmask"` + + // QOS + QOS QOS `json:"qos"` +} + +// Information about devices +type RecordDevices struct { + // Main information about primary device + Primary RecordPrimary `json:"primary"` +} + +// Main information about primary device +type RecordPrimary struct { + // Device ID + DevID uint64 `json:"devId"` + + // IFace01 + IFace01 string `json:"iface01"` + + // IFace02 + IFace02 string `json:"iface02"` +} + +// Main information about DHCP +type RecordDHCP struct { + // CKey + CKey string `json:"_ckey"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Config + Config RecordDHCPConfig `json:"config"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Detailed information about devices + Devices RecordDevices `json:"devices"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Owner ID + OwnerID uint64 `json:"ownerId"` + + // Owner type + OwnerType string `json:"ownerType"` + + // Pure virtual + PureVirtual bool `json:"pureVirtual"` + + // Routes + Routes ListRoutes `json:"routes"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` +} + +// DHCP configuration +type RecordDHCPConfig struct { + // Default GW + DefaultGW string `json:"default_gw"` + + // List of DNS + DNS []string `json:"dns"` + + // IP end + IPEnd string `json:"ip_end"` + + // IP start + IPStart string `json:"ip_start"` + + // Lease + Lease uint64 `json:"lease"` + + // Network mask + NetMask uint64 `json:"netmask"` + + // Network + Network string `json:"network"` + + // List of reservations + Reservations ListReservations `json:"reservations"` +} + +// List of static routes +type ListStaticRoutes struct { + // Data + Data []ItemRoutes `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// List of Routes +type ListRoutes []ItemRoutes + +// Detailed information about Routes +type ItemRoutes struct { + //Compute Id + ComputeIds []uint64 `json:"computeIds"` + + // Destination network + Destination string `json:"destination"` + + //Next hop host, IP address from ViNS ID free IP pool + Gateway string `json:"gateway"` + + // GUID + GUID string `json:"guid"` + + // ID + ID uint64 `json:"id"` + + //Destination network mask in 255.255.255.255 format + Netmask string `json:"netmask"` +} + +// Detailed information about VINS +type RecordVINS struct { + // Main information about VNF device + VNFDev RecordVNFDev `json:"VNFDev"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // List of VINS computes + Computes ListVINSComputes `json:"computes"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Default QOS + DefaultQOS QOS `json:"defaultQos"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Manager ID + ManagerID uint64 `json:"managerId"` + + // Manager type + ManagerType string `json:"managerType"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network + Network string `json:"network"` + + // Pre reservaions number + PreReservaionsNum uint64 `json:"preReservationsNum"` + + // Redundant + Redundant bool `json:"redundant"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // SecVNFDevID + SecVNFDevID uint64 `json:"secVnfDevId"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed + UserManaged bool `json:"userManaged"` + + // Main information about VNFs + VNFs RecordVNFs `json:"vnfs"` + + // VXLAN ID + VXLANID uint64 `json:"vxlanId"` +} + +// Information about libvirt settings +type LibvirtSettings struct { + // TX mode + TXMode string `json:"txmode"` + + // IO event + IOEventFD string `json:"ioeventfd"` + + // Event ID + EventIDx string `json:"event_idx"` + + // Number of queues + Queues uint64 `json:"queues"` + + // RX queue size + RXQueueSize uint64 `json:"rx_queue_size"` + + // TX queue size + TXQueueSize uint64 `json:"tx_queue_size"` + + // GUID + GUID string `json:"guid"` +} + +// Main information about NAT rule +type ItemNATRule struct { + // ID + ID uint64 `json:"id"` + + // Local IP + LocalIP string `json:"localIp"` + + // Local port + LocalPort uint64 `json:"localPort"` + + // Protocol + Protocol string `json:"protocol"` + + // Public port end + PublicPortEnd uint64 `json:"publicPortEnd"` + + // Public port start + PublicPortStart uint64 `json:"publicPortStart"` + + // Virtual machine ID + VMID uint64 `json:"vmId"` + + // Virtual machine name + VMName string `json:"vmName"` +} + +// List of NAT rules +type ListNATRules struct { + // Data + Data []ItemNATRule `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about reservation +type ItemReservation struct { + // Client type + ClientType string `json:"clientType"` + + // Description + Description string `json:"desc"` + + // Domain name + DomainName string `json:"domainname"` + + // Hostname + Hostname string `json:"hostname"` + + // IP + IP string `json:"ip"` + + // MAC + MAC string `json:"mac"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmId"` +} + +// List of reservations +type ListReservations []ItemReservation diff --git a/pkg/cloudapi/vins/nat_rule_add.go b/pkg/cloudapi/vins/nat_rule_add.go new file mode 100644 index 0000000..7f70ba4 --- /dev/null +++ b/pkg/cloudapi/vins/nat_rule_add.go @@ -0,0 +1,61 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NATRuleAddRequest struct to create NAT rules +type NATRuleAddRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Internal IP address to apply this rule to + // Required: true + IntIP string `url:"intIp" json:"intIp" validate:"required"` + + // External IP start port to use for this rule + // Required: true + ExtPortStart uint `url:"extPortStart" json:"extPortStart" validate:"required"` + + // Internal IP port number to use for this rule + // Required: false + IntPort uint `url:"intPort,omitempty" json:"intPort,omitempty"` + + // External IP end port to use for this rule + // Required: false + ExtPortEnd uint `url:"extPortEnd,omitempty" json:"extPortEnd,omitempty"` + + // IP protocol type + // Should be one of: + // - "tcp" + // - "udp" + // Required: false + Proto string `url:"proto,omitempty" json:"proto,omitempty" validate:"omitempty,proto"` +} + +// NATRuleAdd create NAT (port forwarding) rule on VINS +func (v VINS) NATRuleAdd(ctx context.Context, req NATRuleAddRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/natRuleAdd" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/nat_rule_del.go b/pkg/cloudapi/vins/nat_rule_del.go new file mode 100644 index 0000000..36712d7 --- /dev/null +++ b/pkg/cloudapi/vins/nat_rule_del.go @@ -0,0 +1,43 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NATRuleDelRequest struct to delete NAT rule +type NATRuleDelRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // ID of the rule to delete. + // Pass -1 to clear all rules at once + // Required: true + RuleID int64 `url:"ruleId" json:"ruleId" validate:"required"` +} + +// NATRuleDel deletes NAT (port forwarding) rule on VINS +func (v VINS) NATRuleDel(ctx context.Context, req NATRuleDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/natRuleDel" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/nat_rule_list.go b/pkg/cloudapi/vins/nat_rule_list.go new file mode 100644 index 0000000..0f8c6fe --- /dev/null +++ b/pkg/cloudapi/vins/nat_rule_list.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NATRuleListRequest struct to get list of NAT rules +type NATRuleListRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// NATRuleList gets list of NAT (port forwarding) rules +func (v VINS) NATRuleList(ctx context.Context, req NATRuleListRequest) (*ListNATRules, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/natRuleList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := &ListNATRules{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/vins/restore.go b/pkg/cloudapi/vins/restore.go new file mode 100644 index 0000000..8cae3ae --- /dev/null +++ b/pkg/cloudapi/vins/restore.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct for restore +type RestoreRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Restore restores VINS from recycle bin +func (v VINS) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/restore" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/search.go b/pkg/cloudapi/vins/search.go new file mode 100644 index 0000000..d8efec9 --- /dev/null +++ b/pkg/cloudapi/vins/search.go @@ -0,0 +1,45 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" +) + +// SearchRequest struct for search VINSes +type SearchRequest struct { + // ID of the account to search for the ViNSes + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // ID of the resource group to limit search to the specified RG level only + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Name of the ViNS to search for + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // If False, then VINSes having one of the statuses are not listed for + // Required: false + ShowAll bool `url:"show_all,omitempty" json:"show_all,omitempty"` +} + +// Search searches VINSes +func (v VINS) Search(ctx context.Context, req SearchRequest) (SearchListVINS, error) { + url := "/cloudapi/vins/search" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := SearchListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudapi/vins/serialize.go b/pkg/cloudapi/vins/serialize.go new file mode 100644 index 0000000..418f301 --- /dev/null +++ b/pkg/cloudapi/vins/serialize.go @@ -0,0 +1,43 @@ +package vins + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lv ListVINS) Serialize(params ...string) (serialization.Serialized, error) { + if len(lv.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lv, prefix, indent) + } + + return json.Marshal(lv) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (iv ItemVINS) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(iv, prefix, indent) + } + + return json.Marshal(iv) +} diff --git a/pkg/cloudapi/vins/sorting.go b/pkg/cloudapi/vins/sorting.go new file mode 100644 index 0000000..80d898c --- /dev/null +++ b/pkg/cloudapi/vins/sorting.go @@ -0,0 +1,60 @@ +package vins + +import "sort" + +// SortByCreatedTime sorts ListVINS by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lv ListVINS) SortByCreatedTime(inverse bool) ListVINS { + if len(lv.Data) < 2 { + return lv + } + + sort.Slice(lv.Data, func(i, j int) bool { + if inverse { + return lv.Data[i].CreatedTime > lv.Data[j].CreatedTime + } + + return lv.Data[i].CreatedTime < lv.Data[j].CreatedTime + }) + + return lv +} + +// SortByUpdatedTime sorts ListVINS by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lv ListVINS) SortByUpdatedTime(inverse bool) ListVINS { + if len(lv.Data) < 2 { + return lv + } + + sort.Slice(lv.Data, func(i, j int) bool { + if inverse { + return lv.Data[i].UpdatedTime > lv.Data[j].UpdatedTime + } + + return lv.Data[i].UpdatedTime < lv.Data[j].UpdatedTime + }) + + return lv +} + +// SortByDeletedTime sorts ListVINS by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lv ListVINS) SortByDeletedTime(inverse bool) ListVINS { + if len(lv.Data) < 2 { + return lv + } + + sort.Slice(lv.Data, func(i, j int) bool { + if inverse { + return lv.Data[i].DeletedTime > lv.Data[j].DeletedTime + } + + return lv.Data[i].DeletedTime < lv.Data[j].DeletedTime + }) + + return lv +} diff --git a/pkg/cloudapi/vins/static_route_access_grant.go b/pkg/cloudapi/vins/static_route_access_grant.go new file mode 100644 index 0000000..6a46234 --- /dev/null +++ b/pkg/cloudapi/vins/static_route_access_grant.go @@ -0,0 +1,46 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAccessGrantRequest struct to grant access to static route to Compute/ViNS +type StaticRouteAccessGrantRequest struct { + // ViNS ID to grant access + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Route ID to grant access, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` + + // List of Compute IDs to grant access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAccessGrant grants access to static route to Compute/ViNS +func (v VINS) StaticRouteAccessGrant(ctx context.Context, req StaticRouteAccessGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/staticRouteAccessGrant" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/static_route_access_revoke.go b/pkg/cloudapi/vins/static_route_access_revoke.go new file mode 100644 index 0000000..05c3165 --- /dev/null +++ b/pkg/cloudapi/vins/static_route_access_revoke.go @@ -0,0 +1,46 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAccessRevokeRequest struct to revoke access to static route to Compute/ViNS +type StaticRouteAccessRevokeRequest struct { + // ViNS ID to revoke access + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Route ID to revoke access, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` + + // List of Compute IDs to revoke access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAccessRevoke revokes access to static route to Compute/ViNS +func (v VINS) StaticRouteAccessRevoke(ctx context.Context, req StaticRouteAccessRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/staticRouteAccessRevoke" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/static_route_add.go b/pkg/cloudapi/vins/static_route_add.go new file mode 100644 index 0000000..f375d40 --- /dev/null +++ b/pkg/cloudapi/vins/static_route_add.go @@ -0,0 +1,54 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAddRequest struct to add static route +type StaticRouteAddRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Destination network + // Required: true + Destination string `url:"destination" json:"destination" validate:"required"` + + // Destination network mask in 255.255.255.255 format + // Required: true + Netmask string `url:"netmask" json:"netmask" validate:"required"` + + // Next hop host, IP address from ViNS ID free IP pool + // Required: true + Gateway string `url:"gateway" json:"gateway" validate:"required"` + + // List of Compute IDs which have access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAdd adds new static route to ViNS +func (v VINS) StaticRouteAdd(ctx context.Context, req StaticRouteAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/staticRouteAdd" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/static_route_del.go b/pkg/cloudapi/vins/static_route_del.go new file mode 100644 index 0000000..547ff6a --- /dev/null +++ b/pkg/cloudapi/vins/static_route_del.go @@ -0,0 +1,42 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteDelRequest struct to remove static route from ViNS +type StaticRouteDelRequest struct { + // ViNS ID to remove static route from + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Route ID to remove, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` +} + +// StaticRouteDel removes static route from ViNS +func (v VINS) StaticRouteDel(ctx context.Context, req StaticRouteDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/staticRouteDel" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/static_route_list.go b/pkg/cloudapi/vins/static_route_list.go new file mode 100644 index 0000000..d1c1dcf --- /dev/null +++ b/pkg/cloudapi/vins/static_route_list.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteListRequest struct for static route list +type StaticRouteListRequest struct { + // ViNS ID to show list of static routes + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// StaticRouteList shows list of static routes for ViNS +func (v VINS) StaticRouteList(ctx context.Context, req StaticRouteListRequest) (*ListStaticRoutes, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/staticRouteList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListStaticRoutes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudapi/vins/vins.go b/pkg/cloudapi/vins/vins.go new file mode 100644 index 0000000..a8ba08d --- /dev/null +++ b/pkg/cloudapi/vins/vins.go @@ -0,0 +1,18 @@ +// API Actor for managing VINS. This actor is a final API for endusers to manage VINS +package vins + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to VINS +type VINS struct { + client interfaces.Caller +} + +// Builder for VINS endpoints +func New(client interfaces.Caller) *VINS { + return &VINS{ + client, + } +} diff --git a/pkg/cloudapi/vins/vnfdev_redeploy.go b/pkg/cloudapi/vins/vnfdev_redeploy.go new file mode 100644 index 0000000..bc08f31 --- /dev/null +++ b/pkg/cloudapi/vins/vnfdev_redeploy.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// VNFDevRedeployRequest struct for redeploy VNFDevs +type VNFDevRedeployRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// VNFDevRedeploy redeploys VINS VNFDevs +func (v VINS) VNFDevRedeploy(ctx context.Context, req VNFDevRedeployRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/vnfdevRedeploy" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudapi/vins/vnfdev_restart.go b/pkg/cloudapi/vins/vnfdev_restart.go new file mode 100644 index 0000000..e1fe164 --- /dev/null +++ b/pkg/cloudapi/vins/vnfdev_restart.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// VNFDevRestartRequest struct for reboot VINSes primary VNF device +type VNFDevRestartRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// VNFDevRestart reboots VINSes primary VNF device +func (v VINS) VNFDevRestart(ctx context.Context, req VNFDevRestartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/vnfdevRestart" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account.go b/pkg/cloudbroker/account.go new file mode 100644 index 0000000..211a1d9 --- /dev/null +++ b/pkg/cloudbroker/account.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/account" +) + +// Accessing the Account method group +func (cb *CloudBroker) Account() *account.Account { + return account.New(cb.client) +} diff --git a/pkg/cloudbroker/account/account.go b/pkg/cloudbroker/account/account.go new file mode 100644 index 0000000..e2f55ce --- /dev/null +++ b/pkg/cloudbroker/account/account.go @@ -0,0 +1,16 @@ +// API Actor API for managing account +package account + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to account +type Account struct { + client interfaces.Caller +} + +// Builder for account endpoints +func New(client interfaces.Caller) *Account { + return &Account{ + client: client, + } +} diff --git a/pkg/cloudbroker/account/add_user.go b/pkg/cloudbroker/account/add_user.go new file mode 100644 index 0000000..220c982 --- /dev/null +++ b/pkg/cloudbroker/account/add_user.go @@ -0,0 +1,49 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AddUserRequest struct for adding permission to access to account for a user +type AddUserRequest struct { + // ID of account to add to + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Name of the user to be given rights + // Required: true + Username string `url:"username" json:"username" validate:"required"` + + // Account permission types: + // - 'R' for read only access + // - 'RCX' for Write + // - 'ARCXDU' for Admin + // Required: true + AccessType string `url:"accesstype" json:"accesstype" validate:"accessType"` +} + +// AddUser gives a user access rights. +func (a Account) AddUser(ctx context.Context, req AddUserRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/addUser" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/audits.go b/pkg/cloudbroker/account/audits.go new file mode 100644 index 0000000..d5804bd --- /dev/null +++ b/pkg/cloudbroker/account/audits.go @@ -0,0 +1,40 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuditsRequest struct to give list of account audits +type AuditsRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Audits gets audit records for the specified account object +func (a Account) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/audits" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/account/create.go b/pkg/cloudbroker/account/create.go new file mode 100644 index 0000000..288a493 --- /dev/null +++ b/pkg/cloudbroker/account/create.go @@ -0,0 +1,85 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct for creating account +type CreateRequest struct { + // Display name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Name of the account + // Required: true + Username string `url:"username" json:"username" validate:"required"` + + // Email + // Required: false + EmailAddress string `url:"emailaddress,omitempty" json:"emailaddress,omitempty" validate:"omitempty,email"` + + // Max size of memory in MB + // Required: false + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + + // Max size of aggregated vdisks in GB + // Required: false + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + + // Max number of CPU cores + // Required: false + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + + // Max sent/received network transfer peering + // Required: false + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + + // Max number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + + // If true send emails when a user is granted access to resources + // Required: false + SendAccessEmails bool `url:"sendAccessEmails" json:"sendAccessEmails"` + + // Limit (positive) or disable (0) GPU resources + // Required: false + GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` + + // List of strings with pools + // i.e.: ["sep1_poolName1", "sep2_poolName2", etc] + // Required: false + UniqPools []string `url:"uniqPools,omitempty" json:"uniqPools,omitempty"` + + // Advanced compute features, + // one of: hugepages, numa, cpupin, vfnic + // Required: false + ComputeFeatures []string `url:"computeFeatures,omitempty" json:"computeFeatures,omitempty" validate:"omitempty,computeFeatures"` +} + +// Create creates account +// Setting a cloud unit maximum to -1 or empty will not put any restrictions on the resource +func (a Account) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/create" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/delete.go b/pkg/cloudbroker/account/delete.go new file mode 100644 index 0000000..64893cc --- /dev/null +++ b/pkg/cloudbroker/account/delete.go @@ -0,0 +1,36 @@ +package account + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete account +type DeleteRequest struct { + // ID of account to delete + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Whether to completely delete the account + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete completes delete an account from the system Returns true if account is deleted or was already deleted or never existed +func (a Account) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/delete" + + _, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/account/delete_accounts.go b/pkg/cloudbroker/account/delete_accounts.go new file mode 100644 index 0000000..60cd21f --- /dev/null +++ b/pkg/cloudbroker/account/delete_accounts.go @@ -0,0 +1,36 @@ +package account + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteAccountsRequest struct to delete group of accounts +type DeleteAccountsRequest struct { + // IDs of accounts + // Required: true + AccountsIDs []uint64 `url:"accountIds" json:"accountIds" validate:"min=1"` + + // Whether to completely destroy accounts or not + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// DeleteAccounts destroys a group of accounts +func (a Account) DeleteAccounts(ctx context.Context, req DeleteAccountsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/deleteAccounts" + + _, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/account/delete_user.go b/pkg/cloudbroker/account/delete_user.go new file mode 100644 index 0000000..8b58c15 --- /dev/null +++ b/pkg/cloudbroker/account/delete_user.go @@ -0,0 +1,42 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteUserRequest struct to revoke access to account +type DeleteUserRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // ID or emailaddress of the user to remove + // Required: true + UserName string `url:"username" json:"username" validate:"required"` +} + +// DeleteUser revokes user access from the account +func (a Account) DeleteUser(ctx context.Context, req DeleteUserRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/deleteUser" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/disable.go b/pkg/cloudbroker/account/disable.go new file mode 100644 index 0000000..04aec6f --- /dev/null +++ b/pkg/cloudbroker/account/disable.go @@ -0,0 +1,38 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable account +type DisableRequest struct { + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Disable disables an account +func (a Account) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/disable" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/disable_accounts.go b/pkg/cloudbroker/account/disable_accounts.go new file mode 100644 index 0000000..ede6c72 --- /dev/null +++ b/pkg/cloudbroker/account/disable_accounts.go @@ -0,0 +1,32 @@ +package account + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableAccountsRequest struct to disable group of accounts +type DisableAccountsRequest struct { + // IDs of accounts + // Required: true + AccountIDs []uint64 `url:"accountIds" json:"accountIds" validate:"min=1"` +} + +// DisableAccounts disables accounts +func (a Account) DisableAccounts(ctx context.Context, req DisableAccountsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/disableAccounts" + + _, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/account/enable.go b/pkg/cloudbroker/account/enable.go new file mode 100644 index 0000000..7898e65 --- /dev/null +++ b/pkg/cloudbroker/account/enable.go @@ -0,0 +1,38 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable account +type EnableRequest struct { + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Enable enables an account +func (a Account) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/enable" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/enable_accounts.go b/pkg/cloudbroker/account/enable_accounts.go new file mode 100644 index 0000000..eb14045 --- /dev/null +++ b/pkg/cloudbroker/account/enable_accounts.go @@ -0,0 +1,32 @@ +package account + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableAccountsRequest to enable group of accounts +type EnableAccountsRequest struct { + // IDs od accounts + // Required: true + AccountIDs []uint64 `url:"accountIds" json:"accountIds" validate:"min=1"` +} + +// EnableAccounts enables accounts +func (a Account) EnableAccounts(ctx context.Context, req EnableAccountsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/enableAccounts" + + _, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/account/filter.go b/pkg/cloudbroker/account/filter.go new file mode 100644 index 0000000..c402d33 --- /dev/null +++ b/pkg/cloudbroker/account/filter.go @@ -0,0 +1,88 @@ +package account + +// FilterByID returns ListAccounts with specified ID. +func (la ListAccounts) FilterByID(id uint64) ListAccounts { + predicate := func(ia ItemAccount) bool { + return ia.ID == id + } + + return la.FilterFunc(predicate) +} + +// FilterByName returns ListAccounts with specified Name. +func (la ListAccounts) FilterByName(name string) ListAccounts { + predicate := func(ia ItemAccount) bool { + return ia.Name == name + } + + return la.FilterFunc(predicate) +} + +// FilterByStatus returns ListAccounts with specified Status. +func (la ListAccounts) FilterByStatus(status string) ListAccounts { + predicate := func(ia ItemAccount) bool { + return ia.Status == status + } + + return la.FilterFunc(predicate) +} + +// FilterByUserGroupID returns ListAccounts with specified UserGroupID. +func (la ListAccounts) FilterByUserGroupID(userGroupID string) ListAccounts { + predicate := func(ia ItemAccount) bool { + acl := ia.ACL + + for _, item := range acl { + if item.UserGroupID == userGroupID { + return true + } + } + + return false + } + + return la.FilterFunc(predicate) +} + +// FilterByCompany returns ListAccounts with specified Company. +func (la ListAccounts) FilterByCompany(company string) ListAccounts { + predicate := func(ia ItemAccount) bool { + return ia.Company == company + } + + return la.FilterFunc(predicate) +} + +// FilterByCreatedBy returns ListAccounts created by specified user. +func (la ListAccounts) FilterByCreatedBy(createdBy string) ListAccounts { + predicate := func(ia ItemAccount) bool { + return ia.CreatedBy == createdBy + } + + return la.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListAccounts based on a user-specified predicate. +func (la ListAccounts) FilterFunc(predicate func(ItemAccount) bool) ListAccounts { + var result ListAccounts + + for _, item := range la.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemAccount. +// If none was found, returns an empty struct. +func (la ListAccounts) FindOne() ItemAccount { + if len(la.Data) == 0 { + return ItemAccount{} + } + + return la.Data[0] +} diff --git a/pkg/cloudbroker/account/filter_test.go b/pkg/cloudbroker/account/filter_test.go new file mode 100644 index 0000000..3b2458a --- /dev/null +++ b/pkg/cloudbroker/account/filter_test.go @@ -0,0 +1,158 @@ +package account + +import ( + "testing" +) + +var accounts = ListAccounts{ + Data: []ItemAccount{ + { + Meta: []interface{}{}, + InfoAccount: InfoAccount{ + ACL: []ACL{ + { + Explicit: true, + GUID: "", + Right: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "not_really_timofey_tkachev_1@decs3o", + }, + }, + CreatedTime: 1676878820, + DeletedTime: 0, + ID: 132847, + Name: "std_2", + Status: "CONFIRMED", + UpdatedTime: 1676645275, + }, + }, + { + Meta: []interface{}{}, + InfoAccount: InfoAccount{ + ACL: []ACL{ + { + Explicit: true, + GUID: "", + Right: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "timofey_tkachev_1@decs3o", + }, + }, + CreatedTime: 1676645275, + DeletedTime: 1677723401, + ID: 132846, + Name: "std", + Status: "DELETED", + UpdatedTime: 1676645275, + }, + }, + { + Meta: []interface{}{}, + InfoAccount: InfoAccount{ + ACL: []ACL{ + { + Explicit: true, + GUID: "", + Right: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "timofey_tkachev_1@decs3o", + }, + { + Explicit: true, + GUID: "", + Right: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "second_account@decs3o", + }, + }, + CreatedTime: 1676883850, + DeletedTime: 1676883899, + ID: 132848, + Name: "std_broker", + Status: "DELETED", + UpdatedTime: 1676878820, + }, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := accounts.FilterByID(132846).FindOne() + + if actual.ID != 132846 { + t.Fatal("actual: ", actual.ID, " > expected: 132846") + } +} + +func TestFilterByUserGroupId(t *testing.T) { + actual := accounts.FilterByUserGroupID("second_account@decs3o").FindOne() + + for _, item := range actual.ACL { + if item.UserGroupID == "second_account@decs3o" { + return + } + } + + t.Fatal("second_account@decs3o has not been found. expected 1 found") +} + +func TestFilterByName(t *testing.T) { + actual := accounts.FilterByName("std_broker").FindOne() + + if actual.Name != "std_broker" { + t.Fatal("actual: ", actual.Name, " >> expected: std_broker") + } +} + +func TestFilterByStatus(t *testing.T) { + actual := accounts.FilterByStatus("DELETED") + + if len(actual.Data) != 2 { + t.Fatal("Expected 2 elements in slice, found: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "DELETED" { + t.Fatal("expected DELETED, found: ", item.Status) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := accounts.FilterFunc(func(ia ItemAccount) bool { + return ia.DeletedTime == 0 + }) + + for _, item := range actual.Data { + if item.DeletedTime != 0 { + t.Fatal("Expected DeletedTime = 0, found: ", item.DeletedTime) + } + } +} + +func TestSortingByCreatedTime(t *testing.T) { + actual := accounts.SortByCreatedTime(false) + + if actual.Data[0].Name != "std" { + t.Fatal("Expected account std as earliest, found: ", actual.Data[0].Name) + } + + actual = accounts.SortByCreatedTime(true) + + if actual.Data[0].Name != "std_broker" { + t.Fatal("Expected account std_broker as latest, found: ", actual.Data[0].Name) + } +} + +func TestFilterEmpty(t *testing.T) { + actual := accounts.FilterByID(0) + + if len(actual.Data) != 0 { + t.Fatal("Expected 0 found, actual: ", len(actual.Data)) + } +} diff --git a/pkg/cloudbroker/account/get.go b/pkg/cloudbroker/account/get.go new file mode 100644 index 0000000..0d47099 --- /dev/null +++ b/pkg/cloudbroker/account/get.go @@ -0,0 +1,46 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about account +type GetRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Get gets information about account as a RecordAccount struct +func (a Account) Get(ctx context.Context, req GetRequest) (*RecordAccount, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordAccount{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about account as an array of bytes +func (a Account) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/account/get_resource_consumption.go b/pkg/cloudbroker/account/get_resource_consumption.go new file mode 100644 index 0000000..38ede51 --- /dev/null +++ b/pkg/cloudbroker/account/get_resource_consumption.go @@ -0,0 +1,40 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetResourceConsumptionRequest struct for getting resource consumption +type GetResourceConsumptionRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// GetResourceConsumption shows amount of consumed and reserved resources (cpu, ram, disk) by specific account +func (a Account) GetResourceConsumption(ctx context.Context, req GetResourceConsumptionRequest) (*RecordResourceConsumption, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/getResourceConsumption" + + info := RecordResourceConsumption{} + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/account/grant_access_templates.go b/pkg/cloudbroker/account/grant_access_templates.go new file mode 100644 index 0000000..1e375b6 --- /dev/null +++ b/pkg/cloudbroker/account/grant_access_templates.go @@ -0,0 +1,42 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GrantAccessTemplatesRequest struct to share images with account +type GrantAccessTemplatesRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // list of image IDs + // Required: true + ImageIDs []uint64 `url:"imageIds" json:"imageIds" validate:"required"` +} + +// GrantAccessTemplates shares specified images with specified account +func (a Account) GrantAccessTemplates(ctx context.Context, req GrantAccessTemplatesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/grantAccessTemplates" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/ids.go b/pkg/cloudbroker/account/ids.go new file mode 100644 index 0000000..7a7fe5b --- /dev/null +++ b/pkg/cloudbroker/account/ids.go @@ -0,0 +1,64 @@ +package account + +// IDs gets array of AccountIDs from ListAccounts struct +func (la ListAccounts) IDs() []uint64 { + res := make([]uint64, 0, len(la.Data)) + for _, acc := range la.Data { + res = append(res, acc.ID) + } + return res +} + +// IDs gets array of ComputeIDs from ListComputes struct +func (lc ListComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lc.Data)) + for _, c := range lc.Data { + res = append(res, c.ID) + } + return res +} + +// IDs gets array of DiskIDs from ListDisks struct +func (ld ListDisks) IDs() []uint64 { + res := make([]uint64, 0, len(ld.Data)) + for _, d := range ld.Data { + res = append(res, d.ID) + } + return res +} + +// IDs gets array of FLIPGroupIDs from ListFLIPGroups struct +func (fg ListFLIPGroups) IDs() []uint64 { + res := make([]uint64, 0, len(fg.Data)) + for _, g := range fg.Data { + res = append(res, g.ID) + } + return res +} + +// IDs gets array of AccountIDs from ListResourceConsumption struct +func (rc ListResources) IDs() []uint64 { + res := make([]uint64, 0, len(rc.Data)) + for _, r := range rc.Data { + res = append(res, r.AccountID) + } + return res +} + +// IDs gets array of RGIDs from ListRG struct +func (rg ListRG) IDs() []uint64 { + res := make([]uint64, 0, len(rg.Data)) + for _, g := range rg.Data { + res = append(res, g.ID) + } + return res +} + +// IDs gets array of VINSIDs from ListVINS struct +func (lv ListVINS) IDs() []uint64 { + res := make([]uint64, 0, len(lv.Data)) + for _, v := range lv.Data { + res = append(res, v.ID) + } + return res +} diff --git a/pkg/cloudbroker/account/list.go b/pkg/cloudbroker/account/list.go new file mode 100644 index 0000000..eb04b1e --- /dev/null +++ b/pkg/cloudbroker/account/list.go @@ -0,0 +1,71 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of accounts +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by access control list + // Required: false + ACL string `url:"acl,omitempty" json:"acl,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all accounts the user has access to as a ListAccounts struct +func (a Account) List(ctx context.Context, req ListRequest) (*ListAccounts, error) { + + res, err := a.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListAccounts{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all accounts the user has access to as an array of bytes +func (a Account) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/account/list_available_templates.go b/pkg/cloudbroker/account/list_available_templates.go new file mode 100644 index 0000000..a16a277 --- /dev/null +++ b/pkg/cloudbroker/account/list_available_templates.go @@ -0,0 +1,41 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListAvailableTemplatesRequest struct to list templates who sharedWith include accountId +type ListAvailableTemplatesRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// ListAvailableTemplates lists templates who sharedWith include accountId +func (a Account) ListAvailableTemplates(ctx context.Context, req ListAvailableTemplatesRequest) ([]uint64, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/listAvailableTemplates" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := make([]uint64, 0) + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/account/list_computes.go b/pkg/cloudbroker/account/list_computes.go new file mode 100644 index 0000000..1bc0a11 --- /dev/null +++ b/pkg/cloudbroker/account/list_computes.go @@ -0,0 +1,85 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListComputesRequest struct to a get list of compute instances +type ListComputesRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by compute id + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by resource group name + // Required: false + RGName string `url:"rgName,omitempty" json:"rgName,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by ip address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by external network name + // Required: false + ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"` + + // Find by external network id + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListComputes gets list of all compute instances under specified account, accessible by the user +func (a Account) ListComputes(ctx context.Context, req ListComputesRequest) (*ListComputes, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/listComputes" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/account/list_deleted.go b/pkg/cloudbroker/account/list_deleted.go new file mode 100644 index 0000000..e099256 --- /dev/null +++ b/pkg/cloudbroker/account/list_deleted.go @@ -0,0 +1,60 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted accounts +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by access control list + // Required: false + ACL string `url:"acl,omitempty" json:"acl,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list all deleted accounts the user has access to +func (a Account) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListAccounts, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/listDeleted" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAccounts{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/account/list_disks.go b/pkg/cloudbroker/account/list_disks.go new file mode 100644 index 0000000..e8778b0 --- /dev/null +++ b/pkg/cloudbroker/account/list_disks.go @@ -0,0 +1,69 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDisksRequest struct to get list of deleted disks +type ListDisksRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by disk id + // Required: false + DiskID uint64 `url:"diskId,omitempty" json:"diskId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by max size disk + // Required: false + DiskMaxSize uint64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"` + + // Type of the disks + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDisks gets list of all currently unattached disks under specified account +func (a Account) ListDisks(ctx context.Context, req ListDisksRequest) (*ListDisks, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/listDisks" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/account/list_flip_groups.go b/pkg/cloudbroker/account/list_flip_groups.go new file mode 100644 index 0000000..9c208b0 --- /dev/null +++ b/pkg/cloudbroker/account/list_flip_groups.go @@ -0,0 +1,77 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListFLIPGroupsRequest struct to get list of FLIPGroups +type ListFLIPGroupsRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by vinsId + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Find by VINS name + // Required: false + VINSName string `url:"vinsName,omitempty" json:"vinsName,omitempty"` + + // Find by external network id + // Required: false + ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"` + + // Find by IP + // Required: false + ByIP string `url:"byIp,omitempty" json:"byIp,omitempty"` + + // Find by flipGroup Id + // Required: false + FLIPGroupID uint64 `url:"flipGroupId,omitempty" json:"flipGroupId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListFLIPGroups gets list of all FLIPGroups under specified account, accessible by the user +func (a Account) ListFLIPGroups(ctx context.Context, req ListFLIPGroupsRequest) (*ListFLIPGroups, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/listFlipGroups" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListFLIPGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/account/list_resource_consumption.go b/pkg/cloudbroker/account/list_resource_consumption.go new file mode 100644 index 0000000..29d8b34 --- /dev/null +++ b/pkg/cloudbroker/account/list_resource_consumption.go @@ -0,0 +1,26 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" +) + +// ListResourceConsumption show data list amount of consumed and reserved resources (cpu, ram, disk) by specific accounts +func (a Account) ListResourceConsumption(ctx context.Context) (*ListResources, error) { + url := "/cloudbroker/account/listResourceConsumption" + + info := ListResources{} + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/account/list_rg.go b/pkg/cloudbroker/account/list_rg.go new file mode 100644 index 0000000..37bb21d --- /dev/null +++ b/pkg/cloudbroker/account/list_rg.go @@ -0,0 +1,73 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRGRequest struct to get list of resource groups +type ListRGRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by vinsId + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Find by VM ID + // Required: false + VMID uint64 `url:"vmId,omitempty" json:"vmId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` +} + +// ListRG gets list of all resource groups under specified account, accessible by the user +func (a Account) ListRG(ctx context.Context, req ListRGRequest) (*ListRG, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/listRG" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListRG{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/account/list_vins.go b/pkg/cloudbroker/account/list_vins.go new file mode 100644 index 0000000..4607f2c --- /dev/null +++ b/pkg/cloudbroker/account/list_vins.go @@ -0,0 +1,69 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListVINSRequest struct to get list of VINS +type ListVINSRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Find by VINS ID + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by external network ip + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListVINS gets list of all ViNSes under specified account, accessible by the user +func (a Account) ListVINS(ctx context.Context, req ListVINSRequest) (*ListVINS, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/listVins" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/account/models.go b/pkg/cloudbroker/account/models.go new file mode 100644 index 0000000..de03ff0 --- /dev/null +++ b/pkg/cloudbroker/account/models.go @@ -0,0 +1,577 @@ +package account + +// Main info about audit +type ItemAudit struct { + // Call + Call string `json:"call"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List of audits +type ListAudits []ItemAudit + +type RecordResourceConsumption struct { + ItemResourceConsumption + + ResourceLimits ResourceLimits `json:"resourceLimits"` +} + +type ItemResourceConsumption struct { + // Current information about resources + Consumed Resource `json:"consumed"` + + // Reserved information about resources + Reserved Resource `json:"reserved"` + + // ID of account + AccountID uint64 `json:"id"` +} + +type ListResources struct { + // Data + Data []ItemResourceConsumption `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type Resource struct { + // Number of cores + CPU int64 `json:"cpu"` + + // Disk size + DiskSize float64 `json:"disksize"` + + // Disk size max + DiskSizeMax float64 `json:"disksizemax"` + + // Number of External IPs + ExtIPs int64 `json:"extips"` + + // External traffic + ExtTraffic int64 `json:"exttraffic"` + + // Number of grafic cores + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +// Disk usage +type DiskUsage struct { + // Disk size + DiskSize float64 `json:"disksize"` + + // Disk size max + DiskSizeMax float64 `json:"disksizemax"` +} + +// Access Control List +type ACL struct { + // Whether access is explicitly specified + Explicit bool `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Access rights + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// Resource limits +type ResourceLimits struct { + // CuC + CuC float64 `json:"CU_C"` + + // CuD + CuD float64 `json:"CU_D"` + + // CuDM + CuDM float64 `json:"CU_DM"` + + // CuI + CuI float64 `json:"CU_I"` + + // CuM + CuM float64 `json:"CU_M"` + + // CuNP + CuNP float64 `json:"CU_NP"` + + // GPUUnits + GPUUnits float64 `json:"gpu_units"` +} + +// Main information about account +type InfoAccount struct { + // DCLocation + DCLocation string `json:"DCLocation"` + + // CKey + CKey string `json:"_ckey"` + + // Access Control List + ACL []ACL `json:"acl"` + + // Company + Company string `json:"company"` + + // Company URL + CompanyURL string `json:"companyurl"` + + // Compute Features + ComputeFeatures []string `json:"computeFeatures"` + + // CPU allocation parameter + CPUAllocationParameter string `json:"cpu_allocation_parameter"` + + // CPU allocation ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deactivation time + DeactivationTime float64 `json:"deactivationTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Display name + DisplayName string `json:"displayname"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // Resource types + ResTypes []string `json:"resourceTypes"` + + // Send access emails + SendAccessEmails bool `json:"sendAccessEmails"` + + // Status + Status string `json:"status"` + + // UniqPools + UniqPools []string `json:"uniqPools"` + + // UpdatedTime + UpdatedTime uint64 `json:"updatedTime"` + + // Version + Version uint64 `json:"version"` + + // List of VINS IDs + VINS []uint64 `json:"vins"` +} + +// Deatailed information about account +type RecordAccount struct { + // Main information about account + InfoAccount +} + +// More information about account +type ItemAccount struct { + // Meta + Meta []interface{} `json:"_meta"` + + // Main information about account + InfoAccount +} + +// List of accounts +type ListAccounts struct { + // Data + Data []ItemAccount `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// List of computes +type ListComputes struct { + // Data + Data []ItemCompute `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about compute +type ItemCompute struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Number of CPU + CPUs uint64 `json:"cpus"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Registered + Registered bool `json:"registered"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RgName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Total disks size + TotalDisksSize uint64 `json:"totalDisksSize"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed + UserManaged bool `json:"userManaged"` + + // VINS Connected + VINSConnected uint64 `json:"vinsConnected"` +} + +// List of disks +type ListDisks struct { + // Data + Data []ItemDisk `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about disks +type ItemDisk struct { + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Pool + Pool string `json:"pool"` + + // SepID + SepID uint64 `json:"sepId"` + + // Shareable + Shareable bool `json:"shareable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + // Type + Type string `json:"type"` +} + +// List of FLIPGroups +type ListFLIPGroups struct { + // Data + Data []ItemFLIPGroup `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about FLIPGroup +type ItemFLIPGroup struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Client type + ClientType string `json:"clientType"` + + // Connection type + ConnType string `json:"connType"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IP + IP string `json:"ip"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network type + NetType string `json:"netType"` + + // Network mask + Netmask uint64 `json:"netmask"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// Computes info +type Computes struct { + // Started + Started uint64 `json:"Started"` + + // Stopped + Stopped uint64 `json:"Stopped"` +} + +// Limits +type Limits struct { + // Number of CPU + CPU int64 `json:"cpu"` + + // Disk size + DiskSize int64 `json:"disksize"` + + // Disk size max + DiskSizeMax int64 `json:"disksizemax"` + + // External IPs + ExtIPs int64 `json:"extips"` + + // External traffic + ExtTraffic int64 `json:"exttraffic"` + + // Number of GPU + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs number + SEPs uint64 `json:"seps"` +} + +// Resources of resource group +type RGResuorces struct { + // Consumed + Consumed Resource `json:"Consumed"` + + // Limits + Limits Limits `json:"Limits"` + + // Reserved + Reserved Resource `json:"Reserved"` +} + +// Main information about Resource group +type ItemRG struct { + // Compute + Computes Computes `json:"Computes"` + + // Resources of resource group + Resources RGResuorces `json:"Resources"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // ID + ID uint64 `json:"id"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Number of VINSes + VINSes uint64 `json:"vinses"` +} + +// List of resource groups +type ListRG struct { + // Data + Data []ItemRG `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about VINS +type ItemVINS struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Computes + Computes uint64 `json:"computes"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // External IP + ExternalIP string `json:"externalIP"` + + // Extnet ID + ExtnetId uint64 `json:"extnetId"` + + // Free IPs + FreeIPs int64 `json:"freeIPs"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // PriVNFDevID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// List of VINSes +type ListVINS struct { + //Data + Data []ItemVINS `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/account/restore.go b/pkg/cloudbroker/account/restore.go new file mode 100644 index 0000000..7cd6eae --- /dev/null +++ b/pkg/cloudbroker/account/restore.go @@ -0,0 +1,32 @@ +package account + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore a deleted account +type RestoreRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Restore restores a deleted account +func (a Account) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/restore" + + _, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/account/revoke_access_templates.go b/pkg/cloudbroker/account/revoke_access_templates.go new file mode 100644 index 0000000..c12366c --- /dev/null +++ b/pkg/cloudbroker/account/revoke_access_templates.go @@ -0,0 +1,42 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RevokeAccessTemplatesRequest struct to unshare images with account +type RevokeAccessTemplatesRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // list of image IDs + // Required: true + ImageIDs []uint64 `url:"imageIds" json:"imageIds" validate:"required"` +} + +// RevokeAccessTemplates unshares specified images with specified account +func (a Account) RevokeAccessTemplates(ctx context.Context, req RevokeAccessTemplatesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/revokeAccessTemplates" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/serialize.go b/pkg/cloudbroker/account/serialize.go new file mode 100644 index 0000000..225ddfb --- /dev/null +++ b/pkg/cloudbroker/account/serialize.go @@ -0,0 +1,43 @@ +package account + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (la ListAccounts) Serialize(params ...string) (serialization.Serialized, error) { + if la.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(la, prefix, indent) + } + + return json.Marshal(la) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ia ItemAccount) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ia, prefix, indent) + } + + return json.Marshal(ia) +} diff --git a/pkg/cloudbroker/account/set_cpu_allocation_parameter.go b/pkg/cloudbroker/account/set_cpu_allocation_parameter.go new file mode 100644 index 0000000..bb168ce --- /dev/null +++ b/pkg/cloudbroker/account/set_cpu_allocation_parameter.go @@ -0,0 +1,44 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCPUAllocationParameterRequest struct for setting CPU allocation parameter +type SetCPUAllocationParameterRequest struct { + // Account ID + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // CPU allocation parameter. + // If "strict" VM can't be run if not enough CPU resources. + // "loose" allow running VM if not enough resources. + // Required: true + StrictLoose string `url:"strict_loose" json:"strict_loose" validate:"required,strict_loose"` +} + +// SetCPUAllocationParameter sets CPU allocation parameter +func (a Account) SetCPUAllocationParameter(ctx context.Context, req SetCPUAllocationParameterRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/setCpuAllocationParameter" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/set_cpu_allocation_ratio.go b/pkg/cloudbroker/account/set_cpu_allocation_ratio.go new file mode 100644 index 0000000..08214d6 --- /dev/null +++ b/pkg/cloudbroker/account/set_cpu_allocation_ratio.go @@ -0,0 +1,42 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCPUAllocationRatioRequest struct for setting CPU allocation ratio +type SetCPUAllocationRatioRequest struct { + // Account ID + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // CPU allocation ratio, i.e. one pCPU = ratio*vCPU + // Required: true + Ratio float64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetCPUAllocationRatio sets CPU allocation ratio +func (a Account) SetCPUAllocationRatio(ctx context.Context, req SetCPUAllocationRatioRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/setCpuAllocationRatio" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/sorting.go b/pkg/cloudbroker/account/sorting.go new file mode 100644 index 0000000..191bbe1 --- /dev/null +++ b/pkg/cloudbroker/account/sorting.go @@ -0,0 +1,60 @@ +package account + +import "sort" + +// SortByCreatedTime sorts ListAccounts by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (la ListAccounts) SortByCreatedTime(inverse bool) ListAccounts { + if len(la.Data) < 2 { + return la + } + + sort.Slice(la.Data, func(i, j int) bool { + if inverse { + return la.Data[i].CreatedTime > la.Data[j].CreatedTime + } + + return la.Data[i].CreatedTime < la.Data[j].CreatedTime + }) + + return la +} + +// SortByUpdatedTime sorts ListAccounts by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (la ListAccounts) SortByUpdatedTime(inverse bool) ListAccounts { + if len(la.Data) < 2 { + return la + } + + sort.Slice(la.Data, func(i, j int) bool { + if inverse { + return la.Data[i].UpdatedTime > la.Data[j].UpdatedTime + } + + return la.Data[i].UpdatedTime < la.Data[j].UpdatedTime + }) + + return la +} + +// SortByDeletedTime sorts LisAccounts by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (la ListAccounts) SortByDeletedTime(inverse bool) ListAccounts { + if len(la.Data) < 2 { + return la + } + + sort.Slice(la.Data, func(i, j int) bool { + if inverse { + return la.Data[i].DeletedTime > la.Data[j].DeletedTime + } + + return la.Data[i].DeletedTime < la.Data[j].DeletedTime + }) + + return la +} diff --git a/pkg/cloudbroker/account/update.go b/pkg/cloudbroker/account/update.go new file mode 100644 index 0000000..7f70783 --- /dev/null +++ b/pkg/cloudbroker/account/update.go @@ -0,0 +1,80 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update account +type UpdateRequest struct { + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Name of the account + // Required: false + Name string `url:"name" json:"name"` + + // Max size of memory in MB + // Required: false + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + + // Max size of aggregated vdisks in GB + // Required: false + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + + // Max number of CPU cores + // Required: false + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + + // Max sent/received network transfer peering + // Required: false + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + + // Max number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + + // If true send emails when a user is granted access to resources + // Required: false + SendAccessEmails bool `url:"sendAccessEmails" json:"sendAccessEmails"` + + // Limit (positive) or disable (0) GPU resources + // Required: false + GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` + + // List of strings with pools + // i.e.: ["sep1_poolName1", "sep2_poolName2", etc] + // Required: false + UniqPools []string `url:"uniqPools,omitempty" json:"uniqPools,omitempty"` + + // if True the field will be cleared + // Default: false + // Required: false + ClearUniqPools bool `url:"clearUniqPools" json:"clearUniqPools"` +} + +// Update updates an account name and resource types and limits +func (a Account) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/update" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/update_compute_features.go b/pkg/cloudbroker/account/update_compute_features.go new file mode 100644 index 0000000..a63d6d6 --- /dev/null +++ b/pkg/cloudbroker/account/update_compute_features.go @@ -0,0 +1,43 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateComputeFeaturesRequest struct to update advanced compute features +type UpdateComputeFeaturesRequest struct { + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Advanced compute features, + // one of: hugepages, numa, cpupin, vfnic + // Required: false + ComputeFeatures []string `url:"computeFeatures,omitempty" json:"computeFeatures,omitempty" validate:"omitempty,computeFeatures"` +} + +// UpdateComputeFeatures updates advanced compute features +func (a Account) UpdateComputeFeatures(ctx context.Context, req UpdateComputeFeaturesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/updateComputeFeatures" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/update_resource_types.go b/pkg/cloudbroker/account/update_resource_types.go new file mode 100644 index 0000000..18f5ee2 --- /dev/null +++ b/pkg/cloudbroker/account/update_resource_types.go @@ -0,0 +1,48 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateResourceTypesRequest struct to update resource types in account +type UpdateResourceTypesRequest struct { + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Resource types available to create in this account + // Each element in a resource type slice must be one of: + // - compute + // - vins + // - k8s + // - openshift + // - lb + // - flipgroup + // Required: true + ResTypes []string `url:"resourceTypes" json:"resourceTypes" validate:"min=1,resTypes"` +} + +func (a Account) UpdateResourceTypes(ctx context.Context, req UpdateResourceTypesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/updateResourceTypes" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/account/update_user.go b/pkg/cloudbroker/account/update_user.go new file mode 100644 index 0000000..326f1b2 --- /dev/null +++ b/pkg/cloudbroker/account/update_user.go @@ -0,0 +1,49 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateUserRequest struct to update user access rights +type UpdateUserRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Userid/Email for registered users or emailaddress for unregistered users + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` + + // Account permission types: + // - 'R' for read only access + // - 'RCX' for Write + // - 'ARCXDU' for Admin + // Required: true + AccessType string `url:"accesstype" json:"accesstype" validate:"accessType"` +} + +// UpdateUser updates user access rights +func (a Account) UpdateUser(ctx context.Context, req UpdateUserRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/updateUser" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/apiaccess.go b/pkg/cloudbroker/apiaccess.go new file mode 100644 index 0000000..a647342 --- /dev/null +++ b/pkg/cloudbroker/apiaccess.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/apiaccess" + +// Accessing the APIAccess method group +func (cb *CloudBroker) APIAccess() *apiaccess.APIAccess { + return apiaccess.New(cb.client) +} diff --git a/pkg/cloudbroker/apiaccess/api_find.go b/pkg/cloudbroker/apiaccess/api_find.go new file mode 100644 index 0000000..31fa7a3 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/api_find.go @@ -0,0 +1,41 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// APIFindRequest struct for finding apiaccess groups. +type APIFindRequest struct { + // API function to find + // Example: cloudbroker/k8s/create + // Required: true + APIName string `url:"apiName" json:"apiName" validate:"required"` +} + +// APIFind outputs a list of apiaccess groups that mention the specified API function. +func (a APIAccess) APIFind(ctx context.Context, req APIFindRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/apiFind" + + list := make([]uint64, 0) + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/apiaccess/apiaccess.go b/pkg/cloudbroker/apiaccess/apiaccess.go new file mode 100644 index 0000000..45c84e4 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/apiaccess.go @@ -0,0 +1,15 @@ +package apiaccess + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to APIAccess +type APIAccess struct { + client interfaces.Caller +} + +// Builder for APIAccess endpoints +func New(client interfaces.Caller) *APIAccess { + return &APIAccess{ + client: client, + } +} diff --git a/pkg/cloudbroker/apiaccess/apis_exclude.go b/pkg/cloudbroker/apiaccess/apis_exclude.go new file mode 100644 index 0000000..d2073ff --- /dev/null +++ b/pkg/cloudbroker/apiaccess/apis_exclude.go @@ -0,0 +1,69 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type APIString string + +// APIsExcludeRequest struct for removing api from access group. +type APIsExcludeRequest struct { + // APIAccess group ID + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` + + // APIs to remove from APIAccess group + // Required: true + APIs APIString `url:"-" json:"apis" validate:"required"` +} + +type wrapperAPIsExcludeRequest struct { + APIsExcludeRequest + + APIString string `url:"apis"` +} + +// APIsExclude removes the listed API functions from the specified apiaccess group. +func (a APIAccess) APIsExclude(ctx context.Context, req APIsExcludeRequest) (*APIsEndpoints, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/apisExclude" + + info := APIsEndpoints{} + + apiJSON, err := json.Marshal(&req.APIs) + if err != nil { + return nil, err + } + + apiJSONPretty, err := json.MarshalIndent(&req.APIs, "", " ") + if err != nil { + return nil, err + } + fmt.Println(string(apiJSONPretty)) + + reqWrapped := wrapperAPIsExcludeRequest{ + APIsExcludeRequest: req, + APIString: string(apiJSON), + } + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/apiaccess/apis_include.go b/pkg/cloudbroker/apiaccess/apis_include.go new file mode 100644 index 0000000..9996a2a --- /dev/null +++ b/pkg/cloudbroker/apiaccess/apis_include.go @@ -0,0 +1,60 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// APIsIncludeRequest struct for adding api to access group. +type APIsIncludeRequest struct { + // APIAccess group ID. + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` + + // APIs to add to APIAccess group. + // Required: true + APIs APIString `url:"-" json:"apis" validate:"required"` +} + +type wrapperAPIsIncludeRequest struct { + APIsIncludeRequest + + APIString string `url:"apis"` +} + +// APIsInclude adds the listed API functions to the specified apiaccess group. +func (a APIAccess) APIsInclude(ctx context.Context, req APIsIncludeRequest) (*APIsEndpoints, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/apisInclude" + + info := APIsEndpoints{} + + apiJSON, err := json.Marshal(&req.APIs) + if err != nil { + return nil, err + } + + reqWrapped := wrapperAPIsIncludeRequest{ + APIsIncludeRequest: req, + APIString: string(apiJSON), + } + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/apiaccess/copy.go b/pkg/cloudbroker/apiaccess/copy.go new file mode 100644 index 0000000..07e9c75 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/copy.go @@ -0,0 +1,42 @@ +package apiaccess + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CopyRequest Request for copying apiaccess group. +type CopyRequest struct { + // ID of the API access group to make copy from + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` + + // Name of the target API access group, which will be created on successful copy + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +// Copy creates a copy of the specified apiaccess group with a new name (and a new unique ID). +func (a APIAccess) Copy(ctx context.Context, req CopyRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/copy" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/apiaccess/create.go b/pkg/cloudbroker/apiaccess/create.go new file mode 100644 index 0000000..367228e --- /dev/null +++ b/pkg/cloudbroker/apiaccess/create.go @@ -0,0 +1,42 @@ +package apiaccess + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct for creating apiaccess group. +type CreateRequest struct { + // Name of this apiaccess group. + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Description of this apiaccess group. + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// Create creates apiaccess group. +func (a APIAccess) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/create" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/apiaccess/delete.go b/pkg/cloudbroker/apiaccess/delete.go new file mode 100644 index 0000000..f633603 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/delete.go @@ -0,0 +1,42 @@ +package apiaccess + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct for deleting apiaccess group. +type DeleteRequest struct { + // APIAccess group ID. + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` + + // Set True to delete apiaccess group with attached users. + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Delete deletes apiaccess group +func (a APIAccess) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/delete" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/apiaccess/desc_update.go b/pkg/cloudbroker/apiaccess/desc_update.go new file mode 100644 index 0000000..3529ee1 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/desc_update.go @@ -0,0 +1,42 @@ +package apiaccess + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DescUpdateRequest struct for updating apiaccess group description. +type DescUpdateRequest struct { + // APIAccess group ID. + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` + + // New description to set for the apiaccess group. + // Required: true + Description string `url:"desc" json:"desc" validate:"required"` +} + +// DescUpdate sets a new text description of the apiaccess group. +func (a APIAccess) DescUpdate(ctx context.Context, req DescUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/descUpdate" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/apiaccess/get.go b/pkg/cloudbroker/apiaccess/get.go new file mode 100644 index 0000000..af5e7ec --- /dev/null +++ b/pkg/cloudbroker/apiaccess/get.go @@ -0,0 +1,46 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get apiaccess group. +type GetRequest struct { + // APIAccess group ID. + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` +} + +// Get gets apiaccess group as an ItemAPIAccess struct +func (a APIAccess) Get(ctx context.Context, req GetRequest) (*ItemAPIAccess, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ItemAPIAccess{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets apiaccess group as an array of bytes +func (a APIAccess) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/apiaccess/get_full.go b/pkg/cloudbroker/apiaccess/get_full.go new file mode 100644 index 0000000..db79d69 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/get_full.go @@ -0,0 +1,26 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" +) + +// GetFull gets full current endpoints dictionary +func (a APIAccess) GetFull(ctx context.Context) (*APIsEndpoints, error) { + url := "/cloudbroker/apiaccess/getFull" + + info := APIsEndpoints{} + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/apiaccess/get_pre_groups.go b/pkg/cloudbroker/apiaccess/get_pre_groups.go new file mode 100644 index 0000000..0315f4d --- /dev/null +++ b/pkg/cloudbroker/apiaccess/get_pre_groups.go @@ -0,0 +1,26 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" +) + +// GetPreGroups gets list of pre default groups from spec +func (a APIAccess) GetPreGroups(ctx context.Context) (map[string]APIsEndpoints, error) { + url := "/cloudbroker/apiaccess/getPreGroups" + + info := make(map[string]APIsEndpoints) + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return info, nil +} diff --git a/pkg/cloudbroker/apiaccess/ids.go b/pkg/cloudbroker/apiaccess/ids.go new file mode 100644 index 0000000..ec571a3 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/ids.go @@ -0,0 +1,10 @@ +package apiaccess + +// IDs gets array of APIAccessId from ListAPIAccess struct +func (laa ListAPIAccess) IDs() []uint64 { + res := make([]uint64, 0, len(laa.Data)) + for _, apiaccess := range laa.Data { + res = append(res, apiaccess.ID) + } + return res +} diff --git a/pkg/cloudbroker/apiaccess/list.go b/pkg/cloudbroker/apiaccess/list.go new file mode 100644 index 0000000..0be7777 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/list.go @@ -0,0 +1,79 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of all non deleted apiaccess instances. +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name apiaccess + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by status apiaccess + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by created actor + // Required: false + CreatedBy string `url:"createdBy,omitempty" json:"createdBy,omitempty"` + + // Find by created after time (unix timestamp) + // Required: false + CreatedAfter uint64 `url:"createdAfter,omitempty" json:"createdAfter,omitempty"` + + // Find by created before time (unix timestamp) + // Required: false + CreatedBefore uint64 `url:"createdBefore,omitempty" json:"createdBefore,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size, maximum - 100 + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all non deleted apiaccess instances as a ListAPIAccess struct +func (a APIAccess) List(ctx context.Context, req ListRequest) (*ListAPIAccess, error) { + + res, err := a.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ListAPIAccess{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// ListRaw gets list of all non deleted apiaccess instances as an array of bytes +func (a APIAccess) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/apiaccess/list_deleted.go b/pkg/cloudbroker/apiaccess/list_deleted.go new file mode 100644 index 0000000..ba36f26 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/list_deleted.go @@ -0,0 +1,48 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct for getting list of all deleted apiaccess instances. +type ListDeletedRequest struct { + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number. + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size. + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list of all deleted apiaccess instances. +func (a APIAccess) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListAPIAccess, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/listDeleted" + + info := ListAPIAccess{} + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/apiaccess/models.go b/pkg/cloudbroker/apiaccess/models.go new file mode 100644 index 0000000..82b23da --- /dev/null +++ b/pkg/cloudbroker/apiaccess/models.go @@ -0,0 +1,180 @@ +package apiaccess + +type ItemAPIAccess struct { + // APIs + APIs APIsEndpoints `json:"apis"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Is default + Default bool `json:"default"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"decs"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Is protected + Protected bool `json:"protected"` + + // Status + Status string `json:"status"` + + //Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +type ListAPIAccess struct { + Data []ItemAPIAccess `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +type APIsEndpoints struct { + // CloudAPI endpoints + CloudAPI CloudAPIEndpoints `json:"cloudapi,omitempty"` + + // CloudBroker endpoints + CloudBroker CloudBrokerEndpoints `json:"cloudbroker,omitempty"` + + // LibCloud endpoints + LibCloud LibCloudEndpoints `json:"libcloud,omitempty"` + + // System endpoints + System SystemEndpoints `json:"system,omitempty"` +} + +type CloudAPIEndpoints struct { + Account []string `json:"account,omitempty"` + BService []string `json:"bservice,omitempty"` + CloudSpace []string `json:"cloudspace,omitempty"` + Compute []string `json:"compute,omitempty"` + ComputeCI []string `json:"computeci,omitempty"` + Disks []string `json:"disks,omitempty"` + ExtNet []string `json:"extnet,omitempty"` + FLIPGroup []string `json:"flipgroup,omitempty"` + GPU []string `json:"gpu,omitempty"` + Image []string `json:"image,omitempty"` + K8CI []string `json:"k8ci,omitempty"` + K8S []string `json:"k8s,omitempty"` + KVMX86 []string `json:"kvmx86,omitempty"` + LB []string `json:"lb,omitempty"` + Loactions []string `json:"locations,omitempty"` + Machine []string `json:"machine,omitempty"` + Openshift []string `json:"openshift,omitempty"` + OpenshiftCI []string `json:"openshiftci,omitempty"` + PCIDevice []string `json:"pcidevice,omitempty"` + PortForwarding []string `json:"portforwarding,omitempty"` + Prometheus []string `json:"prometheus,omitempty"` + RG []string `json:"rg,omitempty"` + Sizes []string `json:"sizes,omitempty"` + Tasks []string `json:"tasks,omitempty"` + User []string `json:"user,omitempty"` + VGPU []string `json:"vgpu,omitempty"` + VINS []string `json:"vins,omitempty"` + All bool `json:"ALL,omitempty"` +} + +type CloudBrokerEndpoints struct { + Account []string `json:"account,omitempty"` + APIAccess []string `json:"apiaccess,omitempty"` + Audit interface{} `json:"audit,omitempty"` + AuditBeat []string `json:"auditbeat,omitempty"` + AuditCollector []string `json:"auditcollector,omitempty"` + BackupCreator []string `json:"backupcreator,omitempty"` + BService []string `json:"bservice,omitempty"` + CloudSpace []string `json:"cloudspace,omitempty"` + Compute []string `json:"compute,omitempty"` + ComputeCI []string `json:"computeci,omitempty"` + Desnode []string `json:"desnode,omitempty"` + Diagnostics []string `json:"diagnostics,omitempty"` + Disks []string `json:"disks,omitempty"` + Eco []string `json:"eco,omitempty"` + ExtNet []string `json:"extnet,omitempty"` + FlIPgroup []string `json:"flipgroup,omitempty"` + Grid []string `json:"grid,omitempty"` + Group []string `json:"group,omitempty"` + Health []string `json:"health,omitempty"` + IaaS []string `json:"iaas,omitempty"` + Image []string `json:"image,omitempty"` + Job interface{} `json:"job,omitempty"` + K8CI []string `json:"k8ci,omitempty"` + K8S []string `json:"k8s,omitempty"` + KVMX86 []string `json:"kvmx86,omitempty"` + LB []string `json:"lb,omitempty"` + Machine []string `json:"machine,omitempty"` + Metering []string `json:"metering,omitempty"` + Milestones []string `json:"milestones,omitempty"` + Node []string `json:"node,omitempty"` + Openshift []string `json:"openshift,omitempty"` + OpenshiftCI []string `json:"openshiftci,omitempty"` + Ovsnode []string `json:"ovsnode,omitempty"` + PCIDevice []string `json:"pcidevice,omitempty"` + PGPU []string `json:"pgpu,omitempty"` + Prometheus []string `json:"prometheus,omitempty"` + QOS []string `json:"qos,omitempty"` + Resmon interface{} `json:"resmon,omitempty"` + RG []string `json:"rg,omitempty"` + Sep []string `json:"sep,omitempty"` + Stack []string `json:"stack,omitempty"` + Tasks []string `json:"tasks,omitempty"` + TLock []string `json:"tlock,omitempty"` + User []string `json:"user,omitempty"` + VGPU []string `json:"vgpu,omitempty"` + VINS []string `json:"vins,omitempty"` + VNFDev []string `json:"vnfdev,omitempty"` + ZeroAccess []string `json:"zeroaccess,omitempty"` + All bool `json:"ALL,omitempty"` +} + +type LibCloudEndpoints struct { + Libvirt []string `json:"libvirt,omitempty"` + All bool `json:"ALL,omitempty"` +} + +type SystemEndpoints struct { + AgentController []string `json:"agentcontroller,omitempty"` + Alerts []string `json:"alerts,omitempty"` + Audits []string `json:"audits,omitempty"` + ContentManager []string `json:"contentmanager,omitempty"` + DocGenerator []string `json:"docgenerator,omitempty"` + EmailSender []string `json:"emailsender,omitempty"` + ErrorConditionHandler []string `json:"errorconditionhandler,omitempty"` + GridManager []string `json:"gridmanager,omitempty"` + Health []string `json:"health,omitempty"` + Info []string `json:"info,omitempty"` + InfoMGR []string `json:"infomgr,omitempty"` + Job []string `json:"job,omitempty"` + Log []string `json:"log,omitempty"` + Logo []string `json:"logo,omitempty"` + Oauth []string `json:"oauth,omitempty"` + Task []string `json:"task,omitempty"` + UserManager []string `json:"usermanager,omitempty"` + All bool `json:"ALL,omitempty"` +} diff --git a/pkg/cloudbroker/apiaccess/set_default.go b/pkg/cloudbroker/apiaccess/set_default.go new file mode 100644 index 0000000..ba05696 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/set_default.go @@ -0,0 +1,38 @@ +package apiaccess + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetDefaultRequest struct for setting default apiaccess group. +type SetDefaultRequest struct { + // APIAccess group ID + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` +} + +// SetDefault sets current apiaccess group default. +func (a APIAccess) SetDefault(ctx context.Context, req SetDefaultRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/setDefault" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/apiaccess/subtract.go b/pkg/cloudbroker/apiaccess/subtract.go new file mode 100644 index 0000000..d066073 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/subtract.go @@ -0,0 +1,43 @@ +package apiaccess + +import ( + "encoding/json" + "net/http" + + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SubtractRequest struct for subtracting. +type SubtractRequest struct { + // ID of the API access group to subtract from. This group will contain the difference. + MinuendID uint64 `url:"minuendId" json:"minuendId" validate:"required"` + + // ID of the API access group which is subtracted. This group is unchanged. + SubtrahendID uint64 `url:"subtrahendId" json:"subtrahendId" validate:"required"` +} + +// Subtract removes such APIs from MinuendID that match APIs from SubtrahendID. +func (a APIAccess) Subtruct(ctx context.Context, req SubtractRequest) (*APIsEndpoints, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/subtract" + + info := APIsEndpoints{} + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/apiaccess/union.go b/pkg/cloudbroker/apiaccess/union.go new file mode 100644 index 0000000..92e64c1 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/union.go @@ -0,0 +1,45 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UnionRequest struct for union. +type UnionRequest struct { + // Recipient apiaccess group ID + // Required: true + RecipientID uint64 `url:"recipientId" json:"recipientId" validate:"required"` + + // Donor apiaccess group ID + // Required: true + DonorID uint64 `url:"donorId" json:"donorId" validate:"required"` +} + +// Union combines the API list of group #1 ("recipient") and group #2 ("donor"), +// writing the result to group #1 and avoiding duplicates in the list +func (a APIAccess) Union(ctx context.Context, req UnionRequest) (*APIsEndpoints, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/union" + + info := APIsEndpoints{} + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/apiaccess/update.go b/pkg/cloudbroker/apiaccess/update.go new file mode 100644 index 0000000..688f424 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/update.go @@ -0,0 +1,60 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct for updating apis of apiaccess group. +type UpdateRequest struct { + // APIAccess group ID + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` + + // APIs to remove from APIAccess group + // Required: false + APIs APIsEndpoints `url:"-" json:"-"` +} + +type wrapperUpdateRequest struct { + UpdateRequest + + APIString string `url:"apis"` +} + +// Update updates apis of apiaccess group. +func (a APIAccess) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/update" + + reqWrapped := wrapperUpdateRequest{ + UpdateRequest: req, + } + + apiJSON, err := json.Marshal(&req.APIs) + if err != nil { + return false, err + } + + reqWrapped.APIString = string(apiJSON) + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/apiaccess/user_list.go b/pkg/cloudbroker/apiaccess/user_list.go new file mode 100644 index 0000000..757a91e --- /dev/null +++ b/pkg/cloudbroker/apiaccess/user_list.go @@ -0,0 +1,48 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserListRequest struct for getting a list of users currently included in the specified group. +type UserListRequest struct { + // APIAccess group ID + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// UserList gets a list of users currently included in the specified group. +func (a APIAccess) UserList(ctx context.Context, req UserListRequest) ([]string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/apiaccess/userList" + + list := make([]string, 0) + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/audit.go b/pkg/cloudbroker/audit.go new file mode 100644 index 0000000..2308dc5 --- /dev/null +++ b/pkg/cloudbroker/audit.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/audit" +) + +// Accessing the Stack method group +func (cb *CloudBroker) Audit() *audit.Audit { + return audit.New(cb.client) +} diff --git a/pkg/cloudbroker/audit/audit.go b/pkg/cloudbroker/audit/audit.go new file mode 100644 index 0000000..81e0263 --- /dev/null +++ b/pkg/cloudbroker/audit/audit.go @@ -0,0 +1,15 @@ +package audit + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to audit +type Audit struct { + client interfaces.Caller +} + +// Builder for audit endpoint +func New(client interfaces.Caller) *Audit { + return &Audit{ + client: client, + } +} diff --git a/pkg/cloudbroker/audit/export_audits_to_file.go b/pkg/cloudbroker/audit/export_audits_to_file.go new file mode 100644 index 0000000..e0dc5fb --- /dev/null +++ b/pkg/cloudbroker/audit/export_audits_to_file.go @@ -0,0 +1,18 @@ +package audit + +import ( + "context" + "net/http" +) + +// ExportAuditsToFile Get audits in csv file, return []byte which should be written to a file *tar.gz +func (a Audit) ExportAuditsToFile(ctx context.Context) ([]byte, error) { + url := "/cloudbroker/audit/exportAuditsToFile" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + return res, nil +} diff --git a/pkg/cloudbroker/audit/get.go b/pkg/cloudbroker/audit/get.go new file mode 100644 index 0000000..1aa4bf3 --- /dev/null +++ b/pkg/cloudbroker/audit/get.go @@ -0,0 +1,46 @@ +package audit + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about account +type GetRequest struct { + // Audit GUID + // Required: true + AuditGuid string `url:"auditGuid" json:"auditGuid" validate:"required"` +} + +// Get gets information about audit as a RecordAudit struct +func (a Audit) Get(ctx context.Context, req GetRequest) (*RecordAudit, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordAudit{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about audit as an array of bytes +func (a Audit) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/audit/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/audit/linked_jobs.go b/pkg/cloudbroker/audit/linked_jobs.go new file mode 100644 index 0000000..812b4db --- /dev/null +++ b/pkg/cloudbroker/audit/linked_jobs.go @@ -0,0 +1,46 @@ +package audit + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// LinkedJobsRequest struct to get information about jobs linked with Audit +type LinkedJobsRequest struct { + // Audit GUID + // Required: true + AuditGuid string `url:"auditGuid" json:"auditGuid" validate:"required"` +} + +// LinkedJobs gets information about Linked Jobs as a ListLinkedJobs struct +func (a Audit) LinkedJobs(ctx context.Context, req LinkedJobsRequest) (*ListLinkedJobs, error) { + res, err := a.GetRawLinkedJobs(ctx, req) + if err != nil { + return nil, err + } + + info := ListLinkedJobs{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRawLinkedJobs gets information about Linked Jobs as an array of bytes +func (a Audit) GetRawLinkedJobs(ctx context.Context, req LinkedJobsRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/audit/linkedJobs" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/audit/list.go b/pkg/cloudbroker/audit/list.go new file mode 100644 index 0000000..19cd8fb --- /dev/null +++ b/pkg/cloudbroker/audit/list.go @@ -0,0 +1,80 @@ +package audit + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to give list of account audits +type ListRequest struct { + + // Find all audits after point in time (unixtime) + // Required: false + TimestampAt uint64 `url:"timestampAt,omitempty" json:"timestampAt,omitempty"` + + // Find all audits before point in time (unixtime) + // Required: false + TimestampTo uint64 `url:"timestampTo,omitempty" json:"timestampTo,omitempty"` + + // Find by user (Mongo RegExp supported) + // Required: false + User string `url:"user,omitempty" json:"user,omitempty"` + + // Find by api endpoint (Mongo RegExp supported) + // Required: false + Call string `url:"call,omitempty" json:"call,omitempty"` + + // Find by HTTP min status code + // Required: false + MinStatusCode uint64 `url:"minStatusCode,omitempty" json:"minStatusCode,omitempty"` + + // Find by HTTP max status code + // Required: false + MaxStatusCode uint64 `url:"maxStatusCode,omitempty" json:"maxStatusCode,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets audit records for the specified account object +func (a Audit) List(ctx context.Context, req ListRequest) (*ListAudits, error) { + + res, err := a.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of audit records an array of bytes +func (a Audit) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/audit/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/audit/models.go b/pkg/cloudbroker/audit/models.go new file mode 100644 index 0000000..adf0885 --- /dev/null +++ b/pkg/cloudbroker/audit/models.go @@ -0,0 +1,102 @@ +package audit + +// Main info about audit +type ItemAudit struct { + // Call + Call string `json:"call"` + + // GUID + GUID string `json:"guid"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List of audits +type ListAudits struct { + // Data + Data []ItemAudit `json:"data"` + + // EntryCount + EntryCount uint64 `json:"entryCount"` +} + +// Main info about audit +type RecordAudit struct { + + // Arguments + Arguments string `json:"args"` + + // Call + Call string `json:"call"` + + // GUID + GUID string `json:"guid"` + + // Kwargs + Kwargs string `json:"kwargs"` + + // RemoteAddr + RemoteAddr string `json:"remote_addr"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Result + Result string `json:"result"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Tags + Tags string `json:"tags"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // TimestampEnd + TimestampEnd float64 `json:"timestampEnd"` + + // User + User string `json:"user"` +} + +// List of Linked Jobs +type ListLinkedJobs []ItemLinkedJobs + +// Main info about Linked Jobs +type ItemLinkedJobs struct { + + // CMD + CMD string `json:"cmd"` + + // GUID + GUID string `json:"guid"` + + // NID + NID uint64 `json:"nid"` + + // state + State string `json:"state"` + + // TimeCreate + TimeCreate uint64 `json:"timeCreate"` + + // TimeStart + TimeStart uint64 `json:"timeStart"` + + // TimeStop + TimeStop uint64 `json:"timeStop"` + + // Timeout + Timeout uint64 `json:"timeout"` +} diff --git a/pkg/cloudbroker/backup.go b/pkg/cloudbroker/backup.go new file mode 100644 index 0000000..db56f36 --- /dev/null +++ b/pkg/cloudbroker/backup.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/backup" + +// Accessing the Backup method group +func (cb *CloudBroker) Backup() *backup.Backup { + return backup.New(cb.client) +} diff --git a/pkg/cloudbroker/backup/backup.go b/pkg/cloudbroker/backup/backup.go new file mode 100644 index 0000000..57e7f23 --- /dev/null +++ b/pkg/cloudbroker/backup/backup.go @@ -0,0 +1,17 @@ +package backup + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to backup +type Backup struct { + client interfaces.Caller +} + +// Builder for backup endpoints +func New(client interfaces.Caller) *Backup { + return &Backup{ + client: client, + } +} diff --git a/pkg/cloudbroker/backup/create_disk_backup.go b/pkg/cloudbroker/backup/create_disk_backup.go new file mode 100644 index 0000000..42fb513 --- /dev/null +++ b/pkg/cloudbroker/backup/create_disk_backup.go @@ -0,0 +1,84 @@ +package backup + +import ( + "context" + "encoding/json" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateDiskBackupRequest struct for creating disk backup +type CreateDiskBackupRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Disk ID + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Backup path + // Required: true + BackupPath string `url:"backupPath" json:"backupPath" validate:"required"` +} + +type wrapperCreateDiskBackupRequest struct { + CreateDiskBackupRequest + + AsyncMode bool `url:"asyncMode"` +} + +// CreateDiskBackup creates disk backup +func (b Backup) CreateDiskBackup(ctx context.Context, req CreateDiskBackupRequest) (ListInfoBackup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateDiskBackupRequest{ + CreateDiskBackupRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/backup/createDiskBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + result := make(ListInfoBackup, 0) + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// CreateDiskBackupAsync creates disk backup +func (b Backup) CreateDiskBackupAsync(ctx context.Context, req CreateDiskBackupRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateDiskBackupRequest{ + CreateDiskBackupRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/backup/createDiskBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/backup/create_disks_backup.go b/pkg/cloudbroker/backup/create_disks_backup.go new file mode 100644 index 0000000..b7aaa1e --- /dev/null +++ b/pkg/cloudbroker/backup/create_disks_backup.go @@ -0,0 +1,86 @@ +package backup + +import ( + "context" + "encoding/json" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type Disk struct { + // Disk ID + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Backup path + BackupPath string `url:"backupPath" json:"backupPath" validate:"required"` +} + +// CreateDisksBackupRequest struct for creating disks backup +type CreateDisksBackupRequest struct { + // Compute ID + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Disks + Disks []Disk `url:"disks" json:"disks" validate:"required,dive"` +} + +type wrapperCreateDisksBackupRequest struct { + CreateDisksBackupRequest + + AsyncMode bool `url:"asyncMode"` +} + +// CreateDisksBackup creates disks backup +func (b Backup) CreateDisksBackup(ctx context.Context, req CreateDisksBackupRequest) (ListInfoBackup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateDisksBackupRequest{ + CreateDisksBackupRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/backup/createDisksBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + result := make(ListInfoBackup, 0) + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// CreateDisksBackupAsync creates disks backup +func (b Backup) CreateDisksBackupAsync(ctx context.Context, req CreateDisksBackupRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateDisksBackupRequest{ + CreateDisksBackupRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/backup/createDisksBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/backup/delete_disk_backup.go b/pkg/cloudbroker/backup/delete_disk_backup.go new file mode 100644 index 0000000..783fe25 --- /dev/null +++ b/pkg/cloudbroker/backup/delete_disk_backup.go @@ -0,0 +1,76 @@ +package backup + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteDiskBackupRequest struct for deleting disk backup +type DeleteDiskBackupRequest struct { + // Backup path + BackupPath string `url:"backupPath" json:"backupPath" validate:"required"` + + // Backup file + BackupFile string `url:"backupFile" json:"backupFile" validate:"required"` +} + +type wrapperDeleteDiskBackupRequest struct { + DeleteDiskBackupRequest + + AsyncMode bool `url:"asyncMode"` +} + +// DeleteDiskBackup deletes disk backup +func (b Backup) DeleteDiskBackup(ctx context.Context, req DeleteDiskBackupRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDeleteDiskBackupRequest{ + DeleteDiskBackupRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/backup/deleteDiskBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// DeleteDiskBackupAsync deletes disk backup +func (b Backup) DeleteDiskBackupAsync(ctx context.Context, req DeleteDiskBackupRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDeleteDiskBackupRequest{ + DeleteDiskBackupRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/backup/deleteDiskBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/backup/list_backup_paths.go b/pkg/cloudbroker/backup/list_backup_paths.go new file mode 100644 index 0000000..e506ce2 --- /dev/null +++ b/pkg/cloudbroker/backup/list_backup_paths.go @@ -0,0 +1,40 @@ +package backup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListBackupPathsRequest struct for getting list of backup paths +type ListBackupPathsRequest struct { + // Grid ID + GID uint64 `url:"gridId" json:"gridId" validate:"required"` +} + +// ListBackupPaths gets list of backup paths +func (b Backup) ListBackupPaths(ctx context.Context, req ListBackupPathsRequest) ([]string, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/backup/listBackupPaths" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := make([]string, 0) + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/backup/models.go b/pkg/cloudbroker/backup/models.go new file mode 100644 index 0000000..7553740 --- /dev/null +++ b/pkg/cloudbroker/backup/models.go @@ -0,0 +1,31 @@ +package backup + +// Main info about backup +type InfoBackup struct { + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Disk ID + DiskID uint64 `json:"diskId"` + + // Backup path + BackupPath string `json:"backupPath"` + + // Possible error + Error string `json:"error"` +} + +// CreateDisksBackup response +type ListInfoBackup []InfoBackup + +// RestoreDiskFromFile response +type InfoRestoredDisk struct { + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Disk ID + DiskID uint64 `json:"diskId"` +} + +// RestoreDisksFromFile response +type ListInfoRestoredDisk []InfoRestoredDisk diff --git a/pkg/cloudbroker/backup/restore_disk_from_backup.go b/pkg/cloudbroker/backup/restore_disk_from_backup.go new file mode 100644 index 0000000..26532cd --- /dev/null +++ b/pkg/cloudbroker/backup/restore_disk_from_backup.go @@ -0,0 +1,84 @@ +package backup + +import ( + "context" + "encoding/json" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreDiskFromBackupRequest struct for restoring disk from backup +type RestoreDiskFromBackupRequest struct { + // Compute ID + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Disk ID + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Backup path + BackupPath string `url:"backupPath" json:"backupPath" validate:"required"` + + // Backup file + BackupFile string `url:"backupFile" json:"backupFile" validate:"required"` +} + +type wrapperRestoreDiskFromBackupRequest struct { + RestoreDiskFromBackupRequest + + AsyncMode bool `url:"asyncMode"` +} + +// RestoreDiskFromBackup restores disk from backup +func (b Backup) RestoreDiskFromBackup(ctx context.Context, req RestoreDiskFromBackupRequest) (ListInfoRestoredDisk, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRestoreDiskFromBackupRequest{ + RestoreDiskFromBackupRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/backup/restoreDiskFromBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + result := make(ListInfoRestoredDisk, 0) + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// RestoreDiskFromBackupAsync restores disk from backup +func (b Backup) RestoreDiskFromBackupAsync(ctx context.Context, req RestoreDiskFromBackupRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRestoreDiskFromBackupRequest{ + RestoreDiskFromBackupRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/backup/restoreDiskFromBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/backup/restore_disks_from_backup.go b/pkg/cloudbroker/backup/restore_disks_from_backup.go new file mode 100644 index 0000000..f02ea46 --- /dev/null +++ b/pkg/cloudbroker/backup/restore_disks_from_backup.go @@ -0,0 +1,89 @@ +package backup + +import ( + "context" + "encoding/json" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type BackupFile struct { + // Disk ID + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Backup path + BackupPath string `url:"backupPath" json:"backupPath" validate:"required"` + + // Backup file + BackupFile string `url:"backupFile" json:"backupFile" validate:"required"` +} + +// RestoreDisksFromBackupRequest struct for restoring disks from backup +type RestoreDisksFromBackupRequest struct { + // Compute ID + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + //Backup files + BackupFiles []BackupFile `url:"disks" json:"disks" validate:"required,dive"` +} + +type wrapperRestoreDisksFromBackupRequest struct { + RestoreDisksFromBackupRequest + + AsyncMode bool `url:"asyncMode"` +} + +// RestoreDisksFromBackup restores disks from backup +func (b Backup) RestoreDisksFromBackup(ctx context.Context, req RestoreDisksFromBackupRequest) (ListInfoRestoredDisk, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRestoreDisksFromBackupRequest{ + RestoreDisksFromBackupRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/backup/restoreDisksFromBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + result := make(ListInfoRestoredDisk, 0) + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// RestoreDisksFromBackupAsync restores disks from backup +func (b Backup) RestoreDisksFromBackupAsync(ctx context.Context, req RestoreDisksFromBackupRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRestoreDisksFromBackupRequest{ + RestoreDisksFromBackupRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/backup/restoreDisksFromBackup" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/cloudbroker.go b/pkg/cloudbroker/cloudbroker.go new file mode 100644 index 0000000..621262c --- /dev/null +++ b/pkg/cloudbroker/cloudbroker.go @@ -0,0 +1,16 @@ +// List of method groups for the admin +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to CloudBroker groups +type CloudBroker struct { + client interfaces.Caller +} + +// Builder to get access to CloudBroker +func New(client interfaces.Caller) *CloudBroker { + return &CloudBroker{ + client: client, + } +} diff --git a/pkg/cloudbroker/compute.go b/pkg/cloudbroker/compute.go new file mode 100644 index 0000000..305020e --- /dev/null +++ b/pkg/cloudbroker/compute.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/compute" +) + +// Accessing the Compute method group +func (cb *CloudBroker) Compute() *compute.Compute { + return compute.New(cb.client) +} diff --git a/pkg/cloudbroker/compute/affinity_group_check_start.go b/pkg/cloudbroker/compute/affinity_group_check_start.go new file mode 100644 index 0000000..3dc240a --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_group_check_start.go @@ -0,0 +1,36 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityGroupCheckStartRequest struct to check all computes with current affinity label can start +type AffinityGroupCheckStartRequest struct { + // ID of the resource group + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Affinity group label + // Required: true + AffinityLabel string `url:"affinityLabel" json:"affinityLabel" validate:"required"` +} + +// AffinityGroupCheckStart check all computes with current affinity label can start +func (c Compute) AffinityGroupCheckStart(ctx context.Context, req AffinityGroupCheckStartRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/affinityGroupCheckStart" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/affinity_label_remove.go b/pkg/cloudbroker/compute/affinity_label_remove.go new file mode 100644 index 0000000..dd2c31b --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_label_remove.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityLabelRemoveRequest struct for clear affinity label for compute +type AffinityLabelRemoveRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` +} + +// AffinityLabelRemove clear affinity label for compute +func (c Compute) AffinityLabelRemove(ctx context.Context, req AffinityLabelRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/affinityLabelRemove" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/affinity_label_set.go b/pkg/cloudbroker/compute/affinity_label_set.go new file mode 100644 index 0000000..496e153 --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_label_set.go @@ -0,0 +1,41 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityLabelSetRequest struct to set affinity label for compute +type AffinityLabelSetRequest struct { + // IDs of the compute instances + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Affinity group label + // Required: true + AffinityLabel string `url:"affinityLabel" json:"affinityLabel" validate:"required"` +} + +// AffinityLabelSet sets affinity label for compute +func (c Compute) AffinityLabelSet(ctx context.Context, req AffinityLabelSetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/affinityLabelSet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/affinity_relations.go b/pkg/cloudbroker/compute/affinity_relations.go new file mode 100644 index 0000000..c5cfd1f --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_relations.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityRelationsRequest struct to get dict of computes +type AffinityRelationsRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// AffinityRelations gets dict of computes divided by affinity and anti affinity rules +func (c Compute) AffinityRelations(ctx context.Context, req AffinityRelationsRequest) (*RecordAffinityRelations, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/affinityRelations" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordAffinityRelations{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/compute/affinity_rule_add.go b/pkg/cloudbroker/compute/affinity_rule_add.go new file mode 100644 index 0000000..2dc88fd --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_rule_add.go @@ -0,0 +1,68 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityRuleAddRequest struct to add affinity rule +type AffinityRuleAddRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Should be one of: + // - node + // - compute + // Required: true + Topology string `url:"topology" json:"topology" validate:"computeTopology"` + + // The degree of 'strictness' of this rule + // Should be one of: + // - RECOMMENDED + // - REQUIRED + // Required: true + Policy string `url:"policy" json:"policy" validate:"computePolicy"` + + // The comparison mode is 'value', recorded by the specified 'key' + // Should be one of: + // - EQ + // - EN + // - ANY + // Required: true + Mode string `url:"mode" json:"mode" validate:"computeMode"` + + // Key that are taken into account when analyzing this rule will be identified + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Value that must match the key to be taken into account when analyzing this rule + // Required: false + // Not required on purpose: despite required tag on platform, empty string is allowed + Value string `url:"value" json:"value"` +} + +// AffinityRuleAdd adds affinity rule +func (c Compute) AffinityRuleAdd(ctx context.Context, req AffinityRuleAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/affinityRuleAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/affinity_rule_remove.go b/pkg/cloudbroker/compute/affinity_rule_remove.go new file mode 100644 index 0000000..c91b333 --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_rule_remove.go @@ -0,0 +1,66 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityRuleRemoveRequest struct to remove affinity rule +type AffinityRuleRemoveRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Compute or node, for whom rule applies + // Required: true + Topology string `url:"topology" json:"topology" validate:"computeTopology"` + + // The degree of 'strictness' of this rule + // Should be one of: + // - RECOMMENDED + // - REQUIRED + // Required: true + Policy string `url:"policy" json:"policy" validate:"computePolicy"` + + // The comparison mode is 'value', recorded by the specified 'key' + // Should be one of: + // - EQ + // - EN + // - ANY + // Required: true + Mode string `url:"mode" json:"mode" validate:"computeMode"` + + // Key that are taken into account when analyzing this rule will be identified + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Value that must match the key to be taken into account when analyzing this rule + // Required: false + // Not required on purpose: despite required tag on platform, empty string is allowed + Value string `url:"value" json:"value"` +} + +// AffinityRuleRemove remove affinity rule +func (c Compute) AffinityRuleRemove(ctx context.Context, req AffinityRuleRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/affinityRuleRemove" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/affinity_rules_clear.go b/pkg/cloudbroker/compute/affinity_rules_clear.go new file mode 100644 index 0000000..0f74c0b --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_rules_clear.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityRulesClearRequest struct to clear affinity rules +type AffinityRulesClearRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` +} + +// AffinityRulesClear clears affinity rules +func (c Compute) AffinityRulesClear(ctx context.Context, req AffinityRulesClearRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/affinityRulesClear" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/anti_affinity_rule_add.go b/pkg/cloudbroker/compute/anti_affinity_rule_add.go new file mode 100644 index 0000000..b2a7095 --- /dev/null +++ b/pkg/cloudbroker/compute/anti_affinity_rule_add.go @@ -0,0 +1,66 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AntiAffinityRuleAddRequest struct to add anti affinity rule +type AntiAffinityRuleAddRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Compute or node, for whom rule applies + // Required: true + Topology string `url:"topology" json:"topology" validate:"computeTopology"` + + // The degree of 'strictness' of this rule + // Should be one of: + // - RECOMMENDED + // - REQUIRED + // Required: true + Policy string `url:"policy" json:"policy" validate:"computePolicy"` + + // The comparison mode is 'value', recorded by the specified 'key' + // Should be one of: + // - EQ + // - EN + // - ANY + // Required: true + Mode string `url:"mode" json:"mode" validate:"computeMode"` + + // Key that are taken into account when analyzing this rule will be identified + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Value that must match the key to be taken into account when analyzing this rule + // Required: false + // Not required on purpose: despite required tag on platform, empty string is allowed + Value string `url:"value" json:"value"` +} + +// AntiAffinityRuleAdd adds anti affinity rule +func (c Compute) AntiAffinityRuleAdd(ctx context.Context, req AntiAffinityRuleAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/antiAffinityRuleAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/anti_affinity_rule_clear.go b/pkg/cloudbroker/compute/anti_affinity_rule_clear.go new file mode 100644 index 0000000..f3fae00 --- /dev/null +++ b/pkg/cloudbroker/compute/anti_affinity_rule_clear.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AntiAffinityRulesClearRequest struct to clear anti affinity rules +type AntiAffinityRulesClearRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` +} + +// AntiAffinityRulesClear clear anti affinity rules +func (c Compute) AntiAffinityRulesClear(ctx context.Context, req AntiAffinityRulesClearRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/antiAffinityRulesClear" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/anti_affinity_rule_remove.go b/pkg/cloudbroker/compute/anti_affinity_rule_remove.go new file mode 100644 index 0000000..01d7a10 --- /dev/null +++ b/pkg/cloudbroker/compute/anti_affinity_rule_remove.go @@ -0,0 +1,66 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AntiAffinityRuleRemoveRequest struct to remove anti affinity rule +type AntiAffinityRuleRemoveRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Compute or node, for whom rule applies + // Required: true + Topology string `url:"topology" json:"topology" validate:"computeTopology"` + + // The degree of 'strictness' of this rule + // Should be one of: + // - RECOMMENDED + // - REQUIRED + // Required: true + Policy string `url:"policy" json:"policy" validate:"computePolicy"` + + // The comparison mode is 'value', recorded by the specified 'key' + // Should be one of: + // - EQ + // - EN + // - ANY + // Required: true + Mode string `url:"mode" json:"mode" validate:"computeMode"` + + // Key that are taken into account when analyzing this rule will be identified + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Value that must match the key to be taken into account when analyzing this rule + // Required: false + // Not required on purpose: despite required tag on platform, empty string is allowed + Value string `url:"value" json:"value"` +} + +// AntiAffinityRuleRemove removes anti affinity rule +func (c Compute) AntiAffinityRuleRemove(ctx context.Context, req AntiAffinityRuleRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/antiAffinityRuleRemove" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/attach_gpu.go b/pkg/cloudbroker/compute/attach_gpu.go new file mode 100644 index 0000000..041e53a --- /dev/null +++ b/pkg/cloudbroker/compute/attach_gpu.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AttachGPURequest struct to attach GPU for compute +type AttachGPURequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Identifier vGPU + // Required: true + VGPUID uint64 `url:"vgpuId" json:"vgpuId" validate:"required"` +} + +// AttachGPU attaches GPU for compute, returns vGPU ID on success +func (c Compute) AttachGPU(ctx context.Context, req AttachGPURequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/attachGpu" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/attach_pci_device.go b/pkg/cloudbroker/compute/attach_pci_device.go new file mode 100644 index 0000000..593554c --- /dev/null +++ b/pkg/cloudbroker/compute/attach_pci_device.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AttachPCIDeviceRequest struct to attach PCI device +type AttachPCIDeviceRequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` +} + +// AttachPCIDevice attaches PCI device +func (c Compute) AttachPCIDevice(ctx context.Context, req AttachPCIDeviceRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/attachPciDevice" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/audits.go b/pkg/cloudbroker/compute/audits.go new file mode 100644 index 0000000..b47638f --- /dev/null +++ b/pkg/cloudbroker/compute/audits.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuditsRequest struct to get audit records +type AuditsRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Audits gets audit records for the specified compute object +func (c Compute) Audits(ctx context.Context, req AuditsRequest) (ListDetailedAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/audits" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListDetailedAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/compute/boot_disk_set.go b/pkg/cloudbroker/compute/boot_disk_set.go new file mode 100644 index 0000000..b389afe --- /dev/null +++ b/pkg/cloudbroker/compute/boot_disk_set.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BootDiskSetRequest struct to set boot disk for compute +type BootDiskSetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to set as boot + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// BootDiskSet sets boot disk for compute +func (c Compute) BootDiskSet(ctx context.Context, req BootDiskSetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/bootDiskSet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/boot_order_get.go b/pkg/cloudbroker/compute/boot_order_get.go new file mode 100644 index 0000000..3f19e7c --- /dev/null +++ b/pkg/cloudbroker/compute/boot_order_get.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BootOrderGetRequest struct to get boot order +type BootOrderGetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// BootOrderGet gets actual compute boot order information +func (c Compute) BootOrderGet(ctx context.Context, req BootOrderGetRequest) ([]string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/bootOrderGet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + orders := make([]string, 0) + + err = json.Unmarshal(res, &orders) + if err != nil { + return nil, err + } + + return orders, nil +} diff --git a/pkg/cloudbroker/compute/boot_order_set.go b/pkg/cloudbroker/compute/boot_order_set.go new file mode 100644 index 0000000..fd937e7 --- /dev/null +++ b/pkg/cloudbroker/compute/boot_order_set.go @@ -0,0 +1,48 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BootOrderSetRequest struct to set boot order +type BootOrderSetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // List of boot devices + // Should be one of: + // - cdrom + // - network + // - hd + // Required: true + Order []string `url:"order" json:"order" validate:"min=1,computeOrder"` +} + +// BootOrderSet sets compute boot order +func (c Compute) BootOrderSet(ctx context.Context, req BootOrderSetRequest) ([]string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/bootOrderSet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + orders := make([]string, 0) + + err = json.Unmarshal(res, &orders) + if err != nil { + return nil, err + } + + return orders, nil +} diff --git a/pkg/cloudbroker/compute/cd_eject.go b/pkg/cloudbroker/compute/cd_eject.go new file mode 100644 index 0000000..a4a09eb --- /dev/null +++ b/pkg/cloudbroker/compute/cd_eject.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CDEjectRequest struct to eject CD image +type CDEjectRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// CDEject ejects CD image to compute's CD-ROM +func (c Compute) CDEject(ctx context.Context, req CDEjectRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/cdEject" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, err +} diff --git a/pkg/cloudbroker/compute/cd_insert.go b/pkg/cloudbroker/compute/cd_insert.go new file mode 100644 index 0000000..d0694ef --- /dev/null +++ b/pkg/cloudbroker/compute/cd_insert.go @@ -0,0 +1,36 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CDInsertRequest struct to insert new CD image +type CDInsertRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of CD-ROM image + // Required: true + CDROMID uint64 `url:"cdromId" json:"cdromId" validate:"required"` +} + +// CDInsert inserts new CD image to compute's CD-ROM +func (c Compute) CDInsert(ctx context.Context, req CDInsertRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/cdInsert" + + _, err = c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/compute/change_ip.go b/pkg/cloudbroker/compute/change_ip.go new file mode 100644 index 0000000..5c7b6c9 --- /dev/null +++ b/pkg/cloudbroker/compute/change_ip.go @@ -0,0 +1,54 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ChangeIPRequest struct to change IP for network +type ChangeIPRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Network type + // 'EXTNET' for connect to external network directly + // 'VINS' for connect to ViNS + // Required: true + NetType string `url:"netType" json:"netType" validate:"computeNetType"` + + // Network ID for connect to + // For EXTNET - external network ID + // For VINS - VINS ID + // Required: true + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // IP address to which we will change the existing one, it must be from the same subnet + // Required: true + IPAddr string `url:"ipAddr" json:"ipAddr" validate:"required"` +} + +// ChangeIP change reserved IP for compute instance +func (c Compute) ChangeIP(ctx context.Context, req ChangeIPRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/changeIp" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/change_link_state.go b/pkg/cloudbroker/compute/change_link_state.go new file mode 100644 index 0000000..56e2d9a --- /dev/null +++ b/pkg/cloudbroker/compute/change_link_state.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ChangeLinkStateRequest struct for changing link state +type ChangeLinkStateRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Interface name or MAC address + // Required: true + Interface string `url:"interface" json:"interface" validate:"required"` + + // Interface state + // Must be either "on" or "off" + // Required: true + State string `url:"state" json:"state" validate:"required,interfaceState"` +} + +// ChangeLinkState changes the status link virtual of compute +func (c Compute) ChangeLinkState(ctx context.Context, req ChangeLinkStateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/changeLinkState" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + return result, nil +} diff --git a/pkg/cloudbroker/compute/clone.go b/pkg/cloudbroker/compute/clone.go new file mode 100644 index 0000000..82f3c6f --- /dev/null +++ b/pkg/cloudbroker/compute/clone.go @@ -0,0 +1,55 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CloneRequest struct to clone compute instance +type CloneRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the clone + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Timestamp of the parent's snapshot to create clone from + // Required: false + SnapshotTimestamp uint64 `url:"snapshotTimestamp" json:"snapshotTimestamp"` + + // Name of the parent's snapshot to create clone from + // Required: false + SnapshotName string `url:"snapshotName" json:"snapshotName"` + + // true ignore that the compute is running + // Default: false + // Required: false + Force bool `url:"force" json:"force"` +} + +// Clone clones compute instance +func (c Compute) Clone(ctx context.Context, req CloneRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/clone" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/compute.go b/pkg/cloudbroker/compute/compute.go new file mode 100644 index 0000000..b51dcc7 --- /dev/null +++ b/pkg/cloudbroker/compute/compute.go @@ -0,0 +1,16 @@ +// API Actor for managing Compute. This actor is a final API for admin to manage Compute +package compute + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to compute +type Compute struct { + client interfaces.Caller +} + +// Builder for compute endpoints +func New(client interfaces.Caller) *Compute { + return &Compute{ + client: client, + } +} diff --git a/pkg/cloudbroker/compute/computeci_set.go b/pkg/cloudbroker/compute/computeci_set.go new file mode 100644 index 0000000..a2a0845 --- /dev/null +++ b/pkg/cloudbroker/compute/computeci_set.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ComputeCISetRequest struct to set compute CI +type ComputeCISetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the Compute CI + // Required: true + ComputeCIID uint64 `url:"computeciId" json:"computeciId" validate:"required"` +} + +// ComputeCISet sets compute CI ID for compute +func (c Compute) ComputeCISet(ctx context.Context, req ComputeCISetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/computeciSet" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/computeci_unset.go b/pkg/cloudbroker/compute/computeci_unset.go new file mode 100644 index 0000000..8498e46 --- /dev/null +++ b/pkg/cloudbroker/compute/computeci_unset.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ComputeCIUnsetRequest struct to unset compute CI +type ComputeCIUnsetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// ComputeCIUnset unsets compute CI ID from compute +func (c Compute) ComputeCIUnset(ctx context.Context, req ComputeCIUnsetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/computeciUnset" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/create_template.go b/pkg/cloudbroker/compute/create_template.go new file mode 100644 index 0000000..608a6fb --- /dev/null +++ b/pkg/cloudbroker/compute/create_template.go @@ -0,0 +1,78 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateTemplateRequest struct to create template +type CreateTemplateRequest struct { + // ID of the compute to create template from + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name to assign to the template being created + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +type wrapperCreateTemplateRequest struct { + CreateTemplateRequest + + AsyncMode bool `url:"asyncMode"` +} + +// CreateTemplateAsync create template from compute instance +func (c Compute) CreateTemplateAsync(ctx context.Context, req CreateTemplateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateTemplateRequest{ + CreateTemplateRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/createTemplate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} + +// CreateTemplate create template from compute instance +func (c Compute) CreateTemplate(ctx context.Context, req CreateTemplateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateTemplateRequest{ + CreateTemplateRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/createTemplate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/create_template_from_blank.go b/pkg/cloudbroker/compute/create_template_from_blank.go new file mode 100644 index 0000000..070bd3e --- /dev/null +++ b/pkg/cloudbroker/compute/create_template_from_blank.go @@ -0,0 +1,112 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateTemplateFromBlankRequest struct to create template from boot disk of current compute +type CreateTemplateFromBlankRequest struct { + // ID of the compute to create template from + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the rescue disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Boot type of image BIOS or UEFI + // Required: true + BootType string `url:"boottype" json:"boottype" validate:"imageBootType"` + + // Image type linux, windows or other + // Required: true + ImageType string `url:"imagetype" json:"imagetype" validate:"imageType"` + + // Username for the image + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Password for the image + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // Account ID to make the image exclusive + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // SEP ID + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool for image create + // Required: false + PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // Does this machine supports hot resize + // Default: false + // Required: false + HotResize bool `url:"hotresize" json:"hotresize"` +} + +type wrapperCreateTemplateFromBlankRequest struct { + CreateTemplateFromBlankRequest + AsyncMode bool `url:"asyncMode"` +} + +// CreateTemplateFromBlank creates template from boot disk of current compute in sync mode. +// It returns id of created compute and error. +func (c Compute) CreateTemplateFromBlank(ctx context.Context, req CreateTemplateFromBlankRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateTemplateFromBlankRequest{ + CreateTemplateFromBlankRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/createTemplateFromBlank" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} + +// CreateTemplateFromBlankAsync creates template from boot disk of current compute in async mode. +// It returns guid of task and error. +func (c Compute) CreateTemplateFromBlankAsync(ctx context.Context, req CreateTemplateFromBlankRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCreateTemplateFromBlankRequest{ + CreateTemplateFromBlankRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/createTemplateFromBlank" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/compute/delete.go b/pkg/cloudbroker/compute/delete.go new file mode 100644 index 0000000..f7542d2 --- /dev/null +++ b/pkg/cloudbroker/compute/delete.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete compute +type DeleteRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Delete permanently + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` + + // Set True if you want to detach data disks (if any) from the compute before its deletion + // Required: false + DetachDisks bool `url:"detachDisks,omitempty" json:"detachDisks,omitempty"` +} + +// Delete deletes compute +func (c Compute) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/delete" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/delete_custom_fields.go b/pkg/cloudbroker/compute/delete_custom_fields.go new file mode 100644 index 0000000..19331b3 --- /dev/null +++ b/pkg/cloudbroker/compute/delete_custom_fields.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteCustomFieldsRequest struct to delete compute's custom fields +type DeleteCustomFieldsRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// DeleteCustomFields deletes computes custom fields +func (c Compute) DeleteCustomFields(ctx context.Context, req DeleteCustomFieldsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/deleteCustomFields" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/detach_gpu.go b/pkg/cloudbroker/compute/detach_gpu.go new file mode 100644 index 0000000..42f987c --- /dev/null +++ b/pkg/cloudbroker/compute/detach_gpu.go @@ -0,0 +1,43 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DetachGPURequest struct to detach VGPU for compute +type DetachGPURequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Identifier virtual GPU + // Required: false + VGPUID int64 `url:"vgpuId,omitempty" json:"vgpuId,omitempty"` +} + +// DetachGPU detaches VGPU for compute. +// If param VGPU ID is equivalent -1, then detach all VGPU for compute +func (c Compute) DetachGPU(ctx context.Context, req DetachGPURequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/detachGpu" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/detach_pci_device.go b/pkg/cloudbroker/compute/detach_pci_device.go new file mode 100644 index 0000000..dfb7164 --- /dev/null +++ b/pkg/cloudbroker/compute/detach_pci_device.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DetachPCIDeviceRequest struct to detach PCI device +type DetachPCIDeviceRequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` +} + +// DetachPciDevice detaches PCI device +func (c Compute) DetachPciDevice(ctx context.Context, req DetachPCIDeviceRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/detachPciDevice" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disable.go b/pkg/cloudbroker/compute/disable.go new file mode 100644 index 0000000..81f15e5 --- /dev/null +++ b/pkg/cloudbroker/compute/disable.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable compute +type DisableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Disable disables compute +func (c Compute) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/disable" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disk_add.go b/pkg/cloudbroker/compute/disk_add.go new file mode 100644 index 0000000..948ba7a --- /dev/null +++ b/pkg/cloudbroker/compute/disk_add.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskAddRequest struct to create and attach disk to compute +type DiskAddRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name for disk + // Required: true + DiskName string `url:"diskName" json:"diskName" validate:"required"` + + // Disk size in GB + // Required: true + Size uint64 `url:"size" json:"size" validate:"required"` + + // Storage endpoint provider ID + // By default the same with boot disk + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Type of the disk + // Should be one of: + // - D + // - B + // Required: false + DiskType string `url:"diskType,omitempty" json:"diskType,omitempty" validate:"omitempty,computeDiskType"` + + // Pool name + // By default will be chosen automatically + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Optional description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Specify image id for create disk from template + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` +} + +// DiskAdd creates new disk and attach to compute +func (c Compute) DiskAdd(ctx context.Context, req DiskAddRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/diskAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disk_attach.go b/pkg/cloudbroker/compute/disk_attach.go new file mode 100644 index 0000000..6c5e66d --- /dev/null +++ b/pkg/cloudbroker/compute/disk_attach.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskAttachRequest struct to attach disk to compute +type DiskAttachRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to attach + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Type of the disk B;D + // Required: false + DiskType string `url:"diskType,omitempty" json:"diskType,omitempty"` +} + +// DiskAttach attach disk to compute +func (c Compute) DiskAttach(ctx context.Context, req DiskAttachRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/diskAttach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disk_del.go b/pkg/cloudbroker/compute/disk_del.go new file mode 100644 index 0000000..b16873e --- /dev/null +++ b/pkg/cloudbroker/compute/disk_del.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskDelRequest struct to detach and delete disk from compute +type DiskDelRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of disk instance + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // False if disk is to be deleted to recycle bin + // Required: true + Permanently bool `url:"permanently" json:"permanently"` +} + +// DiskDel deletes disk and detaches it from compute +func (c Compute) DiskDel(ctx context.Context, req DiskDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/diskDel" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disk_detach.go b/pkg/cloudbroker/compute/disk_detach.go new file mode 100644 index 0000000..854c682 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_detach.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskDetachRequest struct to detach disk from compute +type DiskDetachRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to detach + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// DiskDetach detaches disk from compute +func (c Compute) DiskDetach(ctx context.Context, req DiskDetachRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/diskDetach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disk_migrate.go b/pkg/cloudbroker/compute/disk_migrate.go new file mode 100644 index 0000000..6c7d493 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_migrate.go @@ -0,0 +1,53 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskMigrateRequest struct to migrate compute's disk to target disk +type DiskMigrateRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID source disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID target disk + // Required: true + TargetDiskID uint64 `url:"targetDiskId" json:"targetDiskId" validate:"required"` + + // Migration mode. 1 - Data migration and domain update were already completed by third-party software. + // Use this if target disk already connected to compute and you only need to save changes for next reboot. + // Required: false + Mode int64 `url:"mode,omitempty" json:"mode,omitempty"` +} + +// DiskMigrate - migrate compute's disk to target disk. Source disk will be detached, target disk will be attached to the same PCI slot. +// (WARNING) Current realisation is limited. No actual data migration will be performed. +// Use this API if target disk already connected to compute and you only need to save changes for next reboot (mode: 1). +func (c Compute) DiskMigrate(ctx context.Context, req DiskMigrateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/diskMigrate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disk_qos.go b/pkg/cloudbroker/compute/disk_qos.go new file mode 100644 index 0000000..8004563 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_qos.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskQOSRequest struct to change QOS of the disk +type DiskQOSRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to apply limits + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Limit IO for a certain disk total and read/write options are not allowed to be combined + // Required: true + Limits string `url:"limits" json:"limits" validate:"required"` +} + +// DiskQOS changes QOS of the disk +func (c Compute) DiskQOS(ctx context.Context, req DiskQOSRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/diskQos" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disk_resize.go b/pkg/cloudbroker/compute/disk_resize.go new file mode 100644 index 0000000..80fdd03 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_resize.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskResizeRequest struct to change disk size +type DiskResizeRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to resize + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // New disk size + // Required: true + Size uint64 `url:"size" json:"size" validate:"required"` +} + +// DiskResize changes disk size +func (c Compute) DiskResize(ctx context.Context, req DiskResizeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/diskResize" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/disk_switch_to_replication.go b/pkg/cloudbroker/compute/disk_switch_to_replication.go new file mode 100644 index 0000000..6b37018 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_switch_to_replication.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskSwitchToReplicationRequest struct to switch disk to it's replication +type DiskSwitchToReplicationRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk to switch + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Delete replication relationship + // Required: false + StopReplication bool `url:"stopReplication" json:"stopReplication"` +} + +// DiskSwitchToReplication switches disk to it's replication +func (c Compute) DiskSwitchToReplication(ctx context.Context, req DiskSwitchToReplicationRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/diskSwitchToReplication" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/enable.go b/pkg/cloudbroker/compute/enable.go new file mode 100644 index 0000000..30951a4 --- /dev/null +++ b/pkg/cloudbroker/compute/enable.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable compute +type EnableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Enable enables compute +func (c Compute) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/enable" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/filter.go b/pkg/cloudbroker/compute/filter.go new file mode 100644 index 0000000..7f86b0f --- /dev/null +++ b/pkg/cloudbroker/compute/filter.go @@ -0,0 +1,170 @@ +package compute + +import ( + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/k8s" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/lb" +) + +// FilterByID returns ListComputes with specified ID. +func (lc ListComputes) FilterByID(id uint64) ListComputes { + predicate := func(ic ItemCompute) bool { + return ic.ID == id + } + + return lc.FilterFunc(predicate) +} + +// FilterByName returns ListComputes with specified Name. +func (lc ListComputes) FilterByName(name string) ListComputes { + predicate := func(ic ItemCompute) bool { + return ic.Name == name + } + + return lc.FilterFunc(predicate) +} + +// FilterByStatus returns ListComputes with specified Status. +func (lc ListComputes) FilterByStatus(status string) ListComputes { + predicate := func(ic ItemCompute) bool { + return ic.Status == status + } + + return lc.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListComputes with specified TechStatus. +func (lc ListComputes) FilterByTechStatus(techStatus string) ListComputes { + predicate := func(ic ItemCompute) bool { + return ic.TechStatus == techStatus + } + + return lc.FilterFunc(predicate) +} + +// FilterByDiskID return ListComputes with specified DiskID. +func (lc ListComputes) FilterByDiskID(diskID uint64) ListComputes { + predicate := func(ic ItemCompute) bool { + for _, disk := range ic.Disks { + if disk.ID == diskID { + return true + } + } + return false + } + + return lc.FilterFunc(predicate) +} + +// FilterByK8SID returns master and worker nodes (ListComputes) inside specified K8S cluster. +func (lc ListComputes) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListComputes, error) { + caller := k8s.New(decortClient) + + req := k8s.GetRequest{ + K8SID: k8sID, + } + + cluster, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + predicate := func(ic ItemCompute) bool { + for _, info := range cluster.K8SGroups.Masters.DetailedInfo { + if info.ID == ic.ID { + return true + } + } + + for _, worker := range cluster.K8SGroups.Workers { + for _, info := range worker.DetailedInfo { + if info.ID == ic.ID { + return true + } + } + } + + return false + } + + result := lc.FilterFunc(predicate) + + return &result, nil +} + +// K8SMasters is used to filter master nodes. Best used after FilterByK8SID function. +func (lc ListComputes) FilterByK8SMasters() ListComputes { + predicate := func(ic ItemCompute) bool { + for _, rule := range ic.AntiAffinityRules { + if rule.Value == "master" { + return true + } + } + return false + } + + return lc.FilterFunc(predicate) +} + +// K8SMasters is used to filter worker nodes. Best used after FilterByK8SID function. +func (lc ListComputes) FilterByK8SWorkers() ListComputes { + predicate := func(ic ItemCompute) bool { + for _, rule := range ic.AntiAffinityRules { + if rule.Value == "worker" { + return true + } + } + return false + } + + return lc.FilterFunc(predicate) +} + +// FilterByLBID is used to filter ListComputes used by specified Load Balancer. +func (lc ListComputes) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (*ListComputes, error) { + caller := lb.New(decortClient) + + req := lb.GetRequest{ + LBID: lbID, + } + + foundLB, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + predicate := func(ic ItemCompute) bool { + return ic.ID == foundLB.PrimaryNode.ComputeID || ic.ID == foundLB.SecondaryNode.ComputeID + } + + result := lc.FilterFunc(predicate) + + return &result, nil +} + +// FilterFunc allows filtering ListComputes based on a user-specified predicate. +func (lc ListComputes) FilterFunc(predicate func(ItemCompute) bool) ListComputes { + var result ListComputes + + for _, item := range lc.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemCompute +// If none was found, returns an empty struct. +func (lc ListComputes) FindOne() ItemCompute { + if len(lc.Data) == 0 { + return ItemCompute{} + } + + return lc.Data[0] +} diff --git a/pkg/cloudbroker/compute/filter_test.go b/pkg/cloudbroker/compute/filter_test.go new file mode 100644 index 0000000..ce76bd3 --- /dev/null +++ b/pkg/cloudbroker/compute/filter_test.go @@ -0,0 +1,250 @@ +package compute + +import ( + "testing" +) + +var computes = ListComputes{ + Data: []ItemCompute{ + { + Disks: []InfoDisk{ + { + ID: 65191, + PCISlot: 6, + }, + }, + InfoCompute: InfoCompute{ + ACL: []interface{}{}, + AccountID: 132847, + AccountName: "std_2", + AffinityLabel: "", + AffinityRules: []ItemRule{ + { + GUID: "", + Key: "aff_key", + Mode: "ANY", + Policy: "RECOMMENDED", + Topology: "compute", + Value: "aff_val", + }, + }, + AffinityWeight: 0, + AntiAffinityRules: []ItemRule{ + { + GUID: "", + Key: "antiaff_key", + Mode: "ANY", + Policy: "RECOMMENDED", + Topology: "compute", + Value: "antiaff_val", + }, + }, + Arch: "X86_64", + BootOrder: []string{ + "hd", "cdrom", + }, + BootDiskSize: 0, + CloneReference: 0, + Clones: []uint64{}, + ComputeCIID: 0, + CPUs: 4, + CreatedBy: "timofey_tkachev_1@decs3o", + CreatedTime: 1676975175, + CustomFields: map[string]interface{}{}, + DeletedBy: "", + DeletedTime: 0, + Description: "", + Devices: nil, + Driver: "KVM_X86", + GID: 212, + GUID: 48500, + ID: 48500, + ImageID: 9884, + Interfaces: ListInterfaces{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363500, + Name: "test", + Pinned: false, + RAM: 4096, + ReferenceID: "c7cb19ac-af4a-4067-852f-c5572949207e", + Registered: true, + ResName: "compute-48500", + RGID: 79724, + RGName: "std_broker2", + SnapSets: ListSnapshots{}, + StatelessSEPID: 0, + StatelessSEPType: "", + Status: "ENABLED", + Tags: map[string]interface{}{}, + TechStatus: "STOPPED", + TotalDiskSize: 2, + UpdatedBy: "", + UpdatedTime: 1677058904, + UserManaged: true, + VGPUs: []uint64{}, + VINSConnected: 0, + VirtualImageID: 0, + }, + }, + { + Disks: []InfoDisk{ + { + ID: 65248, + PCISlot: 6, + }, + }, + InfoCompute: InfoCompute{ + ACL: []interface{}{}, + AccountID: 132848, + AccountName: "std_broker", + AffinityLabel: "", + AffinityRules: []ItemRule{}, + AffinityWeight: 0, + AntiAffinityRules: []ItemRule{}, + Arch: "X86_64", + BootOrder: []string{ + "hd", "cdrom", + }, + BootDiskSize: 0, + CloneReference: 0, + Clones: []uint64{}, + ComputeCIID: 0, + CPUs: 6, + CreatedBy: "timofey_tkachev_1@decs3o", + CreatedTime: 1677579436, + CustomFields: map[string]interface{}{}, + DeletedBy: "", + DeletedTime: 0, + Description: "", + Devices: nil, + Driver: "KVM_X86", + GID: 212, + GUID: 48556, + ID: 48556, + ImageID: 9884, + Interfaces: ListInterfaces{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363853, + Name: "compute_2", + Pinned: false, + RAM: 4096, + ReferenceID: "a542c449-5b1c-4f90-88c5-7bb5f8ae68ff", + Registered: true, + ResName: "compute-48556", + RGID: 79727, + RGName: "sdk_negative_fields_test", + SnapSets: ListSnapshots{}, + StatelessSEPID: 0, + StatelessSEPType: "", + Status: "ENABLED", + Tags: map[string]interface{}{}, + TechStatus: "STARTED", + TotalDiskSize: 1, + UpdatedBy: "", + UpdatedTime: 1677579436, + UserManaged: true, + VGPUs: []uint64{}, + VINSConnected: 0, + VirtualImageID: 0, + }, + }, + }, + EntryCount: 2, +} + +func TestFilterByID(t *testing.T) { + actual := computes.FilterByID(48500).FindOne() + + if actual.ID != 48500 { + t.Fatal("expected ID 48500, found: ", actual.ID) + } + + actualEmpty := computes.FilterByID(0) + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByName(t *testing.T) { + actual := computes.FilterByName("compute_2").FindOne() + + if actual.Name != "compute_2" { + t.Fatal("expected compute with name 'test', found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := computes.FilterByStatus("ENABLED") + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected ENABLED status, found: ", item.Status) + } + } +} + +func TestFilterByTechStatus(t *testing.T) { + actual := computes.FilterByTechStatus("STARTED").FindOne() + + if actual.ID != 48556 { + t.Fatal("expected 48556 with STARTED techStatus, found: ", actual.ID) + } +} + +func TestFilterByDiskID(t *testing.T) { + actual := computes.FilterByDiskID(65248).FindOne() + + if actual.ID != 48556 { + t.Fatal("expected 48556 with DiskID 65248, found: ", actual.ID) + } +} + +func TestFilterFunc(t *testing.T) { + actual := computes.FilterFunc(func(ic ItemCompute) bool { + return ic.Registered == true + }) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 elements found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Registered != true { + t.Fatal("expected Registered to be true, actual: ", item.Registered) + } + } +} + +func TestSortingByCreatedTime(t *testing.T) { + actual := computes.SortByCreatedTime(false) + + if actual.Data[0].Name != "test" { + t.Fatal("expected 'test', found: ", actual.Data[0].Name) + } + + actual = computes.SortByCreatedTime(true) + if actual.Data[0].Name != "compute_2" { + t.Fatal("expected 'compute_2', found: ", actual.Data[0].Name) + } +} + +func TestSortingByCPU(t *testing.T) { + actual := computes.SortByCPU(false) + + if actual.Data[0].CPUs != 4 { + t.Fatal("expected 4 CPU cores, found: ", actual.Data[0].CPUs) + } + + actual = computes.SortByCPU(true) + + if actual.Data[0].CPUs != 6 { + t.Fatal("expected 6 CPU cores, found: ", actual.Data[0].CPUs) + } +} diff --git a/pkg/cloudbroker/compute/get.go b/pkg/cloudbroker/compute/get.go new file mode 100644 index 0000000..c44a7a7 --- /dev/null +++ b/pkg/cloudbroker/compute/get.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest to get information about compute +type GetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Get gets information about compute as a RecordCompute struct +func (c Compute) Get(ctx context.Context, req GetRequest) (*RecordCompute, error) { + res, err := c.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordCompute{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (c Compute) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/get" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/compute/get_audits.go b/pkg/cloudbroker/compute/get_audits.go new file mode 100644 index 0000000..df3b72f --- /dev/null +++ b/pkg/cloudbroker/compute/get_audits.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetAuditsRequest struct to get compute audits +type GetAuditsRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// GetAudits gets compute audits +func (c Compute) GetAudits(ctx context.Context, req GetAuditsRequest) (ListAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/getAudits" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/compute/get_console_url.go b/pkg/cloudbroker/compute/get_console_url.go new file mode 100644 index 0000000..8edf930 --- /dev/null +++ b/pkg/cloudbroker/compute/get_console_url.go @@ -0,0 +1,37 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetConsoleURLRequest struct to get console URL +type GetConsoleURLRequest struct { + // ID of compute instance to get console for + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// GetConsoleURL gets computes console URL +func (c Compute) GetConsoleURL(ctx context.Context, req GetConsoleURLRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/getConsoleUrl" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + result = strings.ReplaceAll(result, "\\", "") + + return result, nil +} diff --git a/pkg/cloudbroker/compute/get_custom_fields.go b/pkg/cloudbroker/compute/get_custom_fields.go new file mode 100644 index 0000000..dd948c9 --- /dev/null +++ b/pkg/cloudbroker/compute/get_custom_fields.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetCustomFieldsRequest struct to get Compute's customFields +type GetCustomFieldsRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// GetCustomFields gets Compute's customFields +func (c Compute) GetCustomFields(ctx context.Context, req GetCustomFieldsRequest) (interface{}, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/getCustomFields" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + var info interface{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/compute/get_log.go b/pkg/cloudbroker/compute/get_log.go new file mode 100644 index 0000000..f169da7 --- /dev/null +++ b/pkg/cloudbroker/compute/get_log.go @@ -0,0 +1,36 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetLogRequest struct to get compute logs +type GetLogRequest struct { + // ID of compute instance to get log for + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Path to log file + // Required: true + Path string `url:"path" json:"path" validate:"required"` +} + +// GetLog gets compute's log file by path +func (c Compute) GetLog(ctx context.Context, req GetLogRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/getLog" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/ids.go b/pkg/cloudbroker/compute/ids.go new file mode 100644 index 0000000..3a6b978 --- /dev/null +++ b/pkg/cloudbroker/compute/ids.go @@ -0,0 +1,46 @@ +package compute + +// IDs gets array of ComputeIDs from ListComputes struct +func (lc ListComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lc.Data)) + for _, c := range lc.Data { + res = append(res, c.ID) + } + return res +} + +// IDs gets array of DiskIDs from ListInfoDisks struct +func (lid ListInfoDisks) IDs() []uint64 { + res := make([]uint64, 0, len(lid)) + for _, d := range lid { + res = append(res, d.ID) + } + return res +} + +// IDs gets array of PFWsIDs from ListPFW struct +func (lp ListPFW) IDs() []uint64 { + res := make([]uint64, 0, len(lp.Data)) + for _, p := range lp.Data { + res = append(res, p.ID) + } + return res +} + +// IDs gets array of DiskIDs from ListDisks struct +func (ld ListDisks) IDs() []uint64 { + res := make([]uint64, 0, len(ld)) + for _, d := range ld { + res = append(res, d.ID) + } + return res +} + +// IDs gets array of PCIDeviceIDs from ListPCIDevices struct +func (lpd ListPCIDevices) IDs() []uint64 { + res := make([]uint64, 0, len(lpd.Data)) + for _, pd := range lpd.Data { + res = append(res, pd.ID) + } + return res +} diff --git a/pkg/cloudbroker/compute/list.go b/pkg/cloudbroker/compute/list.go new file mode 100644 index 0000000..8424457 --- /dev/null +++ b/pkg/cloudbroker/compute/list.go @@ -0,0 +1,108 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of available computes +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group name + // Required: false + RGName string `url:"rgName,omitempty" json:"rgName,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by IP address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by stack ID + // Required: false + StackID uint64 `url:"stackId,omitempty" json:"stackId,omitempty"` + + // Find by image ID + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` + + // Find by external network name + // Required: false + ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"` + + // Find by external network ID + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // Include deleted computes + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of the available computes as a ListComputes struct. +// Filtering based on status is possible +func (c Compute) List(ctx context.Context, req ListRequest) (*ListComputes, error) { + + res, err := c.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of the available computes as an array of bytes +func (c Compute) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/list" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/compute/list_deleted.go b/pkg/cloudbroker/compute/list_deleted.go new file mode 100644 index 0000000..7c7e757 --- /dev/null +++ b/pkg/cloudbroker/compute/list_deleted.go @@ -0,0 +1,84 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get deleted computes list +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group name + // Required: false + RGName string `url:"rgName,omitempty" json:"rgName,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by IP address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by external network name + // Required: false + ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"` + + // Find by external network ID + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list all deleted computes +func (c Compute) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListComputes, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/listDeleted" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/compute/list_pci_device.go b/pkg/cloudbroker/compute/list_pci_device.go new file mode 100644 index 0000000..af8afd3 --- /dev/null +++ b/pkg/cloudbroker/compute/list_pci_device.go @@ -0,0 +1,69 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListPCIDeviceRequest struct to get list of PCI devices +type ListPCIDeviceRequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by device id + // Required: false + DevID uint64 `url:"devId,omitempty" json:"devId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListPCIDevice gets list of PCI device +func (c Compute) ListPCIDevice(ctx context.Context, req ListPCIDeviceRequest) (*ListPCIDevices, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/listPciDevice" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListPCIDevices{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/compute/list_vgpu.go b/pkg/cloudbroker/compute/list_vgpu.go new file mode 100644 index 0000000..e39f605 --- /dev/null +++ b/pkg/cloudbroker/compute/list_vgpu.go @@ -0,0 +1,69 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListVGPURequest struct to get list of GPU for compute +type ListVGPURequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Find by GPU id + // Required: false + GPUID uint64 `url:"gpuId,omitempty" json:"gpuId,omitempty"` + + // Find by type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Include deleted computes. If using field 'status', then includedeleted will be ignored + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` +} + +// ListVGPU gets list of GPU for compute +func (c Compute) ListVGPU(ctx context.Context, req ListVGPURequest) (*ListVGPUs, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/listVGpu" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVGPUs{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/compute/mass_delete.go b/pkg/cloudbroker/compute/mass_delete.go new file mode 100644 index 0000000..6b41f63 --- /dev/null +++ b/pkg/cloudbroker/compute/mass_delete.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassDeleteRequest struct to delete several computes +type MassDeleteRequest struct { + // IDs of compute instances to delete + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Delete computes permanently + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// MassDelete starts jobs to delete several computes +func (c Compute) MassDelete(ctx context.Context, req MassDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/massDelete" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/mass_reboot.go b/pkg/cloudbroker/compute/mass_reboot.go new file mode 100644 index 0000000..95d1c8b --- /dev/null +++ b/pkg/cloudbroker/compute/mass_reboot.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassRebootRequest struct to reboot several computes +type MassRebootRequest struct { + // IDs of compute instances to reboot + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` +} + +// MassReboot starts jobs to reboot several computes +func (c Compute) MassReboot(ctx context.Context, req MassRebootRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/massReboot" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/mass_repair_boot_fs.go b/pkg/cloudbroker/compute/mass_repair_boot_fs.go new file mode 100644 index 0000000..a028725 --- /dev/null +++ b/pkg/cloudbroker/compute/mass_repair_boot_fs.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassRepairBootFSRequest struct to repair boot disk filesystem on several computes +type MassRepairBootFSRequest struct { + // IDs of compute instances which boot file systems will be repaired + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` +} + +// MassRepairBootFS repairs boot disk filesystem on several computes +func (c Compute) MassRepairBootFS(ctx context.Context, req MassRepairBootFSRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/massRepairBootFs" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/mass_start.go b/pkg/cloudbroker/compute/mass_start.go new file mode 100644 index 0000000..d310184 --- /dev/null +++ b/pkg/cloudbroker/compute/mass_start.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassStartRequest struct to start several computes +type MassStartRequest struct { + // IDs of compute instances to start + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` +} + +// MassStart starts jobs to start several computes +func (c Compute) MassStart(ctx context.Context, req MassStartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/massStart" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/mass_stop.go b/pkg/cloudbroker/compute/mass_stop.go new file mode 100644 index 0000000..7fbedd3 --- /dev/null +++ b/pkg/cloudbroker/compute/mass_stop.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassStopRequest struct to stop several computes +type MassStopRequest struct { + // IDs of compute instances to stop + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Force stop compute + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// MassStop starts jobs to stop several computes +func (c Compute) MassStop(ctx context.Context, req MassStopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/massStop" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/migrate.go b/pkg/cloudbroker/compute/migrate.go new file mode 100644 index 0000000..a5649f9 --- /dev/null +++ b/pkg/cloudbroker/compute/migrate.go @@ -0,0 +1,47 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MigrateRequest struct to migrate compute +type MigrateRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Particular Stack ID to migrate this compute to + // Required: false + TargetStackID uint64 `url:"targetStackId,omitempty" json:"targetStackId,omitempty"` + + // If live migration fails, destroy compute + // on source node and recreate on the target + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Migrate migrates compute to another stack +func (c Compute) Migrate(ctx context.Context, req MigrateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/migrate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/migrate_storage.go b/pkg/cloudbroker/compute/migrate_storage.go new file mode 100644 index 0000000..fd05bba --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_storage.go @@ -0,0 +1,54 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MigrateStorageRequest struct for migration +type MigrateStorageRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // SEP ID to migrate disks + // Required: true + SEPID uint64 `url:"sepId" json:"sepId" validate:"required"` + + // SEP pool name to migrate disks + // Required: true + PoolName string `url:"poolName" json:"poolName" validate:"required"` + + // Target stack ID + // Required: true + StackID uint64 `url:"stackId" json:"stackId" validate:"required"` + + // Async API call + // Required: true + Sync bool `url:"sync" json:"sync" validate:"required"` +} + +// MigrateStorage gets complex compute migration +// Compute will be migrated to specified stack, and compute disks will +// be migrated to specified SEP to specified pool. +// This action can take up to 84 hours +func (c Compute) MigrateStorage(ctx context.Context, req MigrateStorageRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/migrateStorage" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/compute/migrate_storage_abort.go b/pkg/cloudbroker/compute/migrate_storage_abort.go new file mode 100644 index 0000000..19394d8 --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_storage_abort.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MigrateStorageAbortRequest struct to abort migration +type MigrateStorageAbortRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Status check + // Default: false + // Required: false + StatusCheck bool `url:"statusCheck" json:"statusCheck"` +} + +// MigrateStorageAbort aborts complex compute migration job +func (c Compute) MigrateStorageAbort(ctx context.Context, req MigrateStorageAbortRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/migrateStorageAbort" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/compute/migrate_storage_clean_up.go b/pkg/cloudbroker/compute/migrate_storage_clean_up.go new file mode 100644 index 0000000..9e104fc --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_storage_clean_up.go @@ -0,0 +1,37 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MigrateStorageCleanUpRequest struct to cleanup resources after finished migration +type MigrateStorageCleanUpRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// MigrateStorageCleanUp cleanup resources after finished (success of failed) complex compute migration. +// If the migration was successful, then old disks will be removed, else new (target) disks will be removed. +// Do it wisely! +func (c Compute) MigrateStorageCleanUp(ctx context.Context, req MigrateStorageCleanUpRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/migrateStorageCleanup" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/compute/migrate_storage_info.go b/pkg/cloudbroker/compute/migrate_storage_info.go new file mode 100644 index 0000000..79901fa --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_storage_info.go @@ -0,0 +1,35 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MigrateStorageInfoRequest struct to get info about migration +type MigrateStorageInfoRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// MigrateStorageInfo gets info about last (include ongoing) storage migration +func (c Compute) MigrateStorageInfo(ctx context.Context, req MigrateStorageInfoRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/migrateStorageInfo" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/compute/models.go b/pkg/cloudbroker/compute/models.go new file mode 100644 index 0000000..9457cb3 --- /dev/null +++ b/pkg/cloudbroker/compute/models.go @@ -0,0 +1,1211 @@ +package compute + +// Access Control List +type RecordACL struct { + // Account ACL list + AccountACL ListACL `json:"accountACL"` + + // Compute ACL list + ComputeACL []ItemComputeACL `json:"computeACL"` + + // Resource group ACL list + RGACL ListACL `json:"rgACL"` +} + +type ListUsers struct { + // Data + Data RecordACL `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// ACL information +type ItemACL struct { + // Explicit + Explicit bool `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Right + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// ACL compute information +type ItemComputeACL struct { + // Explicit + Explicit string `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Right + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// List ACL +type ListACL []ItemACL + +// Main information about snapshot +type ItemSnapshot struct { + // List of disk IDs + Disks []uint64 `json:"disks"` + + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Timestamp + Timestamp uint64 `json:"timestamp"` +} + +// List of snapshots +type ListSnapshots []ItemSnapshot + +// List of snapshots +type ListSnapShot struct { + // Data + Data []ItemSnapshot `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about snapshot usage +type ItemSnapshotUsage struct { + // Count + Count uint64 `json:"count,omitempty"` + + // Stored + Stored float64 `json:"stored"` + + // Label + Label string `json:"label,omitempty"` + + // Timestamp + Timestamp uint64 `json:"timestamp,omitempty"` +} + +// List of snapshot usage +type ListSnapshotUsage []ItemSnapshotUsage + +// QOS +type QOS struct { + // ERate + ERate uint64 `json:"eRate"` + + // GUID + GUID string `json:"guid"` + + // InBurst + InBurst uint64 `json:"inBurst"` + + // InRate + InRate uint64 `json:"inRate"` +} + +// Main information about attached network +type RecordNetAttach struct { + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Default GW + DefGW string `json:"defGw"` + + // Enabled + Enabled bool `json:"enabled"` + + // FLIPGroup ID + FLIPGroupID uint64 `json:"flipgroupId"` + + // GUID + GUID string `json:"guid"` + + // IP address + IPAddress string `json:"ipAddress"` + + // Listen SSH + ListenSSH bool `json:"listenSsh"` + + // MAC + MAC string `json:"mac"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network type + NetType string `json:"netType"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // QOS + QOS QOS `json:"qos"` + + // Target + Target string `json:"target"` + + // Type + Type string `json:"type"` + + // List VNF IDs + VNFs []uint64 `json:"vnfs"` +} + +// Main information about affinity relations +type RecordAffinityRelations struct { + // Other node + OtherNode []interface{} `json:"otherNode"` + + // Other node indirect + OtherNodeIndirect []interface{} `json:"otherNodeIndirect"` + + // Other node indirect soft + OtherNodeIndirectSoft []interface{} `json:"otherNodeIndirectSoft"` + + // Other node soft + OtherNodeSoft []interface{} `json:"otherNodeSoft"` + + // Same node + SameNode []interface{} `json:"sameNode"` + + // Same node soft + SameNodeSoft []interface{} `json:"sameNodeSoft"` +} + +// Detailed information about audit +type ItemDetailedAudit struct { + // Call + Call string `json:"call"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List of detailed audit +type ListDetailedAudits []ItemDetailedAudit + +// Main information about port forward +type ItemPFW struct { + // ID + ID uint64 `json:"id"` + + // Local IP + LocalIP string `json:"localIp"` + + // Local port + LocalPort uint64 `json:"localPort"` + + // Protocol + Protocol string `json:"protocol"` + + // Public port end + PublicPortEnd uint64 `json:"publicPortEnd"` + + // Public port start + PublicPortStart uint64 `json:"publicPortStart"` + + // Virtuel machine ID + VMID uint64 `json:"vmId"` +} + +// List port forwards +type ListPFW struct { + // Data + Data []ItemPFW `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about rule +type ItemRule struct { + // GUID + GUID string `json:"guid"` + + // Key + Key string `json:"key"` + + // Mode + Mode string `json:"mode"` + + // Policy + Policy string `json:"policy"` + + // Topology + Topology string `json:"topology"` + + // Value + Value string `json:"value"` +} + +// List rules +type ListRules []ItemRule + +// Main information about IO tune +type IOTune struct { + // ReadBytesSec + ReadBytesSec uint64 `json:"read_bytes_sec"` + + // ReadBytesSecMax + ReadBytesSecMax uint64 `json:"read_bytes_sec_max"` + + // ReadIOPSSec + ReadIOPSSec uint64 `json:"read_iops_sec"` + + // ReadIOPSSecMax + ReadIOPSSecMax uint64 `json:"read_iops_sec_max"` + + // SizeIOPSSec + SizeIOPSSec uint64 `json:"size_iops_sec"` + + // TotalBytesSec + TotalBytesSec uint64 `json:"total_bytes_sec"` + + // TotalBytesSecMax + TotalBytesSecMax uint64 `json:"total_bytes_sec_max"` + + // TotalIOPSSec + TotalIOPSSec uint64 `json:"total_iops_sec"` + + // TotalIOPSSecMax + TotalIOPSSecMax uint64 `json:"total_iops_sec_max"` + + // WriteBytesSec + WriteBytesSec uint64 `json:"write_bytes_sec"` + + // WriteBytesSecMax + WriteBytesSecMax uint64 `json:"write_bytes_sec_max"` + + // WriteIOPSSec + WriteIOPSSec uint64 `json:"write_iops_sec"` + + // WriteIOPSSecMax + WriteIOPSSecMax uint64 `json:"write_iops_sec_max"` +} + +// Detailed information about snapshot +type ItemSnapshotDetailed struct { + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Resource ID + ResID string `json:"resId"` + + // SnapSetGUID + SnapSetGUID string `json:"snapSetGuid"` + + // SnapSetTime + SnapSetTime uint64 `json:"snapSetTime"` + + // TimeStamp + TimeStamp uint64 `json:"timestamp"` +} + +// List detailed snapshots +type ListDetailedSnapshots []ItemSnapshotDetailed + +// Main information about disk +type ItemDisk struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Access Control List + ACL ItemACL `json:"acl"` + + // Boot partition + BootPartition uint64 `json:"bootPartition"` + + // Bus number + BusNumber uint64 `json:"bus_number"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Destruction time + DestructionTime uint64 `json:"destructionTime"` + + // Disk path + DiskPath string `json:"diskPath"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // List image IDs + Images []uint64 `json:"images"` + + // IOTune + IOTune IOTune `json:"iotune"` + + // IQN + IQN string `json:"iqn"` + + // Login + Login string `json:"login"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Order + Order uint64 `json:"order"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // Password + Password string `json:"passwd"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Purge attempts + PurgeAttempts uint64 `json:"purgeAttempts"` + + // Purge time + PurgeTime uint64 `json:"purgeTime"` + + // Reality device number + RealityDeviceNumber uint64 `json:"realityDeviceNumber"` + + // Replication + Replication ItemReplication `json:"replication"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Resource ID + ResID string `json:"resId"` + + // Resource name + ResName string `json:"resName"` + + // Role + Role string `json:"role"` + + // SEP ID + SEPID uint64 `json:"sepId"` + + // Shareable + Shareable bool `json:"shareable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List detailed snapshots + Snapshots ListDetailedSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // Updated by + UpdatedBy uint64 `json:"updatedBy,omitempty"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` +} + +type ItemReplication struct { + // DiskID + DiskID uint64 `json:"diskId"` + + // PoolID + PoolID string `json:"poolId"` + + // Role + Role string `json:"role"` + + // SelfVolumeID + SelfVolumeID string `json:"selfVolumeId"` + + // StorageID + StorageID string `json:"storageId"` + + // VolumeID + VolumeID string `json:"volumeId"` +} + +// List disks +type ListDisks []ItemDisk + +// Main information about interface +type ItemInterface struct { + // Bus number + BusNumber uint64 `json:"bus_number"` + + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Default GW + DefGW string `json:"defGw"` + + // Enabled + Enabled bool `json:"enabled"` + + // FLIPGroup ID + FLIPGroupID uint64 `json:"flipgroupId"` + + // GUID + GUID string `json:"guid"` + + // IP address + IPAddress string `json:"ipAddress"` + + // Listen SSH or not + ListenSSH bool `json:"listenSsh"` + + // Maximum transmission unit + MTU uint64 `json:"mtu"` + + // Libvirt Settings + LibvirtSettings LibvirtSettings `json:"libvirtSettings"` + + // MAC + MAC string `json:"mac"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network type + NetType string `json:"netType"` + + // NodeID + NodeID int64 `json:"nodeId"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // QOS + QOS QOS `json:"qos"` + + // Target + Target string `json:"target"` + + // Type + Type string `json:"type"` + + // List VNF IDs + VNFs []uint64 `json:"vnfs"` +} + +// List interfaces +type ListInterfaces []ItemInterface + +// Main information about OS user +type ItemOSUser struct { + // GUID + GUID string `json:"guid"` + + // Login + Login string `json:"login"` + + // Password + Password string `json:"password"` + + // Public key + PubKey string `json:"pubkey"` +} + +// List OS users +type ListOSUsers []ItemOSUser + +// Information about compute +type InfoCompute struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control List + ACL []interface{} `json:"acl"` + + // Affinity label + AffinityLabel string `json:"affinityLabel"` + + // Affinity rules + AffinityRules ListRules `json:"affinityRules"` + + // Affinity weight + AffinityWeight uint64 `json:"affinityWeight"` + + // Anti affinity rules + AntiAffinityRules ListRules `json:"antiAffinityRules"` + + // Architecture + Arch string `json:"arch"` + + // Boot order + BootOrder []string `json:"bootOrder"` + + // Boot disk size + BootDiskSize uint64 `json:"bootdiskSize"` + + // CD Image Id + CdImageId uint64 `json:"cdImageId"` + + // Chipset + Chipset string `json:"chipset"` + + // Clone reference + CloneReference uint64 `json:"cloneReference"` + + // List clone IDs + Clones []uint64 `json:"clones"` + + // Compute CI ID + ComputeCIID uint64 `json:"computeciId"` + + // CPU Pin + CPUPin bool `json:"cpupin"` + + // Number of CPU + CPUs uint64 `json:"cpus"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Custom fields + CustomFields map[string]interface{} `json:"customFields"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Devices + Devices interface{} `json:"devices"` + + // Driver + Driver string `json:"driver"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // HPBacked + HPBacked bool `json:"hpBacked"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // List interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Manager ID + ManagerID uint64 `json:"managerId"` + + // Manager type + ManagerType string `json:"managerType"` + + // Migration job + MigrationJob uint64 `json:"migrationjob"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Need reboot + NeedReboot bool `json:"needReboot"` + + // Numa Affinity + NumaAffinity string `json:"numaAffinity"` + + //NumaNodeId + NumaNodeId int64 `json:"numaNodeId"` + + // List OS users + OSUsers ListOSUsers `json:"osUsers"` + + // Pinned + Pinned bool `json:"pinned"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Registered + Registered bool `json:"registered"` + + // Resource name + ResName string `json:"resName"` + + // Reserved Node Cpus + ReservedNodeCpus []uint64 `json:"reservedNodeCpus"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // SnapSets + SnapSets ListSnapshots `json:"snapSets"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // Stack name + StackName string `json:"stackName"` + + // Stateless SEP ID + StatelessSEPID int64 `json:"statelessSepId"` + + // Stateless SEP Type + StatelessSEPType string `json:"statelessSepType"` + + // Status + Status string `json:"status"` + + // Tags + Tags map[string]interface{} `json:"tags"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Total disk size + TotalDiskSize uint64 `json:"totalDisksSize"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed + UserManaged bool `json:"userManaged"` + + // Userdata + Userdata interface{} `json:"userdata"` + + // List VGPU IDs + VGPUs []uint64 `json:"vgpus"` + + // VINS connected + VINSConnected uint64 `json:"vinsConnected"` + + // Virtual image ID + VirtualImageID uint64 `json:"virtualImageId"` +} + +// Information about libvirt settings +type LibvirtSettings struct { + // TX mode + TXMode string `json:"txmode"` + + // IO event + IOEventFD string `json:"ioeventfd"` + + // Event ID + EventIDx string `json:"event_idx"` + + // Number of queues + Queues uint64 `json:"queues"` + + // RX queue size + RXQueueSize uint64 `json:"rx_queue_size"` + + // TX queue size + TXQueueSize uint64 `json:"tx_queue_size"` + + // GUID + GUID string `json:"guid"` +} + +// Detailed information about compute +type RecordCompute struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control List + ACL []interface{} `json:"acl"` + + // Affinity label + AffinityLabel string `json:"affinityLabel"` + + // Affinity rules + AffinityRules ListRules `json:"affinityRules"` + + // Affinity weight + AffinityWeight uint64 `json:"affinityWeight"` + + // Anti affinity rules + AntiAffinityRules ListRules `json:"antiAffinityRules"` + + // Architecture + Arch string `json:"arch"` + + // Boot order + BootOrder []string `json:"bootOrder"` + + // Boot disk size + BootDiskSize uint64 `json:"bootdiskSize"` + + // CD Image Id + CdImageId uint64 `json:"cdImageId"` + + // Chipset + Chipset string `json:"chipset"` + + // Clone reference + CloneReference uint64 `json:"cloneReference"` + + // List clone IDs + Clones []uint64 `json:"clones"` + + // Compute CI ID + ComputeCIID uint64 `json:"computeciId"` + + // CPU Pin + CPUPin bool `json:"cpupin"` + + // Number of CPU + CPUs uint64 `json:"cpus"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Custom fields + CustomFields map[string]interface{} `json:"customFields"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Devices + Devices interface{} `json:"devices"` + + // List disks + Disks ListDisks `json:"disks"` + + // Driver + Driver string `json:"driver"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // HPBacked + HPBacked bool `json:"hpBacked"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // ImageName + ImageName string `json:"imageName"` + + // List interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Manager ID + ManagerID uint64 `json:"managerId"` + + // Manager type + ManagerType string `json:"managerType"` + + // Migration job + MigrationJob uint64 `json:"migrationjob"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Node ID + NodeID uint64 `json:"nodeId"` + + // Natable VINS ID + NatableVINSID uint64 `json:"natableVinsId"` + + // Natable VINS IP + NatableVINSIP string `json:"natableVinsIp"` + + // Natable VINS Name + NatableVINSName string `json:"natableVinsName"` + + // Natable VINS network + NatableVINSNetwork string `json:"natableVinsNetwork"` + + // Natable VINS network name + NatableVINSNetworkName string `json:"natableVinsNetworkName"` + + // Need reboot + NeedReboot bool `json:"needReboot"` + + // NumaAffinity + NumaAffinity string `json:"numaAffinity"` + + //NumaNodeId + NumaNodeId int64 `json:"numaNodeId"` + + // List OS users + OSUsers ListOSUsers `json:"osUsers"` + + // Pinned + Pinned bool `json:"pinned"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Registered + Registered bool `json:"registered"` + + // Resource name + ResName string `json:"resName"` + + // Reserved Node Cpus + ReservedNodeCpus []uint64 `json:"reservedNodeCpus"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // SnapSets + SnapSets ListSnapshots `json:"snapSets"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // Stack name + StackName string `json:"stackName"` + + // Stateless SEP ID + StatelessSEPID int64 `json:"statelessSepId"` + + // Stateless SEP Type + StatelessSEPType string `json:"statelessSepType"` + + // Status + Status string `json:"status"` + + // Tags + Tags map[string]interface{} `json:"tags"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Total disk size + TotalDiskSize uint64 `json:"totalDisksSize"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed + UserManaged bool `json:"userManaged"` + + // Userdata + Userdata interface{} `json:"userdata"` + + // List VGPUs + VGPUs []ItemVGPU `json:"vgpus"` + + // Virtual image ID + VirtualImageID uint64 `json:"virtualImageId"` + + // VirtualImageName + VirtualImageName string `json:"virtualImageName"` +} + +// Information about of disk IDs +type ListInfoDisks []InfoDisk + +// Main information about compute for list +type ItemCompute struct { + // List of disk IDs + Disks ListInfoDisks `json:"disks"` + + // Main information about compute + InfoCompute + + // Total disk size + TotalDiskSize uint64 `json:"totalDisksSize"` + + // VINS connected + VINSConnected uint64 `json:"vinsConnected"` +} + +// Information Disk +type InfoDisk struct { + // Bus number + BusNumber uint64 `json:"bus_number"` + + // ID + ID uint64 `json:"id"` + + // PCISlot + PCISlot int64 `json:"pciSlot"` +} + +// List computes +type ListComputes struct { + // Data + Data []ItemCompute `json:"data"` + + // EntryCount + EntryCount uint64 `json:"entryCount"` +} + +// Short information about audit +type ItemAudit struct { + // Epoch + Epoch float64 `json:"epoch"` + + // Message + Message string `json:"message"` +} + +// List audits +type ListAudits []ItemAudit + +// Main information about PCI device +type ItemPCIDevice struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Description + Description string `json:"description"` + + // GUID + GUID uint64 `json:"guid"` + + // HwPath + HwPath string `json:"hwPath"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // Status + Status string `json:"status"` + + // System name + SystemName string `json:"systemName"` +} + +// List PCI devices +type ListPCIDevices struct { + // Data + Data []ItemPCIDevice `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// List VGPUs +type ListVGPUs struct { + // Data + Data []ItemVGPU `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about vgpu device +type ItemVGPU struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Created Time + CreatedTime uint64 `json:"createdTime"` + + // Deleted Time + DeletedTime uint64 `json:"deletedTime"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Last Claimed By + LastClaimedBy uint64 `json:"lastClaimedBy"` + + // Last Update Time + LastUpdateTime uint64 `json:"lastUpdateTime"` + + // Mode + Mode string `json:"mode"` + + // PCI Slot + PCISlot uint64 `json:"pciSlot"` + + // PGPUID + PGPUID uint64 `json:"pgpuid"` + + // Profile ID + ProfileID uint64 `json:"profileId"` + + // RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // RG ID + RGID uint64 `json:"rgId"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // VM ID + VMID uint64 `json:"vmid"` +} diff --git a/pkg/cloudbroker/compute/move_to_rg.go b/pkg/cloudbroker/compute/move_to_rg.go new file mode 100644 index 0000000..494d383 --- /dev/null +++ b/pkg/cloudbroker/compute/move_to_rg.go @@ -0,0 +1,57 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MoveToRGRequest struct to move compute to new resource group +type MoveToRGRequest struct { + // ID of the compute instance to move + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the target resource group + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // New name for the compute upon successful move, + // if name change required. + // Pass empty string if no name change necessary + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Should the compute be restarted upon successful move + // Required: false + Autostart bool `url:"autostart,omitempty" json:"autostart,omitempty"` + + // By default moving compute in a running state is not allowed. + // Set this flag to True to force stop running compute instance prior to move. + // Required: false + ForceStop bool `url:"forceStop,omitempty" json:"forceStop,omitempty"` +} + +// MoveToRG moves compute instance to new resource group +func (c Compute) Validate(ctx context.Context, req MoveToRGRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/moveToRg" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/net_attach.go b/pkg/cloudbroker/compute/net_attach.go new file mode 100644 index 0000000..4028df4 --- /dev/null +++ b/pkg/cloudbroker/compute/net_attach.go @@ -0,0 +1,62 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NetAttachRequest struct to attach network +type NetAttachRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Network type + // 'EXTNET' for connect to external network directly + // 'VINS' for connect to ViNS + // 'VFNIC' for connect to vfpool + // 'DPDK' for connect to DPDK + // Required: true + NetType string `url:"netType" json:"netType" validate:"computex86NetType"` + + // Network ID for connect to + // For EXTNET - external network ID + // For VINS - VINS ID + // Required: true + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // Directly required IP address for new network interface + // Required: true + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // Used only for DPDK type, must be 1-9216 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` +} + +// NetAttach attaches network to compute and gets info about network +func (c Compute) NetAttach(ctx context.Context, req NetAttachRequest) (*RecordNetAttach, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/netAttach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordNetAttach{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/compute/net_detach.go b/pkg/cloudbroker/compute/net_detach.go new file mode 100644 index 0000000..f118717 --- /dev/null +++ b/pkg/cloudbroker/compute/net_detach.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NetDetachRequest struct to detach network from compute +type NetDetachRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // IP of the network interface + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // MAC of the network interface + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` +} + +// NetDetach detaches network from compute +func (c Compute) NetDetach(ctx context.Context, req NetDetachRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/netDetach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/net_qos.go b/pkg/cloudbroker/compute/net_qos.go new file mode 100644 index 0000000..32f6b1d --- /dev/null +++ b/pkg/cloudbroker/compute/net_qos.go @@ -0,0 +1,61 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NetQOSRequest struct for update QOS +type NetQOSRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Network ID + // Required: true + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // Network type + // Should be one of: + // - VINS + // - EXTNET + // Required: true + NetType string `url:"netType" json:"netType" validate:"computeNetType"` + + // Internal traffic, kbit + // Required: false + IngressRate uint64 `url:"ingress_rate,omitempty" json:"ingress_rate,omitempty"` + + // Internal traffic burst, kbit + // Required: false + IngressBurst uint64 `url:"ingress_burst,omitempty" json:"ingress_burst,omitempty"` + + // External traffic rate, kbit + // Required: false + EgressRate uint64 `url:"egress_rate,omitempty" json:"egress_rate,omitempty"` +} + +// NetQOS updates compute interfaces QOS +func (c Compute) NetQOS(ctx context.Context, req NetQOSRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/netQos" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/pause.go b/pkg/cloudbroker/compute/pause.go new file mode 100644 index 0000000..a1bcb07 --- /dev/null +++ b/pkg/cloudbroker/compute/pause.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PauseRequest struct to pause compute +type PauseRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Pause pause compute +func (c Compute) Pause(ctx context.Context, req PauseRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/pause" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/pfw_add.go b/pkg/cloudbroker/compute/pfw_add.go new file mode 100644 index 0000000..16484e3 --- /dev/null +++ b/pkg/cloudbroker/compute/pfw_add.go @@ -0,0 +1,58 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PFWAddRequest struct to add port forward rule +type PFWAddRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // External start port number for the rule + // Required: true + PublicPortStart uint64 `url:"publicPortStart" json:"publicPortStart" validate:"required"` + + // End port number (inclusive) for the ranged rule + // Default value: -1 + // Required: false + PublicPortEnd int64 `url:"publicPortEnd,omitempty" json:"publicPortEnd,omitempty"` + + // Internal base port number + // Required: false + LocalBasePort uint64 `url:"localBasePort,omitempty" json:"localBasePort,omitempty"` + + // Network protocol + // Should be one of: + // - tcp + // - udp + // Required: true + Proto string `url:"proto" json:"proto" validate:"proto"` +} + +// PFWAdd adds port forward rule +func (c Compute) PFWAdd(ctx context.Context, req PFWAddRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/pfwAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/pfw_del.go b/pkg/cloudbroker/compute/pfw_del.go new file mode 100644 index 0000000..546135e --- /dev/null +++ b/pkg/cloudbroker/compute/pfw_del.go @@ -0,0 +1,61 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PFWDelRequest struct to delete port forward rule +type PFWDelRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the rule to delete. If specified, all other arguments will be ignored + // Required: false + RuleID uint64 `url:"ruleId,omitempty" json:"ruleId,omitempty"` + + // External start port number for the rule + // Required: false + PublicPortStart uint64 `url:"publicPortStart,omitempty" json:"publicPortStart,omitempty"` + + // End port number (inclusive) for the ranged rule + // Required: false + PublicPortEnd uint64 `url:"publicPortEnd,omitempty" json:"publicPortEnd,omitempty"` + + // Internal base port number + // Required: false + LocalBasePort uint64 `url:"localBasePort,omitempty" json:"localBasePort,omitempty"` + + // Network protocol + // Should be one of: + // - tcp + // - udp + // Required: false + Proto string `url:"proto,omitempty" json:"proto,omitempty" validate:"omitempty,proto"` +} + +// PFWDel deletes port forward rule +func (c Compute) PFWDel(ctx context.Context, req PFWDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/pfwDel" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/pfw_list.go b/pkg/cloudbroker/compute/pfw_list.go new file mode 100644 index 0000000..fbc7cf1 --- /dev/null +++ b/pkg/cloudbroker/compute/pfw_list.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PFWListRequest struct to get list of port forwards +type PFWListRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// PFWList gets compute port forwards list +func (c Compute) PFWList(ctx context.Context, req PFWListRequest) (*ListPFW, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/pfwList" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListPFW{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/compute/pin_to_stack.go b/pkg/cloudbroker/compute/pin_to_stack.go new file mode 100644 index 0000000..f41335d --- /dev/null +++ b/pkg/cloudbroker/compute/pin_to_stack.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PinToStackRequest struct to pin compute to stack +type PinToStackRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Stack ID to pin to + // Required: false + TargetStackID uint64 `url:"targetStackId" json:"targetStackId"` + + // Try to migrate or not if compute in running states + // Required: false + Force bool `url:"force" json:"force"` +} + +// PinToStack pins compute to current stack +func (c Compute) PinToStack(ctx context.Context, req PinToStackRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/pinToStack" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/power_cycle.go b/pkg/cloudbroker/compute/power_cycle.go new file mode 100644 index 0000000..ca1692a --- /dev/null +++ b/pkg/cloudbroker/compute/power_cycle.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PowerCycleRequest struct to force stop and start compute +type PowerCycleRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// PowerCycle makes force stop and start compute +func (c Compute) PowerCycle(ctx context.Context, req PowerCycleRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/powerCycle" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/raise_down.go b/pkg/cloudbroker/compute/raise_down.go new file mode 100644 index 0000000..53f1d78 --- /dev/null +++ b/pkg/cloudbroker/compute/raise_down.go @@ -0,0 +1,24 @@ +package compute + +import ( + "context" + "net/http" + "strconv" +) + +// RaiseDown starting all computes in "DOWN" tech status +func (c Compute) RaiseDown(ctx context.Context) (bool, error) { + url := "/cloudbroker/compute/raiseDown" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/reboot.go b/pkg/cloudbroker/compute/reboot.go new file mode 100644 index 0000000..549fbb6 --- /dev/null +++ b/pkg/cloudbroker/compute/reboot.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RebootRequest struct to reboot compute +type RebootRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Reboot reboots compute +func (c Compute) Reboot(ctx context.Context, req RebootRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/reboot" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/redeploy.go b/pkg/cloudbroker/compute/redeploy.go new file mode 100644 index 0000000..bc7a295 --- /dev/null +++ b/pkg/cloudbroker/compute/redeploy.go @@ -0,0 +1,62 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RedeployRequest struct for redeploy +type RedeployRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the new OS image, if image change is required + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` + + // New size for the boot disk in GB, if boot disk size change is required + // Required: false + DiskSize uint64 `url:"diskSize,omitempty" json:"diskSize,omitempty"` + + // How to handle data disks connected to this compute instance + // Should be one of: + // - KEEP + // - DETACH + // - DESTROY + // Required: false + DataDisks string `url:"dataDisks,omitempty" json:"dataDisks,omitempty" validate:"omitempty,computeDataDisks"` + + // Should the compute be restarted upon successful redeploy + // Required: false + AutoStart bool `url:"autoStart,omitempty" json:"autoStart,omitempty"` + + // Set this flag to True to force stop running compute instance and redeploy next + // Required: false + ForceStop bool `url:"forceStop,omitempty" json:"forceStop,omitempty"` +} + +// Redeploy redeploys compute +func (c Compute) Redeploy(ctx context.Context, req RedeployRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/redeploy" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/registration.go b/pkg/cloudbroker/compute/registration.go new file mode 100644 index 0000000..f51e3b7 --- /dev/null +++ b/pkg/cloudbroker/compute/registration.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RegistrationRequest struct to set compute registered in RT +type RegistrationRequest struct { + // ID of the Compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Unique compute registration key + // Required: true + RegistrationKey string `url:"registrationKey" json:"registrationKey" validate:"required"` +} + +// Registration sets compute registered in RT +func (c Compute) Registration(ctx context.Context, req RegistrationRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/registration" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/repair_boot_fs.go b/pkg/cloudbroker/compute/repair_boot_fs.go new file mode 100644 index 0000000..b61e0ce --- /dev/null +++ b/pkg/cloudbroker/compute/repair_boot_fs.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RepairBootFSRequest struct to repair filesystem +type RepairBootFSRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// RepairBootFS repairs compute boot disk filesystem +func (c Compute) RepairBootFS(ctx context.Context, req RepairBootFSRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/repairBootFs" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/reset.go b/pkg/cloudbroker/compute/reset.go new file mode 100644 index 0000000..968754f --- /dev/null +++ b/pkg/cloudbroker/compute/reset.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ResetRequest struct to reset compute +type ResetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Reset resets compute +func (c Compute) Reset(ctx context.Context, req ResetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/reset" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/resize.go b/pkg/cloudbroker/compute/resize.go new file mode 100644 index 0000000..32cd3cc --- /dev/null +++ b/pkg/cloudbroker/compute/resize.go @@ -0,0 +1,62 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ResizeRequest struct to resize compute +type ResizeRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // New CPU count. + // Pass 0 if no change to CPU count is required + // Required: false + CPU uint64 `url:"cpu,omitempty" json:"cpu,omitempty"` + + // New RAM volume in MB. + // Pass 0 if no change to RAM volume is required + // Required: false + RAM uint64 `url:"ram,omitempty" json:"ram,omitempty"` + + // Force compute resize + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// GetRAM returns RAM field values +func (r ResizeRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +// Resize resizes compute instance +func (c Compute) Resize(ctx context.Context, req ResizeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/resize" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/restore.go b/pkg/cloudbroker/compute/restore.go new file mode 100644 index 0000000..e978b3d --- /dev/null +++ b/pkg/cloudbroker/compute/restore.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore compute +type RestoreRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Restore restores compute from recycle bin +func (c Compute) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/restore" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/resume.go b/pkg/cloudbroker/compute/resume.go new file mode 100644 index 0000000..a7f873d --- /dev/null +++ b/pkg/cloudbroker/compute/resume.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ResumeRequest struct to resume compute +type ResumeRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// Resume resumes Compute from paused state +func (c Compute) Resume(ctx context.Context, req ResumeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/resume" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/serialize.go b/pkg/cloudbroker/compute/serialize.go new file mode 100644 index 0000000..88fba6c --- /dev/null +++ b/pkg/cloudbroker/compute/serialize.go @@ -0,0 +1,43 @@ +package compute + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lc ListComputes) Serialize(params ...string) (serialization.Serialized, error) { + if len(lc.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lc, prefix, indent) + } + + return json.Marshal(lc) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ic ItemCompute) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ic, prefix, indent) + } + + return json.Marshal(ic) +} diff --git a/pkg/cloudbroker/compute/set_custom_fields.go b/pkg/cloudbroker/compute/set_custom_fields.go new file mode 100644 index 0000000..b502757 --- /dev/null +++ b/pkg/cloudbroker/compute/set_custom_fields.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCustomFieldsRequest struct for setting customFields values for the Compute +type SetCustomFieldsRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Custom fields for Compute. Must be dict. + // Required: true + CustomFields string `url:"customFields" json:"customFields" validate:"required"` +} + +// SetCustomFields sets customFields values for the Compute +func (c Compute) SetCustomFields(ctx context.Context, req SetCustomFieldsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/setCustomFields" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/set_net_config.go b/pkg/cloudbroker/compute/set_net_config.go new file mode 100644 index 0000000..baf26b6 --- /dev/null +++ b/pkg/cloudbroker/compute/set_net_config.go @@ -0,0 +1,66 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetNetConfigRequest struct to Configure libvirt virtio interface parameters +type SetNetConfigRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Interface MAC-address + // Required: true + MAC string `url:"mac" json:"mac" validate:"required"` + + // TXMode, must be 'iothread', 'timer' or 'selected by hypervisor' + // Required: false + TXMode string `url:"txmode,omitempty" json:"txmode,omitempty" validate:"omitempty,interfaceTXModel"` + + // IOEventFD, must be 'on', 'off' or 'selected by hypervisor' + // Required: false + IOEventFD string `url:"ioeventfd,omitempty" json:"ioeventfd,omitempty" validate:"omitempty,interfaceIOEventFD"` + + // EventIDx, must be 'on', 'off' or 'selected by hypervisor' + // Required: false + EventIDx string `url:"event_idx,omitempty" json:"event_idx,omitempty" validate:"omitempty,interfaceEventIDx"` + + // Number of queues + // Required: false + Queues uint64 `url:"queues,omitempty" json:"queues,omitempty"` + + // RX queue size + // Required: false + RXQueueSize uint64 `url:"rx_queue_size,omitempty" json:"rx_queue_size,omitempty"` + + // TX queue size + // Required: false + TXQueueSize uint64 `url:"tx_queue_size,omitempty" json:"tx_queue_size,omitempty"` +} + +// Configure libvirt virtio interface parameters +func (c Compute) SetNetConfig(ctx context.Context, req SetNetConfigRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/setNetConfig" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/snapshot_create.go b/pkg/cloudbroker/compute/snapshot_create.go new file mode 100644 index 0000000..dc1f479 --- /dev/null +++ b/pkg/cloudbroker/compute/snapshot_create.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotCreateRequest struct to create snapshot +type SnapshotCreateRequest struct { + // ID of the compute instance to create snapshot for + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Text label for snapshot. + // Must be unique among this compute snapshots + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotCreate creates compute snapshot +func (c Compute) SnapshotCreate(ctx context.Context, req SnapshotCreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/snapshotCreate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/compute/snapshot_delete.go b/pkg/cloudbroker/compute/snapshot_delete.go new file mode 100644 index 0000000..260c653 --- /dev/null +++ b/pkg/cloudbroker/compute/snapshot_delete.go @@ -0,0 +1,69 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotDeleteRequest struct to delete snapshot +type SnapshotDeleteRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Text label of snapshot to delete + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +type wrapperSnapshotDeleteRequest struct { + SnapshotDeleteRequest + + AsyncMode bool `url:"asyncMode"` +} + +// SnapshotDelete deletes specified compute snapshot +func (c Compute) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSnapshotDeleteRequest{ + SnapshotDeleteRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/snapshotDelete" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} + +// SnapshotDeleteAsync deletes specified compute snapshot with AsyncMode +func (c Compute) SnapshotDeleteAsync(ctx context.Context, req SnapshotDeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSnapshotDeleteRequest{ + SnapshotDeleteRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/snapshotDelete" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/snapshot_evict_disk.go b/pkg/cloudbroker/compute/snapshot_evict_disk.go new file mode 100644 index 0000000..5a9ce17 --- /dev/null +++ b/pkg/cloudbroker/compute/snapshot_evict_disk.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotEvictDiskRequest struct to evict specified disk +type SnapshotEvictDiskRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the disk instance + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// SnapshotEvictDisk evicts specified disk from all snapshots of a compute instance +func (c Compute) SnapshotEvictDisk(ctx context.Context, req SnapshotEvictDiskRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/snapshotEvictDisk" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/snapshot_list.go b/pkg/cloudbroker/compute/snapshot_list.go new file mode 100644 index 0000000..07f2333 --- /dev/null +++ b/pkg/cloudbroker/compute/snapshot_list.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotListRequest struct to get list of snapshots +type SnapshotListRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// SnapshotList gets list of compute snapshots +func (c Compute) SnapshotList(ctx context.Context, req SnapshotListRequest) (*ListSnapShot, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/snapshotList" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListSnapShot{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/compute/snapshot_rollback.go b/pkg/cloudbroker/compute/snapshot_rollback.go new file mode 100644 index 0000000..608f6a9 --- /dev/null +++ b/pkg/cloudbroker/compute/snapshot_rollback.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotRollbackRequest struct for rollback +type SnapshotRollbackRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Text label of snapshot to rollback + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotRollback rollbacks specified compute snapshot +func (c Compute) SnapshotRollback(ctx context.Context, req SnapshotRollbackRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/snapshotRollback" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/snapshot_usage.go b/pkg/cloudbroker/compute/snapshot_usage.go new file mode 100644 index 0000000..fe6a7bb --- /dev/null +++ b/pkg/cloudbroker/compute/snapshot_usage.go @@ -0,0 +1,47 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotUsageRequest struct tto get compute snapshot real size on storage +type SnapshotUsageRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Specify to show usage exact for this snapshot. + // Leave empty for get usage for all compute snapshots + // Required: false + Label string `url:"label,omitempty" json:"label,omitempty"` +} + +// SnapshotUsage gets compute snapshot real size on storage. +// Always returns list of json objects, and first json object contains summary about all related +// snapshots. +func (c Compute) SnapshotUsage(ctx context.Context, req SnapshotUsageRequest) (ListSnapshotUsage, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/snapshotUsage" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListSnapshotUsage{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/compute/sorting.go b/pkg/cloudbroker/compute/sorting.go new file mode 100644 index 0000000..b244d55 --- /dev/null +++ b/pkg/cloudbroker/compute/sorting.go @@ -0,0 +1,98 @@ +package compute + +import "sort" + +// SortByCPU sorts ListComputes by the CPU core amount in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByCPU(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].CPUs > lc.Data[j].CPUs + } + + return lc.Data[i].CPUs < lc.Data[j].CPUs + }) + + return lc +} + +// SortByRAM sorts ListComputes by the RAM amount in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByRAM(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].RAM > lc.Data[j].RAM + } + + return lc.Data[i].RAM < lc.Data[j].RAM + }) + + return lc +} + +// SortByCreatedTime sorts ListComputes by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByCreatedTime(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].CreatedTime > lc.Data[j].CreatedTime + } + + return lc.Data[i].CreatedTime < lc.Data[j].CreatedTime + }) + + return lc +} + +// SortByUpdatedTime sorts ListComputes by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByUpdatedTime(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].UpdatedTime > lc.Data[j].UpdatedTime + } + + return lc.Data[i].UpdatedTime < lc.Data[j].UpdatedTime + }) + + return lc +} + +// SortByDeletedTime sorts ListComputes by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListComputes) SortByDeletedTime(inverse bool) ListComputes { + if len(lc.Data) < 2 { + return lc + } + + sort.Slice(lc.Data, func(i, j int) bool { + if inverse { + return lc.Data[i].DeletedTime > lc.Data[j].DeletedTime + } + + return lc.Data[i].DeletedTime < lc.Data[j].DeletedTime + }) + + return lc +} diff --git a/pkg/cloudbroker/compute/start.go b/pkg/cloudbroker/compute/start.go new file mode 100644 index 0000000..7478e7c --- /dev/null +++ b/pkg/cloudbroker/compute/start.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StartRequest struct to start compute +type StartRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of CD-ROM live image to boot + // Required: false + AltBootID uint64 `url:"altBootId,omitempty" json:"altBootId,omitempty"` + + // ID of stack to start compute + // Required: false + StackID uint64 `url:"stackId,omitempty" json:"stackId,omitempty"` +} + +// Start starts compute +func (c Compute) Start(ctx context.Context, req StartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/start" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/stop.go b/pkg/cloudbroker/compute/stop.go new file mode 100644 index 0000000..ce7d8f1 --- /dev/null +++ b/pkg/cloudbroker/compute/stop.go @@ -0,0 +1,47 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StopRequest struct to stop compute +type StopRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Force stop compute + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` + + // whether to depresent compute disks from node or not + // Default: true + // Required: false + Depresent bool `url:"depresent" json:"depresent"` +} + +// Stop stops compute +func (c Compute) Stop(ctx context.Context, req StopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/stop" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/tag_add.go b/pkg/cloudbroker/compute/tag_add.go new file mode 100644 index 0000000..0e6c50e --- /dev/null +++ b/pkg/cloudbroker/compute/tag_add.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// TagAddRequest struct to add tag to compute +type TagAddRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Tag key + // Required: true + Key string `url:"key" json:"key" validate:"required"` + + // Tag value + // Required: true + Value string `url:"value" json:"value" validate:"required"` +} + +// TagAdd adds tag to compute tags dict +func (c Compute) TagAdd(ctx context.Context, req TagAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/tagAdd" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/tag_remove.go b/pkg/cloudbroker/compute/tag_remove.go new file mode 100644 index 0000000..6ca0aac --- /dev/null +++ b/pkg/cloudbroker/compute/tag_remove.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// TagRemoveRequest struct to remove tag from compute +type TagRemoveRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"min=1"` + + // Tag key + // Required: true + Key string `url:"key" json:"key" validate:"required"` +} + +// TagRemove removes tag from compute tags dict +func (c Compute) TagRemove(ctx context.Context, req TagRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/tagRemove" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/unpin_from_stack.go b/pkg/cloudbroker/compute/unpin_from_stack.go new file mode 100644 index 0000000..6d290cc --- /dev/null +++ b/pkg/cloudbroker/compute/unpin_from_stack.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UnpinFromStackRequest struct to unpin from stack +type UnpinFromStackRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// UnpinFromStack unpins compute from current stack +func (c Compute) UnpinFromStack(ctx context.Context, req UnpinFromStackRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/unpinFromStack" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/update.go b/pkg/cloudbroker/compute/update.go new file mode 100644 index 0000000..7d456b3 --- /dev/null +++ b/pkg/cloudbroker/compute/update.go @@ -0,0 +1,68 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update compute +type UpdateRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // New name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Rule for VM placement with NUMA affinity. + // Possible values - none (placement without NUMA affinity), + // strict (strictly with NUMA affinity, if not possible - do not start VM), + // loose (use NUMA affinity if possible) + // Required: false + // Default: none + NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"` + + // Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node + // Required: false + // Default: false + CPUPin bool `url:"cpupin" json:"cpupin"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node + // Required: false + // Default: false + HPBacked bool `url:"hpBacked" json:"hpBacked"` +} + +// Update updates some properties of the compute +func (c Compute) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/update" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/user_grant.go b/pkg/cloudbroker/compute/user_grant.go new file mode 100644 index 0000000..cab8467 --- /dev/null +++ b/pkg/cloudbroker/compute/user_grant.go @@ -0,0 +1,50 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserGrantRequest struct to grant access to compute +type UserGrantRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the user to add + // Required: true + Username string `url:"userName" json:"userName" validate:"required"` + + // Access type + // Should be one of: + // - 'R' for Read only + // - 'RCX' for Write + // - 'ARCXDU' for Admin + // Required: true + AccessType string `url:"accesstype" json:"accesstype" validate:"accessType"` +} + +// UserGrant grants user access to the compute +func (c Compute) UserGrant(ctx context.Context, req UserGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/userGrant" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/user_list.go b/pkg/cloudbroker/compute/user_list.go new file mode 100644 index 0000000..0812b81 --- /dev/null +++ b/pkg/cloudbroker/compute/user_list.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserListRequest struct to get list of users for compute +type UserListRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// UserList gets users list for compute +func (c Compute) UserList(ctx context.Context, req UserListRequest) (*ListUsers, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/userList" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListUsers{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/compute/user_revoke.go b/pkg/cloudbroker/compute/user_revoke.go new file mode 100644 index 0000000..4268171 --- /dev/null +++ b/pkg/cloudbroker/compute/user_revoke.go @@ -0,0 +1,42 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserRevokeRequest struct to revoke user access +type UserRevokeRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the user to remove + // Required: true + Username string `url:"userName" json:"userName" validate:"required"` +} + +// UserRevoke revokes user access to the compute +func (c Compute) UserRevoke(ctx context.Context, req UserRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/userRevoke" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/user_update.go b/pkg/cloudbroker/compute/user_update.go new file mode 100644 index 0000000..0781066 --- /dev/null +++ b/pkg/cloudbroker/compute/user_update.go @@ -0,0 +1,50 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UserUpdateRequest struct to update user access +type UserUpdateRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Name of the user to update + // Required: true + Username string `url:"userName" json:"userName" validate:"required"` + + // Access type + // Should be one of: + // - 'R' for Read only + // - 'RCX' for Write + // - 'ARCXDU' for Admin + // Required: true + AccessType string `url:"accesstype" json:"accesstype" validate:"accessType"` +} + +// UserUpdate updates user access to the compute +func (c Compute) UserUpdate(ctx context.Context, req UserUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/userUpdate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks.go b/pkg/cloudbroker/disks.go new file mode 100644 index 0000000..dfe35f1 --- /dev/null +++ b/pkg/cloudbroker/disks.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/disks" +) + +// Accessing the Disks method group +func (cb *CloudBroker) Disks() *disks.Disks { + return disks.New(cb.client) +} diff --git a/pkg/cloudbroker/disks/create.go b/pkg/cloudbroker/disks/create.go new file mode 100644 index 0000000..5adcf57 --- /dev/null +++ b/pkg/cloudbroker/disks/create.go @@ -0,0 +1,77 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create disk +type CreateRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // ID of the grid (platform) + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Name of disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Description of disk + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Size in GB, default is 0 + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Type of disk + // - B=Boot + // - D=Data + // - T=Temp + // Required: true + Type string `url:"type" json:"type" validate:"diskType"` + + // Size in GB default is 0 + // Required: false + SSDSize uint64 `url:"ssdSize,omitempty" json:"ssdSize,omitempty"` + + // Max IOPS disk can perform defaults to 2000 + // Required: false + IOPS uint64 `url:"iops,omitempty" json:"iops,omitempty"` + + // Storage endpoint provider ID to create disk + // Required: false + SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"` + + // Pool name to create disk + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` +} + +// Create creates a disk +func (d Disks) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/create" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/delete.go b/pkg/cloudbroker/disks/delete.go new file mode 100644 index 0000000..b959fcb --- /dev/null +++ b/pkg/cloudbroker/disks/delete.go @@ -0,0 +1,46 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete disk +type DeleteRequest struct { + // ID of disk to delete + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Detach disk from machine first + // Required: false + Detach bool `url:"detach,omitempty" json:"detach,omitempty"` + + // Whether to completely delete the disk, works only with non attached disks + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes disk by ID +func (d Disks) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/delete" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/delete_disks.go b/pkg/cloudbroker/disks/delete_disks.go new file mode 100644 index 0000000..73d427d --- /dev/null +++ b/pkg/cloudbroker/disks/delete_disks.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteDisksRequest struct for multiple disks +type DeleteDisksRequest struct { + // List of disk ids to delete + // Required: true + DisksIDs []uint64 `url:"diskIds" json:"diskIds" validate:"min=1"` + + // Whether to completely delete the disks, works only with non attached disks + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// DeleteDisks deletes multiple disks permanently +func (d Disks) DeleteDisks(ctx context.Context, req DeleteDisksRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/deleteDisks" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/depresent.go b/pkg/cloudbroker/disks/depresent.go new file mode 100644 index 0000000..71b3692 --- /dev/null +++ b/pkg/cloudbroker/disks/depresent.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DepresentRequest struct to depresent disk from node +type DepresentRequest struct { + // ID of the disk to depresent + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID of the node to depresent disk from + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// Depresent depresents disk from node +func (d Disks) Depresent(ctx context.Context, req DepresentRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/depresent" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/disks.go b/pkg/cloudbroker/disks/disks.go new file mode 100644 index 0000000..cbb5173 --- /dev/null +++ b/pkg/cloudbroker/disks/disks.go @@ -0,0 +1,16 @@ +// API Actor for managing Disk. This actor is a final API for admin to manage Disk +package disks + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to disks +type Disks struct { + client interfaces.Caller +} + +// Builder for disks endpoints +func New(client interfaces.Caller) *Disks { + return &Disks{ + client: client, + } +} diff --git a/pkg/cloudbroker/disks/filter.go b/pkg/cloudbroker/disks/filter.go new file mode 100644 index 0000000..75eec81 --- /dev/null +++ b/pkg/cloudbroker/disks/filter.go @@ -0,0 +1,143 @@ +package disks + +import ( + "context" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/k8s" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/lb" +) + +// FilterByID returns ListDisks with specified ID. +func (ld ListDisks) FilterByID(id uint64) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.ID == id + } + + return ld.FilterFunc(predicate) +} + +// FilterByName returns ListDisks with specified Name. +func (ld ListDisks) FilterByName(name string) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.Name == name + } + + return ld.FilterFunc(predicate) +} + +// FilterByStatus returns ListDisks with specified Status. +func (ld ListDisks) FilterByStatus(status string) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.Status == status + } + + return ld.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListDisks with specified TechStatus. +func (ld ListDisks) FilterByTechStatus(techStatus string) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.TechStatus == techStatus + } + + return ld.FilterFunc(predicate) +} + +// FilterByImageID returns ListDisks with specified ImageID. +func (ld ListDisks) FilterByImageID(imageID uint64) ListDisks { + predicate := func(idisk ItemDisk) bool { + return idisk.ImageID == imageID + } + + return ld.FilterFunc(predicate) +} + +// FilterByComputeID is used to filter ListDisks attached to specified compute. +func (ld ListDisks) FilterByComputeID(computeID uint64) ListDisks { + predicate := func(idisk ItemDisk) bool { + for k := range idisk.Computes { + if k == strconv.FormatUint(computeID, 10) { + return true + } + } + + return false + } + + return ld.FilterFunc(predicate) +} + +// FilterByK8SID is used to filter ListDisks by specified K8S cluster. +func (ld ListDisks) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListDisks, error) { + caller := k8s.New(decortClient) + + req := k8s.GetRequest{ + K8SID: k8sID, + } + + cluster, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + var result ListDisks + + for _, masterCompute := range cluster.K8SGroups.Masters.DetailedInfo { + result.Data = append(result.Data, ld.FilterByComputeID(masterCompute.ID).Data...) + } + + for _, workerGroup := range cluster.K8SGroups.Workers { + for _, workerCompute := range workerGroup.DetailedInfo { + result.Data = append(result.Data, ld.FilterByComputeID(workerCompute.ID).Data...) + } + } + + return &result, nil +} + +// FilterByLBID is used to filter ListDisks used by computes inside specified Load Balancer. +func (ld ListDisks) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (*ListDisks, error) { + caller := lb.New(decortClient) + + req := lb.GetRequest{ + LBID: lbID, + } + + foundLB, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + var result ListDisks + result.Data = append(result.Data, ld.FilterByComputeID(foundLB.PrimaryNode.ComputeID).Data...) + result.Data = append(result.Data, ld.FilterByComputeID(foundLB.SecondaryNode.ComputeID).Data...) + + return &result, nil +} + +// FilterFunc allows filtering ListDisks based on a user-specified predicate. +func (ld ListDisks) FilterFunc(predicate func(ItemDisk) bool) ListDisks { + var result ListDisks + + for _, item := range ld.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(ld.Data)) + + return result +} + +// FindOne returns first found ItemDisk +// If none was found, returns an empty struct. +func (ld ListDisks) FindOne() ItemDisk { + if len(ld.Data) == 0 { + return ItemDisk{} + } + + return ld.Data[0] +} diff --git a/pkg/cloudbroker/disks/filter_test.go b/pkg/cloudbroker/disks/filter_test.go new file mode 100644 index 0000000..b84d636 --- /dev/null +++ b/pkg/cloudbroker/disks/filter_test.go @@ -0,0 +1,224 @@ +package disks + +import "testing" + +var disks = ListDisks{ + Data: []ItemDisk{ + { + MachineID: 0, + MachineName: "", + RecordDisk: RecordDisk{ + DeviceName: "vda", + SEPType: "", + InfoDisk: InfoDisk{ + AccountID: 132847, + AccountName: "std_2", + ACL: map[string]interface{}{}, + BootPartition: 0, + Computes: map[string]string{ + "48500": "test", + }, + CreatedTime: 1676975177, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + DiskPath: "", + GID: 212, + GUID: 65191, + ID: 65191, + ImageID: 9884, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + IQN: "", + Login: "", + Milestones: 363501, + Name: "bootdisk", + Order: 0, + Params: "", + ParentID: 0, + Password: "", + PCISlot: 6, + Pool: "vmstor", + PresentTo: []uint64{ + 27, + }, + PurgeAttempts: 0, + PurgeTime: 0, + RealityDeviceNumber: 0, + ReferenceID: "sample", + ResID: "sample", + ResName: "sample", + Role: "", + SEPID: 2504, + Shareable: false, + SizeMax: 2, + SizeUsed: 2, + Snapshots: []ItemSnapshot{}, + Status: "ASSIGNED", + TechStatus: "ALLOCATED", + Type: "B", + VMID: 48500, + }, + }, + }, + { + MachineID: 0, + MachineName: "", + RecordDisk: RecordDisk{ + DeviceName: "vda", + SEPType: "", + InfoDisk: InfoDisk{ + AccountID: 132852, + AccountName: "std", + ACL: map[string]interface{}{}, + BootPartition: 0, + Computes: map[string]string{ + "48502": "stdvm2", + }, + CreatedTime: 1676982606, + DeletedTime: 0, + Description: "", + DestructionTime: 0, + DiskPath: "", + GID: 212, + GUID: 65193, + ID: 65193, + ImageID: 9885, + Images: []uint64{}, + IOTune: IOTune{ + TotalIOPSSec: 2000, + }, + IQN: "", + Login: "", + Milestones: 363516, + Name: "bootdisk", + Order: 0, + Params: "", + ParentID: 0, + Password: "", + PCISlot: 6, + Pool: "vmstor", + PresentTo: []uint64{ + 27, + 27, + }, + PurgeAttempts: 0, + PurgeTime: 0, + RealityDeviceNumber: 0, + ReferenceID: "sample", + ResID: "sample", + ResName: "sample", + Role: "", + SEPID: 2504, + Shareable: false, + SizeMax: 4, + SizeUsed: 4, + Snapshots: []ItemSnapshot{}, + Status: "ASSIGNED", + TechStatus: "ALLOCATED", + Type: "B", + VMID: 48502, + }, + }, + }, + }, + EntryCount: 2, +} + +func TestFilterByID(t *testing.T) { + actual := disks.FilterByID(65193) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + actualItem := actual.FindOne() + + if actualItem.ID != 65193 { + t.Fatal("expected ID 65193, found: ", actualItem.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := disks.FilterByName("bootdisk") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 elements, found: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Name != "bootdisk" { + t.Fatal("expected 'bootdisk' name, found: ", item.Name) + } + } +} + +func TestFilterByStatus(t *testing.T) { + actual := disks.FilterByStatus("ASSIGNED") + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual.Data { + if item.Status != "ASSIGNED" { + t.Fatal("expected 'ASSIGNED' status, found: ", item.Status) + } + } +} + +func TestFilterByTechStatus(t *testing.T) { + actual := disks.FilterByTechStatus("ALLOCATED") + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + for _, item := range actual.Data { + if item.TechStatus != "ALLOCATED" { + t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus) + } + } +} + +func TestFilterByImageID(t *testing.T) { + actual := disks.FilterByImageID(9885) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + if actual.Data[0].ImageID != 9885 { + t.Fatal("expected 9885 ImageID, found: ", actual.Data[0].ImageID) + } +} + +func TestFilterFunc(t *testing.T) { + actual := disks.FilterFunc(func(id ItemDisk) bool { + return len(id.PresentTo) == 2 + }) + + if len(actual.Data) == 0 { + t.Fatal("No elements were found") + } + + if len(actual.Data[0].PresentTo) != 2 { + t.Fatal("expected 2 elements in PresentTo, found: ", len(actual.Data[0].PresentTo)) + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := disks.SortByCreatedTime(false) + + if actual.Data[0].ID != 65191 { + t.Fatal("expected ID 65191, found: ", actual.Data[0].ID) + } + + actual = disks.SortByCreatedTime(true) + + if actual.Data[0].ID != 65193 { + t.Fatal("expected ID 65193, found: ", actual.Data[0].ID) + } +} diff --git a/pkg/cloudbroker/disks/from_platform_disk.go b/pkg/cloudbroker/disks/from_platform_disk.go new file mode 100644 index 0000000..b111a8e --- /dev/null +++ b/pkg/cloudbroker/disks/from_platform_disk.go @@ -0,0 +1,126 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FromPlatformDiskRequest struct to create template from platform disk +type FromPlatformDiskRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Name of the rescue disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Boot type of image BIOS or UEFI + // Required: true + BootType string `url:"boottype" json:"boottype" validate:"imageBootType"` + + // Image type linux, windows or other + // Required: true + ImageType string `url:"imagetype" json:"imagetype" validate:"imageType"` + + // Binary architecture of this image + // Should be: + // - X86_64 + // Required: true + Architecture string `url:"architecture" json:"architecture" validate:"imageArchitecture"` + + // Username for the image + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Password for the image + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // Account ID to make the image exclusive + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // SEP ID + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool for image create + // Required: false + PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // List of types of compute suitable for image + // Example: [ "KVM_X86" ] + // Required: true + Drivers []string `url:"drivers" json:"drivers" validate:"required"` + + // Does this machine supports hot resize + // Required: false + HotResize bool `url:"hotresize" json:"hotresize"` + + // Bootable image + // Required: true + Bootable bool `url:"bootable" json:"bootable"` +} + +type wrapperFromPlatformDiskRequest struct { + FromPlatformDiskRequest + AsyncMode bool `url:"asyncMode"` +} + +// FromPlatformDisk creates template from platform disk in sync mode. +// It returns id of created disk and error. +func (d Disks) FromPlatformDisk(ctx context.Context, req FromPlatformDiskRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/fromPlatformDisk" + + reqWrapped := wrapperFromPlatformDiskRequest{ + FromPlatformDiskRequest: req, + AsyncMode: false, + } + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} + +// FromPlatformDiskAsync creates template from platform disk in async mode. +// It returns guid of task and error. +func (d Disks) FromPlatformDiskAsync(ctx context.Context, req FromPlatformDiskRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/fromPlatformDisk" + + reqWrapped := wrapperFromPlatformDiskRequest{ + FromPlatformDiskRequest: req, + AsyncMode: true, + } + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/disks/get.go b/pkg/cloudbroker/disks/get.go new file mode 100644 index 0000000..f33459a --- /dev/null +++ b/pkg/cloudbroker/disks/get.go @@ -0,0 +1,48 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about disk +type GetRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// Get gets disk details as a RecordDisk struct. +// Notice: the devicename field is the name as it is passed to the kernel (kname in linux) for unattached disks this field has no relevant value +func (d Disks) Get(ctx context.Context, req GetRequest) (*RecordDisk, error) { + res, err := d.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordDisk{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets disk details as an array of bytes. +// Notice: the devicename field is the name as it is passed to the kernel (kname in linux) for unattached disks this field has no relevant value +func (d Disks) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/get" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/disks/ids.go b/pkg/cloudbroker/disks/ids.go new file mode 100644 index 0000000..bed9508 --- /dev/null +++ b/pkg/cloudbroker/disks/ids.go @@ -0,0 +1,20 @@ +package disks + +// IDs gets array of DiskIDs from ListDisks struct +func (ld ListDisks) IDs() []uint64 { + res := make([]uint64, 0, len(ld.Data)) + for _, d := range ld.Data { + res = append(res, d.ID) + } + return res +} + +// IDs gets array of DiskIDs from ListUnattachedDisks struct +func (ldu ListUnattachedDisks) IDs() []uint64 { + res := make([]uint64, 0, len(ldu.Data)) + for _, d := range ldu.Data { + res = append(res, d.ID) + } + return res +} + diff --git a/pkg/cloudbroker/disks/limit_io.go b/pkg/cloudbroker/disks/limit_io.go new file mode 100644 index 0000000..eca485e --- /dev/null +++ b/pkg/cloudbroker/disks/limit_io.go @@ -0,0 +1,96 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// LimitIORequest struct for limit IO +type LimitIORequest struct { + // ID of the disk to limit + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Alias for total_iops_sec for backwards compatibility + // Required: false + IOPS uint64 `url:"iops,omitempty" json:"iops,omitempty"` + + // TotalBytesSec + // Required: false + TotalBytesSec uint64 `url:"total_bytes_sec,omitempty" json:"total_bytes_sec,omitempty"` + + // ReadBytesSec + // Required: false + ReadBytesSec uint64 `url:"read_bytes_sec,omitempty" json:"read_bytes_sec,omitempty"` + + // WriteBytesSec + // Required: false + WriteBytesSec uint64 `url:"write_bytes_sec,omitempty" json:"write_bytes_sec,omitempty"` + + // TotalIOPSSec + // Required: false + TotalIOPSSec uint64 `url:"total_iops_sec,omitempty" json:"total_iops_sec,omitempty"` + + // ReadIOPSSec + // Required: false + ReadIOPSSec uint64 `url:"read_iops_sec,omitempty" json:"read_iops_sec,omitempty"` + + // WriteIOPSSec + // Required: false + WriteIOPSSec uint64 `url:"write_iops_sec,omitempty" json:"write_iops_sec,omitempty"` + + // TotalBytesSecMax + // Required: false + TotalBytesSecMax uint64 `url:"total_bytes_sec_max,omitempty" json:"total_bytes_sec_max,omitempty"` + + // ReadBytesSecMax + // Required: false + ReadBytesSecMax uint64 `url:"read_bytes_sec_max,omitempty" json:"read_bytes_sec_max,omitempty"` + + // WriteBytesSecMax + // Required: false + WriteBytesSecMax uint64 `url:"write_bytes_sec_max,omitempty" json:"write_bytes_sec_max,omitempty"` + + // TotalIOPSSecMax + // Required: false + TotalIOPSSecMax uint64 `url:"total_iops_sec_max,omitempty" json:"total_iops_sec_max,omitempty"` + + // ReadIOPSSecMax + // Required: false + ReadIOPSSecMax uint64 `url:"read_iops_sec_max,omitempty" json:"read_iops_sec_max,omitempty"` + + // WriteIOPSSecMax + // Required: false + WriteIOPSSecMax uint64 `url:"write_iops_sec_max,omitempty" json:"write_iops_sec_max,omitempty"` + + // SizeIOPSSec + // Required: false + SizeIOPSSec uint64 `url:"size_iops_sec,omitempty" json:"size_iops_sec,omitempty"` +} + +// LimitIO limits IO for a certain disk +// total and read/write options are not allowed to be combined +// see http://libvirt.org/formatdomain.html#elementsDisks iotune section for more details +func (d Disks) LimitIO(ctx context.Context, req LimitIORequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/limitIO" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/list.go b/pkg/cloudbroker/disks/list.go new file mode 100644 index 0000000..fb00a0f --- /dev/null +++ b/pkg/cloudbroker/disks/list.go @@ -0,0 +1,94 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list/list_deleted of disks +type ListRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account name + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by max size disk + // Required: false + DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by shared, true or false + // Required: false + Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"` + + // ID of the account the disks belong to + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Type of the disks + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Find by sep ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by pool name + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of the created disks belonging to an account as a ListDisks struct +func (d Disks) List(ctx context.Context, req ListRequest) (*ListDisks, error) { + res, err := d.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of the created disks belonging to an account as an array of bytes +func (d Disks) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/list" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/disks/list_deleted.go b/pkg/cloudbroker/disks/list_deleted.go new file mode 100644 index 0000000..f8d718f --- /dev/null +++ b/pkg/cloudbroker/disks/list_deleted.go @@ -0,0 +1,76 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted disks +type ListDeletedRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account name + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by max size disk + // Required: false + DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"` + + // Find by shared, true or false + // Required: false + Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"` + + // ID of the account the disks belong to + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Type of the disks + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list of the deleted disks based on filter +func (d Disks) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListDisks, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/listDeleted" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/disks/list_types.go b/pkg/cloudbroker/disks/list_types.go new file mode 100644 index 0000000..43a906a --- /dev/null +++ b/pkg/cloudbroker/disks/list_types.go @@ -0,0 +1,53 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListTypesRequest struct to get list of types of disks +type ListTypesRequest struct { + // Show detailed disk types by seps + // Required: false + Detailed bool `url:"detailed" json:"detailed"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListTypes gets list of defined disk types +func (d Disks) ListTypes(ctx context.Context, req ListTypesRequest) (*ListTypes, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/listTypes" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListTypes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil + +} diff --git a/pkg/cloudbroker/disks/list_unattached.go b/pkg/cloudbroker/disks/list_unattached.go new file mode 100644 index 0000000..8cd5f3b --- /dev/null +++ b/pkg/cloudbroker/disks/list_unattached.go @@ -0,0 +1,80 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListUnattachedRequest struct to get list of unattached disk +type ListUnattachedRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by account name + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by max size disk + // Required: false + DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Type of the disks + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // ID of the account + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by sep ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by pool name + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListUnattached gets list of unattached disks +func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (*ListUnattachedDisks, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/listUnattached" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListUnattachedDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/disks/models.go b/pkg/cloudbroker/disks/models.go new file mode 100644 index 0000000..6381751 --- /dev/null +++ b/pkg/cloudbroker/disks/models.go @@ -0,0 +1,296 @@ +package disks + +// Main information about IO tune +type IOTune struct { + // ReadBytesSec + ReadBytesSec uint64 `json:"read_bytes_sec"` + + // ReadBytesSecMax + ReadBytesSecMax uint64 `json:"read_bytes_sec_max"` + + // ReadIOPSSec + ReadIOPSSec uint64 `json:"read_iops_sec"` + + // ReadIOPSSecMax + ReadIOPSSecMax uint64 `json:"read_iops_sec_max"` + + // SizeIOPSSec + SizeIOPSSec uint64 `json:"size_iops_sec"` + + // TotalBytesSec + TotalBytesSec uint64 `json:"total_bytes_sec"` + + // TotalBytesSecMax + TotalBytesSecMax uint64 `json:"total_bytes_sec_max"` + + // TotalIOPSSec + TotalIOPSSec uint64 `json:"total_iops_sec"` + + // TotalIOPSSecMax + TotalIOPSSecMax uint64 `json:"total_iops_sec_max"` + + // WriteBytesSec + WriteBytesSec uint64 `json:"write_bytes_sec"` + + // WriteBytesSecMax + WriteBytesSecMax uint64 `json:"write_bytes_sec_max"` + + // WriteIOPSSec + WriteIOPSSec uint64 `json:"write_iops_sec"` + + // WriteIOPSSecMax + WriteIOPSSecMax uint64 `json:"write_iops_sec_max"` +} + +// Main information about disks +type InfoDisk struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control Control + ACL map[string]interface{} `json:"acl"` + + // Boot partition + BootPartition uint64 `json:"bootPartition"` + + // Computes + Computes map[string]string `json:"computes"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Destruction time + DestructionTime uint64 `json:"destructionTime"` + + // Disk path + DiskPath string `json:"diskPath"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // List of image IDs + Images []uint64 `json:"images"` + + // IOTune + IOTune IOTune `json:"iotune"` + + // IQN + IQN string `json:"iqn"` + + // Login + Login string `json:"login"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Order + Order uint64 `json:"order"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // Password + Password string `json:"passwd"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Purge attempts + PurgeAttempts uint64 `json:"purgeAttempts"` + + // Purge time + PurgeTime uint64 `json:"purgeTime"` + + // Reality device number + RealityDeviceNumber uint64 `json:"realityDeviceNumber"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Replication + Replication ItemReplication `json:"replication"` + + // Resource ID + ResID string `json:"resId"` + + // Resource name + ResName string `json:"resName"` + + // Role + Role string `json:"role"` + + // SEP ID + SEPID uint64 `json:"sepId"` + + // Shareable + Shareable bool `json:"shareable"` + + // Size max + SizeMax int64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` +} + +type ItemReplication struct { + // DiskID + DiskID uint64 `json:"diskId"` + + // PoolID + PoolID string `json:"poolId"` + + // Role + Role string `json:"role"` + + // SelfVolumeID + SelfVolumeID string `json:"selfVolumeId"` + + // StorageID + StorageID string `json:"storageId"` + + // VolumeID + VolumeID string `json:"volumeId"` +} + +// Detailed indormation about disk +type RecordDisk struct { + // Device name + DeviceName string `json:"devicename"` + + // SEP type + SEPType string `json:"sepType"` + + // Main information about disk + InfoDisk +} + +// Main information for list disks +type ItemDisk struct { + // Machine ID + MachineID uint64 `json:"machineId"` + + // Machine name + MachineName string `json:"machineName"` + + // Detailed information about disk + RecordDisk +} + +// List disks +type ListDisks struct { + // Data + Data []ItemDisk `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// ListSearchDisks +type SearchListDisks []ItemDisk + +// Main information about unattached disk +type ItemUnattachedDisk struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Main information about disk + InfoDisk + + // Updated by + UpdatedBy uint64 `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// List unattached disks +type ListUnattachedDisks struct { + // Data + Data []ItemUnattachedDisk `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about snapshot +type ItemSnapshot struct { + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Resource ID + ResID string `json:"resId"` + + // SnapSet GUID + SnapSetGUID string `json:"snapSetGuid"` + + // SnapSet time + SnapSetTime uint64 `json:"snapSetTime"` + + // Timestamp + Timestamp uint64 `json:"timestamp"` +} + +// List snapshots +type ListSnapshots []ItemSnapshot + +type ListTypes struct { + // Data + Data []interface{} `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/disks/present.go b/pkg/cloudbroker/disks/present.go new file mode 100644 index 0000000..5e992ec --- /dev/null +++ b/pkg/cloudbroker/disks/present.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PresentRequest struct to present disk to node +type PresentRequest struct { + // ID of the disk to present + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID of the node to present disk to + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// Present presents disk to node +func (d Disks) Present(ctx context.Context, req PresentRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/present" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/rename.go b/pkg/cloudbroker/disks/rename.go new file mode 100644 index 0000000..57d63f3 --- /dev/null +++ b/pkg/cloudbroker/disks/rename.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RenameRequest struct to rename disk +type RenameRequest struct { + // ID of the disk to rename + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // New name of disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +// Rename renames disk +func (d Disks) Rename(ctx context.Context, req RenameRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/rename" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/replicate.go b/pkg/cloudbroker/disks/replicate.go new file mode 100644 index 0000000..0970f7a --- /dev/null +++ b/pkg/cloudbroker/disks/replicate.go @@ -0,0 +1,52 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicateRequest struct to create an empty disk in chosen SEP and pool combination. +type ReplicateRequest struct { + // Id of the disk to replicate. This disk will become master in replication + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Name of replica disk to create + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of SEP to create slave disk + // Required: true + SepID uint64 `url:"sepId" json:"sepId" validate:"required"` + + // Pool name to create slave disk in + // Required: true + PoolName string `url:"poolName" json:"poolName" validate:"required"` +} + +// Create an empty disk in chosen SEP and pool combination. +// Starts replication between chosen disk and newly created disk +// Note: only TATLIN type SEP are supported for replications between +func (d Disks) Replicate(ctx context.Context, req ReplicateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/replicate" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/replication_resume.go b/pkg/cloudbroker/disks/replication_resume.go new file mode 100644 index 0000000..e0d637b --- /dev/null +++ b/pkg/cloudbroker/disks/replication_resume.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationResume struct to resume suspended replication +type ReplicationResumeRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationResume resume suspended replication +func (d Disks) ReplicationResume(ctx context.Context, req ReplicationResumeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/replicationResume" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/replication_reverse.go b/pkg/cloudbroker/disks/replication_reverse.go new file mode 100644 index 0000000..e00979b --- /dev/null +++ b/pkg/cloudbroker/disks/replication_reverse.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationReverseRequest struct to change role between disks replications +type ReplicationReverseRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationReverse change role between disks replications +func (d Disks) ReplicationReverse(ctx context.Context, req ReplicationReverseRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/replicationReverse" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/replication_start.go b/pkg/cloudbroker/disks/replication_start.go new file mode 100644 index 0000000..6dd6e83 --- /dev/null +++ b/pkg/cloudbroker/disks/replication_start.go @@ -0,0 +1,43 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationStartRequest struct to starts replication between two chosen disks +type ReplicationStartRequest struct { + // Id of the disk to replicate. Primary disk in replication + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID of target disk. Secondary disk in replication + // Required: true + TargetDiskID uint64 `url:"targetDiskId" json:"targetDiskId" validate:"required"` +} + +// ReplicationStart starts replication between two chosen disks. It's required for both disks to have same size to avoid replication conflicts +// Note: Source disk's SEP and target SEP supported only of TATLIN type. +func (d Disks) ReplicationStart(ctx context.Context, req ReplicationStartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/replicationStart" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/replication_status.go b/pkg/cloudbroker/disks/replication_status.go new file mode 100644 index 0000000..a2ae2e5 --- /dev/null +++ b/pkg/cloudbroker/disks/replication_status.go @@ -0,0 +1,32 @@ +package disks + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationStatusRequest struct to get replication status +type ReplicationStatusRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationStatus get replication status +func (d Disks) ReplicationStatus(ctx context.Context, req ReplicationStatusRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/replicationStatus" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/disks/replication_stop.go b/pkg/cloudbroker/disks/replication_stop.go new file mode 100644 index 0000000..8e46387 --- /dev/null +++ b/pkg/cloudbroker/disks/replication_stop.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationStopRequest struct to remove replication between disks completely +type ReplicationStopRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationStop remove replication between disks completely +func (d Disks) ReplicationStop(ctx context.Context, req ReplicationStopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/replicationStop" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/replication_suspend.go b/pkg/cloudbroker/disks/replication_suspend.go new file mode 100644 index 0000000..cddcb1d --- /dev/null +++ b/pkg/cloudbroker/disks/replication_suspend.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ReplicationSuspendRequest struct to pause replication with possibility to resume from pause moment +type ReplicationSuspendRequest struct { + // Id of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// ReplicationSuspend pause replication with possibility to resume from pause moment +func (d Disks) ReplicationSuspend(ctx context.Context, req ReplicationSuspendRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/replicationSuspend" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/resize.go b/pkg/cloudbroker/disks/resize.go new file mode 100644 index 0000000..bcf447e --- /dev/null +++ b/pkg/cloudbroker/disks/resize.go @@ -0,0 +1,70 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ResizeRequest struct to resize disk +type ResizeRequest struct { + // ID of the disk to resize + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // New size of the disk in GB + // Required: true + Size uint64 `url:"size" json:"size" validate:"required"` +} + +// Resize resizes disk +// Returns 200 if disk is resized online, else will return 202, +// in that case please stop and start your machine after changing the disk size, for your changes to be reflected. +// This method will not be used for disks, assigned to computes. Only unassigned disks and disks, assigned with "old" virtual machines. +func (d Disks) Resize(ctx context.Context, req ResizeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/resize" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// Resize2 resize disk +// Returns 200 if disk is resized online, else will return 202, +// in that case please stop and start your machine after changing the disk size, for your changes to be reflected. +// This method will not be used for disks, assigned to "old" virtual machines. Only unassigned disks and disks, assigned with computes. +func (d Disks) Resize2(ctx context.Context, req ResizeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/resize2" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/restore.go b/pkg/cloudbroker/disks/restore.go new file mode 100644 index 0000000..4c544ec --- /dev/null +++ b/pkg/cloudbroker/disks/restore.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore a deleted unattached disk +type RestoreRequest struct { + // ID of the disk to restore + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// Restore restores a deleted unattached disk from recycle bin +func (d Disks) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/restore" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/search.go b/pkg/cloudbroker/disks/search.go new file mode 100644 index 0000000..e4af863 --- /dev/null +++ b/pkg/cloudbroker/disks/search.go @@ -0,0 +1,48 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" +) + +// SearchRequest struct for search +type SearchRequest struct { + // ID of the account to search for the Disk + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + // Name of the Disk to search for + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // If false, then disks having one of the statuses are not listed + // Required: false + ShowAll bool `url:"show_all,omitempty" json:"show_all,omitempty"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// Search searches disks +func (d Disks) Search(ctx context.Context, req SearchRequest) (SearchListDisks, error) { + url := "/cloudbroker/disks/search" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := SearchListDisks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/disks/serialize.go b/pkg/cloudbroker/disks/serialize.go new file mode 100644 index 0000000..c852d16 --- /dev/null +++ b/pkg/cloudbroker/disks/serialize.go @@ -0,0 +1,43 @@ +package disks + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ld ListDisks) Serialize(params ...string) (serialization.Serialized, error) { + if len(ld.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ld, prefix, indent) + } + + return json.Marshal(ld) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (idisk ItemDisk) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(idisk, prefix, indent) + } + + return json.Marshal(idisk) +} diff --git a/pkg/cloudbroker/disks/share.go b/pkg/cloudbroker/disks/share.go new file mode 100644 index 0000000..0172907 --- /dev/null +++ b/pkg/cloudbroker/disks/share.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ShareRequest struct to share data disk +type ShareRequest struct { + // ID of the disk to share + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// Share shares data disk +func (d Disks) Share(ctx context.Context, req ShareRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/share" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/snapshot_delete.go b/pkg/cloudbroker/disks/snapshot_delete.go new file mode 100644 index 0000000..feaaf64 --- /dev/null +++ b/pkg/cloudbroker/disks/snapshot_delete.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotDeleteRequest struct to delete snapshot +type SnapshotDeleteRequest struct { + // ID of disk to delete + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Label of the snapshot to delete + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +// SnapshotDelete deletes a snapshot +func (d Disks) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/snapshotDelete" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/snapshot_rollback.go b/pkg/cloudbroker/disks/snapshot_rollback.go new file mode 100644 index 0000000..e9a5474 --- /dev/null +++ b/pkg/cloudbroker/disks/snapshot_rollback.go @@ -0,0 +1,46 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SnapshotRollbackRequest struct to rollback snapshot +type SnapshotRollbackRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // Label of the snapshot to rollback + // Required: false + Label string `url:"label,omitempty" json:"label,omitempty"` + + // Timestamp of the snapshot to rollback + // Required: false + TimeStamp uint64 `url:"timestamp,omitempty" json:"timestamp,omitempty"` +} + +// SnapshotRollback rollbacks an individual disk snapshot +func (d Disks) SnapshotRollback(ctx context.Context, req SnapshotRollbackRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/snapshotRollback" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/sorting.go b/pkg/cloudbroker/disks/sorting.go new file mode 100644 index 0000000..7a973b8 --- /dev/null +++ b/pkg/cloudbroker/disks/sorting.go @@ -0,0 +1,60 @@ +package disks + +import "sort" + +// SortByCreatedTime sorts ListDisks by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListDisks) SortByCreatedTime(inverse bool) ListDisks { + if len(ld.Data) < 2 { + return ld + } + + sort.Slice(ld.Data, func(i, j int) bool { + if inverse { + return ld.Data[i].CreatedTime > ld.Data[j].CreatedTime + } + + return ld.Data[i].CreatedTime < ld.Data[j].CreatedTime + }) + + return ld +} + +// SortByDestructionTime sorts ListDisks by the DestructionTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListDisks) SortByDestructionTime(inverse bool) ListDisks { + if len(ld.Data) < 2 { + return ld + } + + sort.Slice(ld.Data, func(i, j int) bool { + if inverse { + return ld.Data[i].DestructionTime > ld.Data[j].DestructionTime + } + + return ld.Data[i].DestructionTime < ld.Data[j].DestructionTime + }) + + return ld +} + +// SortByDeletedTime sorts ListDisks by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ld ListDisks) SortByDeletedTime(inverse bool) ListDisks { + if len(ld.Data) < 2 { + return ld + } + + sort.Slice(ld.Data, func(i, j int) bool { + if inverse { + return ld.Data[i].DeletedTime > ld.Data[j].DeletedTime + } + + return ld.Data[i].DeletedTime < ld.Data[j].DeletedTime + }) + + return ld +} diff --git a/pkg/cloudbroker/disks/unshare.go b/pkg/cloudbroker/disks/unshare.go new file mode 100644 index 0000000..ae79b07 --- /dev/null +++ b/pkg/cloudbroker/disks/unshare.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UnshareRequest struct to unshare data disk +type UnshareRequest struct { + // ID of the disk to unshare + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` +} + +// Unshare unshares data disk +func (d Disks) Unshare(ctx context.Context, req UnshareRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/unshare" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/dpdk.go b/pkg/cloudbroker/dpdk.go new file mode 100644 index 0000000..117d0dc --- /dev/null +++ b/pkg/cloudbroker/dpdk.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/dpdknet" + +// Accessing the DPDK method group +func (ca *CloudBroker) DPDKNet() *dpdknet.DPDKNet { + return dpdknet.New(ca.client) +} diff --git a/pkg/cloudbroker/dpdknet/create.go b/pkg/cloudbroker/dpdknet/create.go new file mode 100644 index 0000000..0248d7c --- /dev/null +++ b/pkg/cloudbroker/dpdknet/create.go @@ -0,0 +1,62 @@ +package dpdknet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create DPDK network +type CreateRequest struct { + // Name of new DPDK network + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Description of new DPDK network. Prefer to contain additional information about DPDK network. + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // ID of the grid (platform) + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // ID of vlan + // Required: true + VlanID uint64 `url:"vlanId" json:"vlanId" validate:"required"` + + // Name of OVS Bridge to use for creating DPDK network on + // Required: true + OVSBridge string `url:"ovsBridge" json:"ovsBridge" validate:"required"` + + // List of account IDs to which DPDK network will be available. Empty field means all accounts in the system. + // Required: false + AccountAccess []uint64 `url:"accountAccess,omitempty" json:"accountAccess,omitempty"` + + // List of resource groups IDs to which DPDK network will be available. Empty field means all resource groups in the system. + // Required: false + RGAccess []uint64 `url:"rgAccess,omitempty" json:"rgAccess,omitempty"` +} + +// Create creates a DPDK networks +func (d DPDKNet) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/dpdknet/create" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/dpdknet/delete.go b/pkg/cloudbroker/dpdknet/delete.go new file mode 100644 index 0000000..10c8460 --- /dev/null +++ b/pkg/cloudbroker/dpdknet/delete.go @@ -0,0 +1,38 @@ +package dpdknet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete DPDK network +type DeleteRequest struct { + // ID of DPDK network to delete + // Required: true + DPDKID uint64 `url:"dpdkId" json:"dpdkId" validate:"required"` +} + +// Delete deletes DPDK network by ID +func (d DPDKNet) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/dpdknet/delete" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/dpdknet/disable.go b/pkg/cloudbroker/dpdknet/disable.go new file mode 100644 index 0000000..b4c32aa --- /dev/null +++ b/pkg/cloudbroker/dpdknet/disable.go @@ -0,0 +1,39 @@ +package dpdknet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable DPDK network +// Computes will not be able to use it until it is enabled again. +type DisableRequest struct { + // ID of DPDK network to disable + // Required: true + DPDKID uint64 `url:"dpdkId" json:"dpdkId" validate:"required"` +} + +// Disable disables DPDK network by ID +func (d DPDKNet) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/dpdknet/disable" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/dpdknet/dpdk.go b/pkg/cloudbroker/dpdknet/dpdk.go new file mode 100644 index 0000000..c7de8c2 --- /dev/null +++ b/pkg/cloudbroker/dpdknet/dpdk.go @@ -0,0 +1,15 @@ +package dpdknet + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to DPDK network +type DPDKNet struct { + client interfaces.Caller +} + +// Builder for dpdk endpoints +func New(client interfaces.Caller) *DPDKNet { + return &DPDKNet{ + client, + } +} diff --git a/pkg/cloudbroker/dpdknet/enable.go b/pkg/cloudbroker/dpdknet/enable.go new file mode 100644 index 0000000..f20e408 --- /dev/null +++ b/pkg/cloudbroker/dpdknet/enable.go @@ -0,0 +1,38 @@ +package dpdknet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable DPDK network +type EnableRequest struct { + // ID of DPDK network to enable + // Required: true + DPDKID uint64 `url:"dpdkId" json:"dpdkId" validate:"required"` +} + +// Enable enables DPDK network by ID +func (d DPDKNet) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/dpdknet/enable" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/dpdknet/get.go b/pkg/cloudbroker/dpdknet/get.go new file mode 100644 index 0000000..10b6f80 --- /dev/null +++ b/pkg/cloudbroker/dpdknet/get.go @@ -0,0 +1,46 @@ +package dpdknet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about DPDK network +type GetRequest struct { + // ID of the DPDK network + // Required: true + DPDKID uint64 `url:"dpdkId" json:"dpdkId" validate:"required"` +} + +// Get DPDK network details as a RecordDPDKNet struct +func (d DPDKNet) Get(ctx context.Context, req GetRequest) (*RecordDPDKNet, error) { + res, err := d.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordDPDKNet{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets DPDK network details as an array of bytes +func (d DPDKNet) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/dpdknet/get" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/dpdknet/list.go b/pkg/cloudbroker/dpdknet/list.go new file mode 100644 index 0000000..1b203c6 --- /dev/null +++ b/pkg/cloudbroker/dpdknet/list.go @@ -0,0 +1,91 @@ +package dpdknet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of DPDK networks +type ListRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by gid + // Required: false + GID uint64 `url:"gid,omitempty" json:"gid,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by account access + // Required: false + AccountAccess []uint64 `url:"accountAccess,omitempty" json:"accountAccess,omitempty"` + + // Find by resource group access + // Required: false + RGAccess []uint64 `url:"rgAccess,omitempty" json:"rgAccess,omitempty"` + + // Find by vlan ID + // Required: false + VlanID uint64 `url:"vlanId,omitempty" json:"vlanId,omitempty"` + + // Find by computeIDs + // Required: false + ComputeIDs []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of the created DPDK networks belonging to an account as a ListDPDKNet struct +func (d DPDKNet) List(ctx context.Context, req ListRequest) (*ListDPDKNet, error) { + + res, err := d.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListDPDKNet{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of the created DPDK networks belonging to an account as an array of bytes +func (d DPDKNet) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/dpdknet/list" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/dpdknet/models.go b/pkg/cloudbroker/dpdknet/models.go new file mode 100644 index 0000000..7bcf84d --- /dev/null +++ b/pkg/cloudbroker/dpdknet/models.go @@ -0,0 +1,92 @@ +package dpdknet + +// Detailed information about DPDK network +type RecordDPDKNet struct { + // List of accounts with access + AccountAccess []uint64 `json:"accountAccess"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Description + Description string `json:"description"` + + // Grid ID + GID uint64 `json:"gid"` + + // Guid ID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // List of resource groups with access + RGAccess []uint64 `json:"rgAccess"` + + // Status + Status string `json:"status"` + + // OVS bridge + OVSBridge string `json:"ovsBridge"` + + // Vlan ID + VlanID uint64 `json:"vlanId"` + + // Compute IDs + ComputeIDs []uint64 `json:"computeIds"` +} + +type ListDPDKNet struct { + // Data + Data []ItemDPDKNet `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type ItemDPDKNet struct { + // List of accounts with access + AccountAccess []uint64 `json:"accountAccess"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Description + Description string `json:"description"` + + // Grid ID + GID uint64 `json:"gid"` + + // Guid ID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // List of resource groups with access + RGAccess []uint64 `json:"rgAccess"` + + // Status + Status string `json:"status"` + + // OVS bridge + OVSBridge string `json:"ovsBridge"` + + // Vlan ID + VlanID uint64 `json:"vlanId"` + + // Compute IDs + ComputeIDs []uint64 `json:"computeIds"` +} diff --git a/pkg/cloudbroker/dpdknet/update.go b/pkg/cloudbroker/dpdknet/update.go new file mode 100644 index 0000000..a9b4f1d --- /dev/null +++ b/pkg/cloudbroker/dpdknet/update.go @@ -0,0 +1,62 @@ +package dpdknet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update DPDK network +type UpdateRequest struct { + // ID of DPDK network to update + // Required: true + DPDKID uint64 `url:"dpdkId" json:"dpdkId" validate:"required"` + + // New name of DPDK network + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Description of DPDK network. Prefer to contain additional information about DPDK network + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // List of account IDs to which DPDK network will be available. Empty field means all accounts in the system. + // Required: false + AccountAccess []uint64 `url:"accountAccess,omitempty" json:"accountAccess,omitempty"` + + // List of resource groups IDs to which DPDK network will be available. Empty field means all resource groups in the system. + // Required: true + RGAccess []uint64 `url:"rgAccess,omitempty" json:"rgAccess,omitempty"` + + // ID of vlan + // Required: true + VlanID uint64 `url:"vlanId,omitempty" json:"vlanId,omitempty"` + + // Name of OVS Bridge to use for DPDK network + // Required: true + OVSBridge string `url:"ovsBridge,omitempty" json:"ovsBridge,omitempty"` +} + +// Update updates a DPDK networks +func (d DPDKNet) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/dpdknet/update" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet.go b/pkg/cloudbroker/extnet.go new file mode 100644 index 0000000..2b0a5ae --- /dev/null +++ b/pkg/cloudbroker/extnet.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/extnet" +) + +// Accessing the ExtNet method group +func (cb *CloudBroker) ExtNet() *extnet.ExtNet { + return extnet.New(cb.client) +} diff --git a/pkg/cloudbroker/extnet/access_add.go b/pkg/cloudbroker/extnet/access_add.go new file mode 100644 index 0000000..46a0edc --- /dev/null +++ b/pkg/cloudbroker/extnet/access_add.go @@ -0,0 +1,44 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessAddRequest struct to grant access +type AccessAddRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // Account ID + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// AccessAdd grants access to external network for account ID +func (e ExtNet) AccessAdd(ctx context.Context, req AccessAddRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/accessAdd" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := make([]uint64, 0) + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/extnet/access_remove.go b/pkg/cloudbroker/extnet/access_remove.go new file mode 100644 index 0000000..1a23a7c --- /dev/null +++ b/pkg/cloudbroker/extnet/access_remove.go @@ -0,0 +1,44 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessRemoveRequest struct to remove access +type AccessRemoveRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // Account ID + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// AccessRemove removes access from external network for account ID +func (e ExtNet) AccessRemove(ctx context.Context, req AccessRemoveRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/accessRemove" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := make([]uint64, 0) + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/extnet/create.go b/pkg/cloudbroker/extnet/create.go new file mode 100644 index 0000000..13b0ff4 --- /dev/null +++ b/pkg/cloudbroker/extnet/create.go @@ -0,0 +1,138 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type Route struct { + // Destination network + Destination string `url:"destination" json:"destination" validate:"required"` + + //Destination network mask in 255.255.255.255 format + Netmask string `url:"netmask" json:"netmask" validate:"required"` + + //Next hop host, IP address from ViNS ID free IP pool + Gateway string `url:"gateway" json:"gateway" validate:"required"` +} + +// CreateRequest struct to create external network +type CreateRequest struct { + // External network name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Grid (platform) ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // IP network CIDR + // For example 192.168.0.0/24 + // Required: true + IPCIDR string `url:"ipcidr" json:"ipcidr" validate:"required"` + + // VLAN ID + // Required: true + VLANID uint64 `url:"vlanId" json:"vlanId" validate:"required"` + + // External network gateway IP address + // Required: false + Gateway string `url:"gateway,omitempty" json:"gateway,omitempty"` + + // List of DNS addresses + // Required: false + DNS []string `url:"dns,omitempty" json:"dns,omitempty"` + + // List of NTP addresses + // Required: false + NTP []string `url:"ntp,omitempty" json:"ntp,omitempty"` + + // IPs to check network availability + // Required: false + CheckIPs []string `url:"checkIps,omitempty" json:"checkIps,omitempty"` + + // If true - platform DHCP server will not be created + // Required: false + Virtual bool `url:"virtual,omitempty" json:"virtual,omitempty"` + + // Optional description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Start of IP range to be explicitly included + // Required: false + StartIP string `url:"startIP,omitempty" json:"startIP,omitempty"` + + // End of IP range to be explicitly included + // Required: false + EndIP string `url:"endIP,omitempty" json:"endIP,omitempty"` + + // IP to create VNFDev with + // Required: false + VNFDevIP string `url:"vnfdevIP,omitempty" json:"vnfdevIP,omitempty"` + + // Number of pre created reservations + // Required: false + PreReservationsNum uint64 `url:"preReservationsNum,omitempty" json:"preReservationsNum,omitempty"` + + // OpenvSwith bridge name for ExtNet connection + // Required: false + OVSBridge string `url:"ovsBridge,omitempty" json:"ovsBridge,omitempty"` + + // List of static routes, each item must have destination, netmask, and gateway fields + // Required: false + Routes []Route `url:"-" json:"routes,omitempty" validate:"omitempty,dive"` +} + +type wrapperCreateRequest struct { + CreateRequest + Routes []string `url:"routes,omitempty"` +} + +// Create creates new external network into platform +func (e ExtNet) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var routes []string + + if len(req.Routes) != 0 { + routes = make([]string, 0, len(req.Routes)) + + for r := range req.Routes { + b, err := json.Marshal(req.Routes[r]) + if err != nil { + return 0, err + } + + routes = append(routes, string(b)) + } + } else { + routes = []string{} + } + + reqWrapped := wrapperCreateRequest{ + CreateRequest: req, + Routes: routes, + } + + url := "/cloudbroker/extnet/create" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/default_qos_update.go b/pkg/cloudbroker/extnet/default_qos_update.go new file mode 100644 index 0000000..70f3417 --- /dev/null +++ b/pkg/cloudbroker/extnet/default_qos_update.go @@ -0,0 +1,50 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DefaultQOSUpdateRequest struct for update QOS +type DefaultQOSUpdateRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // Internal traffic, kbit + // Required: false + IngressRate uint64 `url:"ingress_rate,omitempty" json:"ingress_rate,omitempty"` + + // Internal traffic burst, kbit + // Required: false + IngressBurst uint64 `url:"ingress_burst,omitempty" json:"ingress_burst,omitempty"` + + // External traffic rate, kbit + // Required: false + EgressRate uint64 `url:"egress_rate,omitempty" json:"egress_rate,omitempty"` +} + +// DefaultQOSUpdate updates default qos values +func (e ExtNet) DefaultQOSUpdate(ctx context.Context, req DefaultQOSUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/defaultQosUpdate" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/destroy.go b/pkg/cloudbroker/extnet/destroy.go new file mode 100644 index 0000000..040e242 --- /dev/null +++ b/pkg/cloudbroker/extnet/destroy.go @@ -0,0 +1,38 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DestroyRequest struct for destroy +type DestroyRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// Destroy destroys external network +func (e ExtNet) Destroy(ctx context.Context, req DestroyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/destroy" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/device_deploy.go b/pkg/cloudbroker/extnet/device_deploy.go new file mode 100644 index 0000000..764661b --- /dev/null +++ b/pkg/cloudbroker/extnet/device_deploy.go @@ -0,0 +1,38 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeviceDeployRequest struct to deploy network device +type DeviceDeployRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// DeviceDeploy deploys network device for external network (make not virtual, "physical") +func (e ExtNet) DeviceDeploy(ctx context.Context, req DeviceDeployRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/deviceDeploy" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/device_migrate.go b/pkg/cloudbroker/extnet/device_migrate.go new file mode 100644 index 0000000..9ade465 --- /dev/null +++ b/pkg/cloudbroker/extnet/device_migrate.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeviceMigrateRequest struct for migrate VNF +type DeviceMigrateRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // Target stack ID to migrate to + // Required: false + StackID uint64 `url:"stackId,omitempty" json:"stackId,omitempty"` +} + +// DeviceMigrate migrates external network VNF device +func (e ExtNet) DeviceMigrate(ctx context.Context, req DeviceMigrateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/deviceMigrate" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/device_remove.go b/pkg/cloudbroker/extnet/device_remove.go new file mode 100644 index 0000000..dc21e6e --- /dev/null +++ b/pkg/cloudbroker/extnet/device_remove.go @@ -0,0 +1,38 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeviceRemoveRequest struct to remove network device +type DeviceRemoveRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// DeviceRemove removes network device of external network (make it virtual, not "physical") +func (e ExtNet) DeviceRemove(ctx context.Context, req DeviceRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/deviceRemove" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/device_restart.go b/pkg/cloudbroker/extnet/device_restart.go new file mode 100644 index 0000000..0967549 --- /dev/null +++ b/pkg/cloudbroker/extnet/device_restart.go @@ -0,0 +1,38 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeviceRestartRequest struct for restart VNF device +type DeviceRestartRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// DeviceRestart restarts external network VNF device +func (e ExtNet) DeviceRestart(ctx context.Context, req DeviceRestartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/deviceRestart" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/disable.go b/pkg/cloudbroker/extnet/disable.go new file mode 100644 index 0000000..64ea81c --- /dev/null +++ b/pkg/cloudbroker/extnet/disable.go @@ -0,0 +1,38 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable external network +type DisableRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// Disable disables external network +func (e ExtNet) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/disable" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/dns_apply.go b/pkg/cloudbroker/extnet/dns_apply.go new file mode 100644 index 0000000..81c2ad9 --- /dev/null +++ b/pkg/cloudbroker/extnet/dns_apply.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DNSApplyRequest struct to set new DNS +type DNSApplyRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // List of DNS to apply + // Required: false + DNSList []string `url:"dns_list,omitempty" json:"dns_list,omitempty"` +} + +// DNSApply sets new DNS +func (e ExtNet) DNSApply(ctx context.Context, req DNSApplyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/dnsApply" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/enable.go b/pkg/cloudbroker/extnet/enable.go new file mode 100644 index 0000000..af3c9d2 --- /dev/null +++ b/pkg/cloudbroker/extnet/enable.go @@ -0,0 +1,38 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable external network +type EnableRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// Enable enables external networks +func (e ExtNet) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/enable" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/extnet.go b/pkg/cloudbroker/extnet/extnet.go new file mode 100644 index 0000000..6d3dde8 --- /dev/null +++ b/pkg/cloudbroker/extnet/extnet.go @@ -0,0 +1,16 @@ +// API Actor for configure and use external networks +package extnet + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to extnet +type ExtNet struct { + client interfaces.Caller +} + +// Builder for extnet endpoints +func New(client interfaces.Caller) *ExtNet { + return &ExtNet{ + client: client, + } +} diff --git a/pkg/cloudbroker/extnet/filter.go b/pkg/cloudbroker/extnet/filter.go new file mode 100644 index 0000000..5b21c55 --- /dev/null +++ b/pkg/cloudbroker/extnet/filter.go @@ -0,0 +1,53 @@ +package extnet + +// FilterByID returns ListExtNet with specified ID. +func (lenet ListExtNet) FilterByID(id uint64) ListExtNet { + predicate := func(iexnet ItemExtNet) bool { + return iexnet.ID == id + } + + return lenet.FilterFunc(predicate) +} + +// FilterByName returns ListExtNet with specified Name. +func (lenet ListExtNet) FilterByName(name string) ListExtNet { + predicate := func(iexnet ItemExtNet) bool { + return iexnet.Name == name + } + + return lenet.FilterFunc(predicate) +} + +// FilterByStatus returns ListExtNet with specified Status. +func (lenet ListExtNet) FilterByStatus(status string) ListExtNet { + predicate := func(iexnet ItemExtNet) bool { + return iexnet.Status == status + } + + return lenet.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListExtNet based on a user-specified predicate. +func (lenet ListExtNet) FilterFunc(predicate func(ItemExtNet) bool) ListExtNet { + var result ListExtNet + + for _, item := range lenet.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(lenet.Data)) + + return result +} + +// FindOne returns first found ItemExtNet +// If none was found, returns an empty struct. +func (lenet ListExtNet) FindOne() ItemExtNet { + if len(lenet.Data) == 0 { + return ItemExtNet{} + } + + return lenet.Data[0] +} diff --git a/pkg/cloudbroker/extnet/filter_test.go b/pkg/cloudbroker/extnet/filter_test.go new file mode 100644 index 0000000..e92a0b3 --- /dev/null +++ b/pkg/cloudbroker/extnet/filter_test.go @@ -0,0 +1,118 @@ +package extnet + +import "testing" + +var extnets = ListExtNet{ + Data: []ItemExtNet{ + { + CKey: "", + Meta: []interface{}{}, + CheckIPs: []string{}, + Default: false, + DefaultQOS: QOS{}, + Description: "", + FreeIPs: 0, + GID: 212, + GUID: 3, + ID: 3, + IPCIDR: "176.118.164.0/24", + Milestones: 1355466, + Name: "176.118.164.0/24", + NetworkID: 0, + OVSBridge: "", + PreReservationsNum: 0, + PriVNFDevID: 0, + SharedWith: []interface{}{}, + Status: "ENABLED", + VLANID: 0, + VNFs: VNFs{}, + }, + { + CKey: "", + Meta: []interface{}{}, + CheckIPs: []string{}, + Default: false, + DefaultQOS: QOS{}, + Description: "", + FreeIPs: 0, + GID: 212, + GUID: 10, + ID: 10, + IPCIDR: "45.134.255.0/24", + Milestones: 2135543, + Name: "45.134.255.0/24", + NetworkID: 0, + OVSBridge: "", + PreReservationsNum: 0, + PriVNFDevID: 0, + SharedWith: []interface{}{}, + Status: "ENABLED", + VLANID: 0, + VNFs: VNFs{}, + }, + { + CKey: "", + Meta: []interface{}{}, + CheckIPs: []string{}, + Default: false, + DefaultQOS: QOS{}, + Description: "", + FreeIPs: 0, + GID: 212, + GUID: 13, + ID: 13, + IPCIDR: "88.218.249.0/24", + Milestones: 1232134, + Name: "88.218.249.0/24", + NetworkID: 0, + OVSBridge: "", + PreReservationsNum: 0, + PriVNFDevID: 0, + SharedWith: []interface{}{}, + Status: "DISABLED", + VLANID: 0, + VNFs: VNFs{}, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := extnets.FilterByID(10).FindOne() + + if actual.ID != 10 { + t.Fatal("expected ID 10, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + name := "88.218.249.0/24" + actual := extnets.FilterByName(name).FindOne() + + if actual.Name != name { + t.Fatal("expected ", name, " found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := extnets.FilterByStatus("ENABLED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := extnets.FilterFunc(func(ien ItemExtNet) bool { + return ien.IPCIDR == ien.Name + }) + + if len(actual.Data) != 3 { + t.Fatal("expected 3 elements, found: ", len(actual.Data)) + } +} diff --git a/pkg/cloudbroker/extnet/get.go b/pkg/cloudbroker/extnet/get.go new file mode 100644 index 0000000..4c35109 --- /dev/null +++ b/pkg/cloudbroker/extnet/get.go @@ -0,0 +1,46 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about external network +type GetRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// Get gets external network details as a RecordExtNet struct +func (e ExtNet) Get(ctx context.Context, req GetRequest) (*RecordExtNet, error) { + res, err := e.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordExtNet{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets external network details as an array of bytes +func (e ExtNet) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/get" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/extnet/get_default.go b/pkg/cloudbroker/extnet/get_default.go new file mode 100644 index 0000000..1285d22 --- /dev/null +++ b/pkg/cloudbroker/extnet/get_default.go @@ -0,0 +1,24 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" +) + +// GetDefault get default external network ID +func (e ExtNet) GetDefault(ctx context.Context) (uint64, error) { + url := "/cloudbroker/extnet/getDefault" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/ids.go b/pkg/cloudbroker/extnet/ids.go new file mode 100644 index 0000000..d286a3c --- /dev/null +++ b/pkg/cloudbroker/extnet/ids.go @@ -0,0 +1,19 @@ +package extnet + +// IDs gets array of ExtNetIDs from ListExtNet struct +func (le ListExtNet) IDs() []uint64 { + res := make([]uint64, 0, len(le.Data)) + for _, e := range le.Data { + res = append(res, e.ID) + } + return res +} + +// IDs gets array of StaticRouteIDs from ListStaticRoutes struct +func (lsr ListStaticRoutes) IDs() []uint64 { + res := make([]uint64, 0, len(lsr.Data)) + for _, sr := range lsr.Data { + res = append(res, sr.ID) + } + return res +} diff --git a/pkg/cloudbroker/extnet/ips_exclude.go b/pkg/cloudbroker/extnet/ips_exclude.go new file mode 100644 index 0000000..1c234dc --- /dev/null +++ b/pkg/cloudbroker/extnet/ips_exclude.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPsExcludeRequest struct to exclude list of IPs +type IPsExcludeRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // List of IPs for exclude from external network + // Required: true + IPs []string `url:"ips" json:"ips" validate:"min=1"` +} + +// IPsExclude excludes list of IPs from external network pool +func (e ExtNet) IPsExclude(ctx context.Context, req IPsExcludeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/ipsExclude" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/ips_exclude_range.go b/pkg/cloudbroker/extnet/ips_exclude_range.go new file mode 100644 index 0000000..10379c9 --- /dev/null +++ b/pkg/cloudbroker/extnet/ips_exclude_range.go @@ -0,0 +1,46 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPsExcludeRangeRequest struct to exclude range of IPs +type IPsExcludeRangeRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // Starting IP + // Required: true + IPStart string `url:"ip_start" json:"ip_start" validate:"required"` + + // Ending IP + // Required: true + IPEnd string `url:"ip_end" json:"ip_end" validate:"required"` +} + +// IPsExcludeRange excludes range of IPs from external network pool +func (e ExtNet) IPsExcludeRange(ctx context.Context, req IPsExcludeRangeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/ipsExcludeRange" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/ips_include.go b/pkg/cloudbroker/extnet/ips_include.go new file mode 100644 index 0000000..76f88a9 --- /dev/null +++ b/pkg/cloudbroker/extnet/ips_include.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPsIncludeRequest struct to include list of IPs +type IPsIncludeRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // List of IPs for include to external network + // Required: true + IPs []string `url:"ips" json:"ips" validate:"min=1"` +} + +// IPsInclude includes list of IPs to external network pool +func (e ExtNet) IPsInclude(ctx context.Context, req IPsIncludeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/ipsInclude" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/ips_include_range.go b/pkg/cloudbroker/extnet/ips_include_range.go new file mode 100644 index 0000000..b70d1bf --- /dev/null +++ b/pkg/cloudbroker/extnet/ips_include_range.go @@ -0,0 +1,46 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPsIncludeRangeRequest struct to include range of IPs +type IPsIncludeRangeRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // Starting IP + // Required: true + IPStart string `url:"ip_start" json:"ip_start" validate:"required"` + + // Ending IP + // Required: true + IPEnd string `url:"ip_end" json:"ip_end" validate:"required"` +} + +// IPsIncludeRange includes range of IPs to external network pool +func (e ExtNet) IPsIncludeRange(ctx context.Context, req IPsIncludeRangeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/ipsIncludeRange" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/list.go b/pkg/cloudbroker/extnet/list.go new file mode 100644 index 0000000..69f28ae --- /dev/null +++ b/pkg/cloudbroker/extnet/list.go @@ -0,0 +1,83 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of external network +type ListRequest struct { + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by network ip address + // Required: false + Network string `url:"network,omitempty" json:"network,omitempty"` + + // Find by vlan ID + // Required: false + VLANID uint64 `url:"vlanId,omitempty" json:"vlanId,omitempty"` + + // Find by vnfDevices ID + // Required: false + VNFDevID uint64 `url:"vnfDevId,omitempty" json:"vnfDevId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all available external networks as a ListExtNet struct +func (e ExtNet) List(ctx context.Context, req ListRequest) (*ListExtNet, error) { + + res, err := e.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListExtNet{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all available external networks as an array of bytes +func (e ExtNet) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/list" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/extnet/models.go b/pkg/cloudbroker/extnet/models.go new file mode 100644 index 0000000..fb34768 --- /dev/null +++ b/pkg/cloudbroker/extnet/models.go @@ -0,0 +1,240 @@ +package extnet + +// QOS +type QOS struct { + // ERate + ERate uint64 `json:"eRate"` + + // GUID + GUID string `json:"guid"` + + // InBurst + InBurst uint64 `json:"inBurst"` + + // InRate + InRate uint64 `json:"inRate"` +} + +// Main information about reservations +type ItemReservation struct { + // Client type + ClientType string `json:"clientType"` + + // Description + Description string `json:"desc"` + + // IP + IP string `json:"ip"` + + // MAC + MAC string `json:"mac"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmId"` + + // Domain name + DomainName string `json:"domainname"` + + // Hostname + Hostname string `json:"hostname"` +} + +// List reservations +type ListReservations []ItemReservation + +// VNFs +type VNFs struct { + DHCP int `json:"dhcp"` +} + +// Main information about external network +type ItemExtNet struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // CheckIPs + CheckIPs []string `json:"checkIps"` + + // Default + Default bool `json:"default"` + + // Default QOS + DefaultQOS QOS `json:"defaultQos"` + + // Description + Description string `json:"desc"` + + // Free IPs number + FreeIPs int64 `json:"freeIps"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IPCIDR + IPCIDR string `json:"ipcidr"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network ID + NetworkID uint64 `json:"networkId"` + + // OVSBridge + OVSBridge string `json:"ovsBridge"` + + // PreReservationsNum + PreReservationsNum uint64 `json:"preReservationsNum"` + + // PriVNFDevID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // List of shared with + SharedWith []interface{} `json:"sharedWith"` + + // Status + Status string `json:"status"` + + // VLAN ID + VLANID uint64 `json:"vlanId"` + + // VNFs + VNFs VNFs `json:"vnfs"` +} + +// List external networks +type ListExtNet struct { + // Data + Data []ItemExtNet `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about external network +type RecordExtNet struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // CheckIPs + CheckIPs []string `json:"checkIps"` + + // Default + Default bool `json:"default"` + + // Default QOS + DefaultQOS QOS `json:"defaultQos"` + + // Description + Description string `json:"desc"` + + // List DNS + DNS []string `json:"dns"` + + // List excludes + Excluded ListReservations `json:"excluded"` + + // Free IPs number + FreeIPs int64 `json:"free_ips"` + + // Gateway + Gateway string `json:"gateway"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IPCIDR + IPCIDR string `json:"ipcidr"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // Network ID + NetworkID uint64 `json:"networkId"` + + // OVSBridge + OVSBridge string `json:"ovsBridge"` + + // PreReservationsNum + PreReservationsNum uint64 `json:"preReservationsNum"` + + // Prefix + Prefix uint64 `json:"prefix"` + + // PriVNFDevID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // List reservations + Reservations ListReservations `json:"reservations"` + + // List of shared with + SharedWith []interface{} `json:"sharedWith"` + + // Status + Status string `json:"status"` + + // VLAN ID + VLANID uint64 `json:"vlanId"` + + // VNFs + VNFs VNFs `json:"vnfs"` +} + +// List of static routes +type ListStaticRoutes struct { + // Data + Data []ItemRoutes `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about Routes +type ItemRoutes struct { + //Compute Id + ComputeIds []uint64 `json:"computeIds"` + + // Destination network + Destination string `json:"destination"` + + //Next hop host, IP address from ViNS ID free IP pool + Gateway string `json:"gateway"` + + // GUID + GUID string `json:"guid"` + + // ID + ID uint64 `json:"id"` + + //Destination network mask in 255.255.255.255 format + Netmask string `json:"netmask"` +} diff --git a/pkg/cloudbroker/extnet/ntp_apply.go b/pkg/cloudbroker/extnet/ntp_apply.go new file mode 100644 index 0000000..a07484d --- /dev/null +++ b/pkg/cloudbroker/extnet/ntp_apply.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NTPApplyRequest struct for set new NTP +type NTPApplyRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // List of NTP to apply + // Required: false + NTPList []string `url:"ntp_list,omitempty" json:"ntp_list,omitempty"` +} + +// NTPApply sets new NTP +func (e ExtNet) NTPApply(ctx context.Context, req NTPApplyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/ntpApply" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/raise_down.go b/pkg/cloudbroker/extnet/raise_down.go new file mode 100644 index 0000000..fae1b19 --- /dev/null +++ b/pkg/cloudbroker/extnet/raise_down.go @@ -0,0 +1,24 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" +) + +// RaiseDown starting all extnets vnfdevs in "DOWN" tech status +func (e ExtNet) RaiseDown(ctx context.Context) (bool, error) { + url := "/cloudbroker/extnet/raiseDown" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/serialize.go b/pkg/cloudbroker/extnet/serialize.go new file mode 100644 index 0000000..8c00d57 --- /dev/null +++ b/pkg/cloudbroker/extnet/serialize.go @@ -0,0 +1,43 @@ +package extnet + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lenet ListExtNet) Serialize(params ...string) (serialization.Serialized, error) { + if len(lenet.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lenet, prefix, indent) + } + + return json.Marshal(lenet) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ienet ItemExtNet) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ienet, prefix, indent) + } + + return json.Marshal(ienet) +} diff --git a/pkg/cloudbroker/extnet/set_default.go b/pkg/cloudbroker/extnet/set_default.go new file mode 100644 index 0000000..e191551 --- /dev/null +++ b/pkg/cloudbroker/extnet/set_default.go @@ -0,0 +1,38 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetDefaultRequest struct to set external network as default +type SetDefaultRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` +} + +// SetDefault sets external network as default for platform +func (e ExtNet) SetDefault(ctx context.Context, req SetDefaultRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/setDefault" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/static_route_access_grant.go b/pkg/cloudbroker/extnet/static_route_access_grant.go new file mode 100644 index 0000000..da67952 --- /dev/null +++ b/pkg/cloudbroker/extnet/static_route_access_grant.go @@ -0,0 +1,46 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAccessGrantRequest struct to grant access to static route to Compute/ViNS +type StaticRouteAccessGrantRequest struct { + // ExtNet ID to grant access + // Required: true + ExtNetID uint64 `url:"extnetId" json:"extnetId" validate:"required"` + + // Route ID to grant access, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` + + // List of Compute IDs to grant access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAccessGrant grants access to static route to Compute/ViNS +func (v ExtNet) StaticRouteAccessGrant(ctx context.Context, req StaticRouteAccessGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/staticRouteAccessGrant" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/static_route_access_revoke.go b/pkg/cloudbroker/extnet/static_route_access_revoke.go new file mode 100644 index 0000000..a1fdf51 --- /dev/null +++ b/pkg/cloudbroker/extnet/static_route_access_revoke.go @@ -0,0 +1,46 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAccessRevokeRequest struct to revoke access to static route to Compute/ViNS +type StaticRouteAccessRevokeRequest struct { + // ExtNet ID to revoke access + // Required: true + ExtNetID uint64 `url:"extnetId" json:"extnetId" validate:"required"` + + // Route ID to revoke access, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` + + // List of Compute IDs to revoke access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAccessRevoke revokes access to static route to Compute/ViNS +func (v ExtNet) StaticRouteAccessRevoke(ctx context.Context, req StaticRouteAccessRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/staticRouteAccessRevoke" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/static_route_add.go b/pkg/cloudbroker/extnet/static_route_add.go new file mode 100644 index 0000000..f452603 --- /dev/null +++ b/pkg/cloudbroker/extnet/static_route_add.go @@ -0,0 +1,54 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAddRequest struct to add static route +type StaticRouteAddRequest struct { + // ExtNet ID to add static route + // Required: true + ExtNetId uint64 `url:"extnetId" json:"extnetId" validate:"required"` + + // Destination network + // Required: true + Destination string `url:"destination" json:"destination" validate:"required"` + + // Destination network mask in 255.255.255.255 format + // Required: true + Netmask string `url:"netmask" json:"netmask" validate:"required"` + + // Next hop host, IP address from ViNS ID free IP pool + // Required: true + Gateway string `url:"gateway" json:"gateway" validate:"required"` + + // List of Compute IDs which have access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAdd adds new static route to ViNS +func (v ExtNet) StaticRouteAdd(ctx context.Context, req StaticRouteAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/staticRouteAdd" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/static_route_del.go b/pkg/cloudbroker/extnet/static_route_del.go new file mode 100644 index 0000000..91dda28 --- /dev/null +++ b/pkg/cloudbroker/extnet/static_route_del.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteDelRequest struct to remove static route from ViNS +type StaticRouteDelRequest struct { + // ExtNet ID to remove static route from + // Required: true + ExtNetID uint64 `url:"extnetId" json:"extnetId" validate:"required"` + + // Route ID to remove, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` +} + +// StaticRouteDel removes static route from ViNS +func (v ExtNet) StaticRouteDel(ctx context.Context, req StaticRouteDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/staticRouteDel" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/extnet/static_route_list.go b/pkg/cloudbroker/extnet/static_route_list.go new file mode 100644 index 0000000..05db41a --- /dev/null +++ b/pkg/cloudbroker/extnet/static_route_list.go @@ -0,0 +1,40 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteListRequest struct for static route list +type StaticRouteListRequest struct { + // ExtNet ID to show list of static routes + // Required: true + ExtNetID uint64 `url:"extnetId" json:"extnetId" validate:"required"` +} + +// StaticRouteList shows list of static routes for ViNS +func (v ExtNet) StaticRouteList(ctx context.Context, req StaticRouteListRequest) (*ListStaticRoutes, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/staticRouteList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListStaticRoutes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/extnet/update.go b/pkg/cloudbroker/extnet/update.go new file mode 100644 index 0000000..98ac7e4 --- /dev/null +++ b/pkg/cloudbroker/extnet/update.go @@ -0,0 +1,46 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update external network +type UpdateRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // New external network name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New external network description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// Update updates external network parameters +func (e ExtNet) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/update" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/flipgoup.go b/pkg/cloudbroker/flipgoup.go new file mode 100644 index 0000000..54b164f --- /dev/null +++ b/pkg/cloudbroker/flipgoup.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/flipgroup" +) + +// Accessing the FLIPGroup method group +func (cb *CloudBroker) FLIPGroup() *flipgroup.FLIPGroup { + return flipgroup.New(cb.client) +} diff --git a/pkg/cloudbroker/flipgroup/compute_add.go b/pkg/cloudbroker/flipgroup/compute_add.go new file mode 100644 index 0000000..21c52e0 --- /dev/null +++ b/pkg/cloudbroker/flipgroup/compute_add.go @@ -0,0 +1,42 @@ +package flipgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ComputeAddRequest struct to add compute instance +type ComputeAddRequest struct { + // ID of the Floating IP group to add compute instance to + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` + + // ID of the compute instance to add to this group + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// ComputeAdd adds compute instance to the Floating IP group +func (f FLIPGroup) ComputeAdd(ctx context.Context, req ComputeAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/flipgroup/computeAdd" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/flipgroup/compute_remove.go b/pkg/cloudbroker/flipgroup/compute_remove.go new file mode 100644 index 0000000..3f4df03 --- /dev/null +++ b/pkg/cloudbroker/flipgroup/compute_remove.go @@ -0,0 +1,42 @@ +package flipgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ComputeRemoveRequest struct to remove compute instance +type ComputeRemoveRequest struct { + // ID of the Floating IP group to remove compute instance from + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` + + // ID of the compute instance to remove + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// ComputeRemove removes compute instance from the Floating IP group +func (f FLIPGroup) ComputeRemove(ctx context.Context, req ComputeRemoveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/flipgroup/computeRemove" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/flipgroup/create.go b/pkg/cloudbroker/flipgroup/create.go new file mode 100644 index 0000000..aadf6c0 --- /dev/null +++ b/pkg/cloudbroker/flipgroup/create.go @@ -0,0 +1,69 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create FLIPGroup +type CreateRequest struct { + // Account ID + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // FLIPGroup name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Network type + // Should be one of: + // - EXTNET + // - VINS + // Required: true + NetType string `url:"netType" json:"netType" validate:"computeNetType"` + + // ID of external network or VINS + // Required: true + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // Type of client + // - 'compute' + // - 'vins' (will be later) + // Required: true + ClientType string `url:"clientType" json:"clientType" validate:"flipgroupClientType"` + + // IP address to associate with this group. If empty, the platform will autoselect IP address + // Required: false + IP string `url:"ip,omitempty" json:"ip,omitempty"` + + // Text description of this FLIPGorup instance + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// Create method will create a new FLIPGorup in the specified Account +func (f FLIPGroup) Create(ctx context.Context, req CreateRequest) (*RecordFLIPGroupCreated, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/flipgroup/create" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordFLIPGroupCreated{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/flipgroup/delete.go b/pkg/cloudbroker/flipgroup/delete.go new file mode 100644 index 0000000..6ae9176 --- /dev/null +++ b/pkg/cloudbroker/flipgroup/delete.go @@ -0,0 +1,38 @@ +package flipgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete FLIPGroup +type DeleteRequest struct { + // FLIPGroup ID + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` +} + +// Delete method wil delete Floating IP group +func (f FLIPGroup) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/flipgroup/delete" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/flipgroup/edit.go b/pkg/cloudbroker/flipgroup/edit.go new file mode 100644 index 0000000..4348f15 --- /dev/null +++ b/pkg/cloudbroker/flipgroup/edit.go @@ -0,0 +1,46 @@ +package flipgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EditRequest struct to edit FLIPGroup +type EditRequest struct { + // FLIPGroup ID + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` + + // FLIPGroup name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // FLIPGroup description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// Edit edits FLIPGroup fields +func (f FLIPGroup) Edit(ctx context.Context, req EditRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/flipgroup/edit" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/flipgroup/flipgroup.go b/pkg/cloudbroker/flipgroup/flipgroup.go new file mode 100644 index 0000000..0f65870 --- /dev/null +++ b/pkg/cloudbroker/flipgroup/flipgroup.go @@ -0,0 +1,18 @@ +// API to manage FLIPGroup instances +package flipgroup + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to FLIPGroup +type FLIPGroup struct { + client interfaces.Caller +} + +// Builder for FLIPGroup endpoints +func New(client interfaces.Caller) *FLIPGroup { + return &FLIPGroup{ + client: client, + } +} diff --git a/pkg/cloudbroker/flipgroup/get.go b/pkg/cloudbroker/flipgroup/get.go new file mode 100644 index 0000000..fa0c6b8 --- /dev/null +++ b/pkg/cloudbroker/flipgroup/get.go @@ -0,0 +1,46 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about FLIPGroup +type GetRequest struct { + // FLIPGroup ID + // Required: true + FLIPGroupID uint64 `url:"flipgroupId" json:"flipgroupId" validate:"required"` +} + +// Get gets details of the specified Floating IP group as a RecordFLIPGroup struct +func (f FLIPGroup) Get(ctx context.Context, req GetRequest) (*RecordFLIPGroup, error) { + res, err := f.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordFLIPGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets details of the specified Floating IP group as an array of bytes +func (f FLIPGroup) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/flipgroup/get" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/flipgroup/ids.go b/pkg/cloudbroker/flipgroup/ids.go new file mode 100644 index 0000000..c326dcd --- /dev/null +++ b/pkg/cloudbroker/flipgroup/ids.go @@ -0,0 +1,10 @@ +package flipgroup + +// IDs gets array of FLIPGroupIDs from ListFLIPGroups struct +func (le ListFLIPGroups) IDs() []uint64 { + res := make([]uint64, 0, len(le.Data)) + for _, e := range le.Data { + res = append(res, e.ID) + } + return res +} diff --git a/pkg/cloudbroker/flipgroup/list.go b/pkg/cloudbroker/flipgroup/list.go new file mode 100644 index 0000000..ee3fa1b --- /dev/null +++ b/pkg/cloudbroker/flipgroup/list.go @@ -0,0 +1,103 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of FLIPGroup available to the current user +type ListRequest struct { + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by vinsId + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Find by VINS name + // Required: false + VINSName string `url:"vinsName,omitempty" json:"vinsName,omitempty"` + + // Find by extnetId + // Required: false + ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"` + + // Find by IP + // Required: false + ByIP string `url:"byIp,omitempty" json:"byIp,omitempty"` + + // Find by accountId + // Required: false + AccountId uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by list of clientIds + // Required: false + ClientIDs []uint64 `url:"clientIds,omitempty" json:"clientIds,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by connId + // Required: false + ConnId uint64 `url:"connId,omitempty" json:"connId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` +} + +// List gets list of FLIPGroup managed cluster instances available to the current user as a ListFLIPGroups struct +func (f FLIPGroup) List(ctx context.Context, req ListRequest) (*ListFLIPGroups, error) { + + res, err := f.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListFLIPGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of FLIPGroup managed cluster instances available to the current user as an array of bytes +func (f FLIPGroup) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/flipgroup/list" + + res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/flipgroup/models.go b/pkg/cloudbroker/flipgroup/models.go new file mode 100644 index 0000000..904536c --- /dev/null +++ b/pkg/cloudbroker/flipgroup/models.go @@ -0,0 +1,169 @@ +package flipgroup + +// Main information about FLIPGroup +type RecordFLIPGroupCreated struct { + // Default GW + DefaultGW string `json:"defaultGW"` + + // ID + ID uint64 `json:"id"` + + // IP + IP string `json:"ip"` + + // Name + Name string `json:"name"` + + // Network mask + NetMask uint64 `json:"netmask"` +} + +type RecordFLIPGroup struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // List of client IDs + ClientIDs []uint64 `json:"clientIds"` + + // Client names + ClientNames []string `json:"clientNames"` + + // Client type + ClientType string `json:"clientType"` + + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IP + IP string `json:"ip"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network type + NetType string `json:"netType"` + + // Network + Network string `json:"network"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// Detailed information about FLIPGroup +type ItemFLIPGroup struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // List of client IDs + ClientIDs []uint64 `json:"clientIds"` + + // Client type + ClientType string `json:"clientType"` + + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // IP + IP string `json:"ip"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network ID + NetID uint64 `json:"netId"` + + // Network type + NetType string `json:"netType"` + + // NetMask + NetMask uint64 `json:"netmask"` + + // Status + Status string `json:"status"` +} + +// List of FLIPGroup +type ListFLIPGroups struct { + Data []ItemFLIPGroup `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/flipgroup/serialize.go b/pkg/cloudbroker/flipgroup/serialize.go new file mode 100644 index 0000000..ee1ad19 --- /dev/null +++ b/pkg/cloudbroker/flipgroup/serialize.go @@ -0,0 +1,43 @@ +package flipgroup + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lfg ListFLIPGroups) Serialize(params ...string) (serialization.Serialized, error) { + if len(lfg.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lfg, prefix, indent) + } + + return json.Marshal(lfg) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ifg ItemFLIPGroup) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ifg, prefix, indent) + } + + return json.Marshal(ifg) +} diff --git a/pkg/cloudbroker/grid.go b/pkg/cloudbroker/grid.go new file mode 100644 index 0000000..061e4ec --- /dev/null +++ b/pkg/cloudbroker/grid.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/grid" + +// Accessing the Grid method group +func (cb *CloudBroker) Grid() *grid.Grid { + return grid.New(cb.client) +} diff --git a/pkg/cloudbroker/grid/add.go b/pkg/cloudbroker/grid/add.go new file mode 100644 index 0000000..7d05c4f --- /dev/null +++ b/pkg/cloudbroker/grid/add.go @@ -0,0 +1,46 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AddRequest struct for location code +type AddRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Name of the location + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Location code typicly used in dns names + // Required: true + LocationCode string `url:"locationcode" json:"locationcode" validate:"required"` +} + +// Add location code (e.g. DNS name of this grid) +func (g Grid) Add(ctx context.Context, req AddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/add" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/add_custom_backup_path.go b/pkg/cloudbroker/grid/add_custom_backup_path.go new file mode 100644 index 0000000..ca00efd --- /dev/null +++ b/pkg/cloudbroker/grid/add_custom_backup_path.go @@ -0,0 +1,42 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AddCustomBackupPathRequest struct to add new path to the list of custom backup paths +type AddCustomBackupPathRequest struct { + // ID of the grid + // Required: true + GID uint64 `url:"gridId" json:"gridId" validate:"required"` + + // Absolute path + // Required: true + Path string `url:"path" json:"path" validate:"required"` +} + +// AddCustomBackupPath add new path to the list of custom backup paths +func (g Grid) AddCustomBackupPath(ctx context.Context, req AddCustomBackupPathRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/addCustomBackupPath" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/change_settings.go b/pkg/cloudbroker/grid/change_settings.go new file mode 100644 index 0000000..444864e --- /dev/null +++ b/pkg/cloudbroker/grid/change_settings.go @@ -0,0 +1,42 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ChangeSettingsRequest struct to change grid settings +type ChangeSettingsRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"id" json:"id" validate:"required"` + + // Json data of the new settings will override old data + // Required: true + Settings string `url:"settings" json:"settings" validate:"required"` +} + +// ChangeSettings changes grid settings +func (g Grid) ChangeSettings(ctx context.Context, req ChangeSettingsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/changeSettings" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/execute_maintenance_script.go b/pkg/cloudbroker/grid/execute_maintenance_script.go new file mode 100644 index 0000000..f0ed52f --- /dev/null +++ b/pkg/cloudbroker/grid/execute_maintenance_script.go @@ -0,0 +1,46 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ExecuteMaintenanceScriptRequest struct to execute script +type ExecuteMaintenanceScriptRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Type of nodes you want to apply the action on + // Required: true + NodesType string `url:"nodestype" json:"nodestype" validate:"required"` + + // The script you want to run + // Required: true + Script string `url:"script" json:"script" validate:"required"` +} + +// ExecuteMaintenanceScript executes maintenance script +func (g Grid) ExecuteMaintenanceScript(ctx context.Context, req ExecuteMaintenanceScriptRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/executeMaintenanceScript" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/filter.go b/pkg/cloudbroker/grid/filter.go new file mode 100644 index 0000000..8b80e14 --- /dev/null +++ b/pkg/cloudbroker/grid/filter.go @@ -0,0 +1,62 @@ +package grid + +// FilterByID returns ListGrids with specified ID. +func (lg ListGrids) FilterByID(id uint64) ListGrids { + predicate := func(rg ItemGridList) bool { + return rg.ID == id + } + + return lg.FilterFunc(predicate) +} + +// FilterByGID returns ListGrids with specified GID. +func (lg ListGrids) FilterByGID(gid uint64) ListGrids { + predicate := func(rg ItemGridList) bool { + return rg.GID == gid + } + + return lg.FilterFunc(predicate) +} + +// FilterByName returns ListGrids with specified Name. +func (lg ListGrids) FilterByName(name string) ListGrids { + predicate := func(rg ItemGridList) bool { + return rg.Name == name + } + + return lg.FilterFunc(predicate) +} + +// FilterByLocationCode returns ListGrids with specified LocationCode. +func (lg ListGrids) FilterByLocationCode(locationCode string) ListGrids { + predicate := func(rg ItemGridList) bool { + return rg.LocationCode == locationCode + } + + return lg.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListGrids based on a user-specified predicate. +func (lg ListGrids) FilterFunc(predicate func(ItemGridList) bool) ListGrids { + var result ListGrids + + for _, item := range lg.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found RecordGrid. +// If none was found, returns an empty struct. +func (lg ListGrids) FindOne() ItemGridList { + if len(lg.Data) == 0 { + return ItemGridList{} + } + + return lg.Data[0] +} diff --git a/pkg/cloudbroker/grid/filter_test.go b/pkg/cloudbroker/grid/filter_test.go new file mode 100644 index 0000000..f69a387 --- /dev/null +++ b/pkg/cloudbroker/grid/filter_test.go @@ -0,0 +1,142 @@ +package grid + +import "testing" + +var grids = ListGrids{ + Data: []ItemGridList{ + { + Resources: Resources{ + Current: RecordResource{ + CPU: 84, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + ExtTraffic: 0, + GPU: 79500, + RAM: 0, + SEPs: map[string]map[string]DiskUsage{}, + }, + Reserved: RecordResource{ + CPU: 123, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + ExtTraffic: 0, + GPU: 0, + RAM: 152600, + SEPs: map[string]map[string]DiskUsage{}, + }, + }, + Flag: "", + GID: 212, + GUID: 1, + ID: 1, + LocationCode: "alfa", + Name: "alfa", + }, + { + Resources: Resources{ + Current: RecordResource{ + CPU: 84, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + ExtTraffic: 0, + GPU: 79500, + RAM: 0, + SEPs: map[string]map[string]DiskUsage{}, + }, + Reserved: RecordResource{ + CPU: 123, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + ExtTraffic: 0, + GPU: 0, + RAM: 152600, + SEPs: map[string]map[string]DiskUsage{}, + }, + }, + Flag: "", + GID: 666, + GUID: 2, + ID: 2, + LocationCode: "beta", + Name: "beta", + }, + { + Resources: Resources{ + Current: RecordResource{ + CPU: 84, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + ExtTraffic: 0, + GPU: 79500, + RAM: 0, + SEPs: map[string]map[string]DiskUsage{}, + }, + Reserved: RecordResource{ + CPU: 123, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + ExtTraffic: 0, + GPU: 0, + RAM: 152600, + SEPs: map[string]map[string]DiskUsage{}, + }, + }, + Flag: "", + GID: 777, + GUID: 3, + ID: 3, + LocationCode: "gamma", + Name: "gamma", + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := grids.FilterByID(2).FindOne() + + if actual.ID != 2 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByGID(t *testing.T) { + actual := grids.FilterByGID(777).FindOne() + + if actual.GID != 777 { + t.Fatal("expected ID 777, found: ", actual.GID) + } +} + +func TestFilterByName(t *testing.T) { + actual := grids.FilterByName("gamma").FindOne() + + if actual.Name != "gamma" { + t.Fatal("expected Name 'gamma', found: ", actual.Name) + } +} + +func TestFilterByLocationCode(t *testing.T) { + actual := grids.FilterByLocationCode("alfa").FindOne() + + if actual.LocationCode != "alfa" { + t.Fatal("expected LocationCode 'alfa', found: ", actual.LocationCode) + } +} + +func TestFilterFunc(t *testing.T) { + actual := grids.FilterFunc(func(rg ItemGridList) bool { + return rg.GID == 777 + }). + FindOne() + + if actual.GID != 777 { + t.Fatal("expected GID 777, found: ", actual.GID) + } +} diff --git a/pkg/cloudbroker/grid/get.go b/pkg/cloudbroker/grid/get.go new file mode 100644 index 0000000..9445cc7 --- /dev/null +++ b/pkg/cloudbroker/grid/get.go @@ -0,0 +1,46 @@ +package grid + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get grid details +type GetRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gridId" json:"gridId" validate:"required"` +} + +// Get gets information about grid by ID as a RecordGrid struct +func (g Grid) Get(ctx context.Context, req GetRequest) (*RecordGrid, error) { + res, err := g.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordGrid{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about grid by ID as an array of bytes +func (g Grid) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/get" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/grid/get_backup.go b/pkg/cloudbroker/grid/get_backup.go new file mode 100644 index 0000000..bb447fb --- /dev/null +++ b/pkg/cloudbroker/grid/get_backup.go @@ -0,0 +1,49 @@ +package grid + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetBackupRequest struct to get backup +type GetBackupRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` +} + +// GetBackup gets platform backup +func (g Grid) GetBackup(ctx context.Context, req GetBackupRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/getBackup" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} + +// GetBackupGET gets platform backup +func (g Grid) GetBackupGET(ctx context.Context, req GetBackupRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/getBackup" + + res, err := g.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/grid/get_diagnosis.go b/pkg/cloudbroker/grid/get_diagnosis.go new file mode 100644 index 0000000..4817579 --- /dev/null +++ b/pkg/cloudbroker/grid/get_diagnosis.go @@ -0,0 +1,50 @@ +package grid + +import ( + "context" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetDiagnosisRequest struct to get platform snapshot with additional diagnosis +type GetDiagnosisRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` +} + +// GetDiagnosis gets platform snapshot with additional diagnosis info like a logs, etc +func (g Grid) GetDiagnosis(ctx context.Context, req GetDiagnosisRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/getDiagnosis" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} + +// GetDiagnosisGET gets platform snapshot with additional diagnosis info like a logs, etc +func (g Grid) GetDiagnosisGET(ctx context.Context, req GetDiagnosisRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := fmt.Sprintf("/cloudbroker/grid/getDiagnosis/?gid=%d", req.GID) + + res, err := g.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/grid/get_resource_consumption.go b/pkg/cloudbroker/grid/get_resource_consumption.go new file mode 100644 index 0000000..e5948f0 --- /dev/null +++ b/pkg/cloudbroker/grid/get_resource_consumption.go @@ -0,0 +1,40 @@ +package grid + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetResourceConsumptionRequest struct to get resource consumption +type GetResourceConsumptionRequest struct { + // ID of the grid + // Required: true + GridID uint64 `url:"gridId" json:"gridId" validate:"required"` +} + +// GetResourceConsumption gets resource consumption +func (g Grid) GetResourceConsumption(ctx context.Context, req GetResourceConsumptionRequest) (*RecordResourcesConsumption, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/getResourceConsumption" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + result := RecordResourcesConsumption{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return &result, nil +} diff --git a/pkg/cloudbroker/grid/get_settings.go b/pkg/cloudbroker/grid/get_settings.go new file mode 100644 index 0000000..1605508 --- /dev/null +++ b/pkg/cloudbroker/grid/get_settings.go @@ -0,0 +1,46 @@ +package grid + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetSettingsRequest struct to get grid settings +type GetSettingsRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"grid_id" json:"grid_id" validate:"required"` +} + +// GetSettings gets settings grid by ID as a RecordSettingsGrid struct +func (g Grid) GetSettings(ctx context.Context, req GetSettingsRequest) (*RecordSettingsGrid, error) { + res, err := g.GetSettingsRaw(ctx, req) + if err != nil { + return nil, err + } + + settings := RecordSettingsGrid{} + + err = json.Unmarshal(res, &settings) + if err != nil { + return nil, err + } + + return &settings, nil +} + +// GetSettingsRaw gets settings grid by ID as an array of bytes +func (g Grid) GetSettingsRaw(ctx context.Context, req GetSettingsRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/getSettings" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/grid/grid.go b/pkg/cloudbroker/grid/grid.go new file mode 100644 index 0000000..545ca0a --- /dev/null +++ b/pkg/cloudbroker/grid/grid.go @@ -0,0 +1,18 @@ +// Operator actions for handling interventions on a grid +package grid + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to grid +type Grid struct { + client interfaces.Caller +} + +// Builder for grid endpoints +func New(client interfaces.Caller) *Grid { + return &Grid{ + client: client, + } +} diff --git a/pkg/cloudbroker/grid/ids.go b/pkg/cloudbroker/grid/ids.go new file mode 100644 index 0000000..1f391cf --- /dev/null +++ b/pkg/cloudbroker/grid/ids.go @@ -0,0 +1,19 @@ +package grid + +// IDs gets array of GRIDID from ListGrids struct +func (lg ListGrids) IDs() []uint64 { + res := make([]uint64, 0, len(lg.Data)) + for _, e := range lg.Data { + res = append(res, e.GID) + } + return res +} + +// IDs gets array of GRIDID from ListResourceConsumption struct +func (lg ListResourceConsumption) IDs() []uint64 { + res := make([]uint64, 0, len(lg.Data)) + for _, e := range lg.Data { + res = append(res, e.GID) + } + return res +} \ No newline at end of file diff --git a/pkg/cloudbroker/grid/list.go b/pkg/cloudbroker/grid/list.go new file mode 100644 index 0000000..e493ece --- /dev/null +++ b/pkg/cloudbroker/grid/list.go @@ -0,0 +1,63 @@ +package grid + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of locations +type ListRequest struct { + // Find by id grid + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name grid + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all locations as a ListGrids struct +func (g Grid) List(ctx context.Context, req ListRequest) (*ListGrids, error) { + + res, err := g.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListGrids{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all locations as an array of bytes +func (g Grid) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/list" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/grid/list_emails.go b/pkg/cloudbroker/grid/list_emails.go new file mode 100644 index 0000000..a1f253b --- /dev/null +++ b/pkg/cloudbroker/grid/list_emails.go @@ -0,0 +1,37 @@ +package grid + +import ( + "context" + "encoding/json" + "net/http" +) + +// ListEmailsRequest struct for getting list of email addresses of users +type ListEmailsRequest struct { + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListEmails returns list of email addresses of users +func (g Grid) ListEmails(ctx context.Context, req ListEmailsRequest) (*ListEmails, error) { + url := "/cloudbroker/grid/listEmails" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListEmails{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/grid/list_resource_consumption.go b/pkg/cloudbroker/grid/list_resource_consumption.go new file mode 100644 index 0000000..0891f19 --- /dev/null +++ b/pkg/cloudbroker/grid/list_resource_consumption.go @@ -0,0 +1,25 @@ +package grid + +import ( + "context" + "encoding/json" + "net/http" +) + +func (g Grid) ListResourceConsumption(ctx context.Context) (*ListResourceConsumption, error) { + url := "/cloudbroker/grid/listResourceConsumption" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + result := ListResourceConsumption{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return &result, nil +} diff --git a/pkg/cloudbroker/grid/models.go b/pkg/cloudbroker/grid/models.go new file mode 100644 index 0000000..1e78e81 --- /dev/null +++ b/pkg/cloudbroker/grid/models.go @@ -0,0 +1,239 @@ +package grid + +// Resource information +type Resources struct { + // Current resources + Current RecordResource `json:"Current"` + + // Reserved resources + Reserved RecordResource `json:"Reserved"` +} + +// Resource consumption information +type RecordResourcesConsumption struct { + // Current resources + Consumed RecordResource `json:"Consumed"` + + // Reserved resources + Reserved RecordResource `json:"Reserved"` + + // GID + GID uint64 `json:"id"` +} + +type ListResourceConsumption struct { + // Data + Data []RecordResourcesConsumption `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Resource details +type RecordResource struct { + // Number of CPU + CPU uint64 `json:"cpu"` + + // Disk size + DiskSize float64 `json:"disksize"` + + // Disk size max + DiskSizeMax int64 `json:"disksizemax"` + + // External IPs + ExtIPs uint64 `json:"extips"` + + // External traffic + ExtTraffic uint64 `json:"exttraffic"` + + // Number of GPU + GPU uint64 `json:"gpu"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +// Disk usage +type DiskUsage struct { + // Disk size + DiskSize float64 `json:"disksize"` + + // Disk size max + DiskSizeMax float64 `json:"disksizemax"` +} + +// Detailed information about grid +type RecordGrid struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // AuthBroker + AuthBroker []interface{} `json:"authBroker"` + + // Flag + Flag string `json:"flag"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Location code + LocationCode string `json:"locationCode"` + + // Name + Name string `json:"name"` +} + +// Information about grid +type ItemGridList struct { + // Resource information + Resources Resources `json:"Resources"` + + // AuthBroker + AuthBroker []interface{} `json:"authBroker"` + + // Flag + Flag string `json:"flag"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Location code + LocationCode string `json:"locationCode"` + + // Name + Name string `json:"name"` +} + +// List Grids +type ListGrids struct { + //Data + Data []ItemGridList `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// List emails +type ListEmails struct { + //Data + Data []string `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about grid settings +type RecordSettingsGrid struct { + //Allowed ports + Allowedports []int `json:"allowedports"` + + //Cleanup retention period + CleanupRetentionPeriod uint64 `json:"cleanupRetentionPeriod"` + + //Docker registry + DockerRegistry DockerRegistry `json:"docker_registry"` + + //Enable uptime monitor + EnableUptimeMonitor bool `json:"enableUptimeMonitor"` + + //Extnet max prereservation num + ExtnetMaxPreReservationsNum int `json:"extnetMaxPreReservationsNum"` + + //Healthcheck notifications + HealthcheckNotifications HealthcheckNotifications `json:"healthcheck_notifications"` + + //k8s cleanup enabled + K8sCleanupEnabled bool `json:"k8s_cleanup_enabled"` + + //Limits + Limits interface{} `json:"limits"` + + //Location url + LocationURL string `json:"location_url"` + + //Net QOS + NetQOS NetQOS `json:"net_qos"` + + //Networks + Networks string `json:"networks"` + + //Prometheus + Prometheus Prometheus `json:"prometheus"` + + //Vins max prereservation num + VinsMaxPreReservationsNum int `json:"vinsMaxPreReservationsNum"` + + //Vnfdev mgmt net range + VnfdevMgmtNetRange string `json:"vnfdev_mgmt_net_range"` +} + +// DockerRegistry in grid settings +type DockerRegistry struct { + //Password + Password string `json:"password"` + + //Server + Server string `json:"server"` + + //Username + Username string `json:"username"` +} + +// NetQOS in grid settings +type NetQOS struct { + // ExtNet Net QOS settings + ExtNet SettingsNetQOS `json:"ext_net"` + + // VINS Net QOS settings + VINS SettingsNetQOS `json:"vins"` +} + +// SettingsNetQOS in grid settings +type SettingsNetQOS struct { + //ERate + ERate uint64 `json:"eRate"` + + //InBurst + InBurst uint64 `json:"inBurst"` + + //InRate + InRate uint64 `json:"inRate"` +} + +// HealthcheckNotifications settings in grid +type HealthcheckNotifications struct { + //Emails + Emails []Emails `json:"emails"` +} + +type Emails struct { + //Address + Address string `json:"address"` + + //Enabled + Enabled bool `json:"enabled"` +} + +// Prometheus setting in grid +type Prometheus struct { + //ScrapeInterval + ScrapeInterval int `json:"scrapeInterval"` +} diff --git a/pkg/cloudbroker/grid/purge_logs.go b/pkg/cloudbroker/grid/purge_logs.go new file mode 100644 index 0000000..10d39a9 --- /dev/null +++ b/pkg/cloudbroker/grid/purge_logs.go @@ -0,0 +1,43 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// PurgeLogsRequest struct to purge logs +type PurgeLogsRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Age of the records to remove, e.g. -1h for records older than 1 hour, -1w - one week, etc + // Required: true + Age string `url:"age" json:"age" validate:"required"` +} + +// PurgeLogs clear Log and ECO records that are older than the specified age. +// By default, records older than one week are removed +func (g Grid) PurgeLogs(ctx context.Context, req PurgeLogsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/purgeLogs" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/remove_custom_backup_path.go b/pkg/cloudbroker/grid/remove_custom_backup_path.go new file mode 100644 index 0000000..bac04a1 --- /dev/null +++ b/pkg/cloudbroker/grid/remove_custom_backup_path.go @@ -0,0 +1,42 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RemoveCustomBackupPathRequest struct to remove path from the list of custom backup paths +type RemoveCustomBackupPathRequest struct { + // ID of the grid + // Required: true + GID uint64 `url:"gridId" json:"gridId" validate:"required"` + + // Absolute path + // Required: true + Path string `url:"path" json:"path" validate:"required"` +} + +// RemoveCustomBackupPath remove path from the list of custom backup paths +func (g Grid) RemoveCustomBackupPath(ctx context.Context, req RemoveCustomBackupPathRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/removeCustomBackupPath" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/rename.go b/pkg/cloudbroker/grid/rename.go new file mode 100644 index 0000000..8e645e0 --- /dev/null +++ b/pkg/cloudbroker/grid/rename.go @@ -0,0 +1,42 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RenameRequest struct to rename grid +type RenameRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // New name + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +// Rename renames a grid +func (g Grid) Rename(ctx context.Context, req RenameRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/rename" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/serialize.go b/pkg/cloudbroker/grid/serialize.go new file mode 100644 index 0000000..e62b328 --- /dev/null +++ b/pkg/cloudbroker/grid/serialize.go @@ -0,0 +1,43 @@ +package grid + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lg ListGrids) Serialize(params ...string) (serialization.Serialized, error) { + if len(lg.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lg, prefix, indent) + } + + return json.Marshal(lg) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (rg RecordGrid) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(rg, prefix, indent) + } + + return json.Marshal(rg) +} diff --git a/pkg/cloudbroker/grid/services_restart.go b/pkg/cloudbroker/grid/services_restart.go new file mode 100644 index 0000000..4ee691f --- /dev/null +++ b/pkg/cloudbroker/grid/services_restart.go @@ -0,0 +1,42 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ServicesRestartRequest struct to restart services +type ServicesRestartRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` +} + +// ServicesRestart restarts decort services on the node +func (g Grid) ServicesRestart(ctx context.Context, req ServicesRestartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/servicesRestart" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/set_cpu_allocation_parameter.go b/pkg/cloudbroker/grid/set_cpu_allocation_parameter.go new file mode 100644 index 0000000..16e5cf6 --- /dev/null +++ b/pkg/cloudbroker/grid/set_cpu_allocation_parameter.go @@ -0,0 +1,44 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCPUAllocationParameterRequest for setting CPU allocation parameter +type SetCPUAllocationParameterRequest struct { + // Grid ID + // Required: true + GridID uint64 `url:"gridId" json:"gridId" validate:"required"` + + // CPU allocation parameter. + // If "strict" VM can't be run if not enough CPU resources. + // "loose" allow running VM if not enough resources. + // Required: true + StrictLoose string `url:"strict_loose" json:"strict_loose" validate:"required,strict_loose"` +} + +// SetCPUAllocationParameter sets CPU allocation parameter +func (g Grid) SetCPUAllocationParameter(ctx context.Context, req SetCPUAllocationParameterRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/setCpuAllocationParameter" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/set_cpu_allocation_ratio.go b/pkg/cloudbroker/grid/set_cpu_allocation_ratio.go new file mode 100644 index 0000000..0039345 --- /dev/null +++ b/pkg/cloudbroker/grid/set_cpu_allocation_ratio.go @@ -0,0 +1,42 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCPUAllocationRatioRequest struct to set allocation +type SetCPUAllocationRatioRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gridId" json:"gridId" validate:"required"` + + // Allocation ratio + // Required: true + Ratio float64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetCPUAllocationRatio sets CPU allocation ratio +func (g Grid) SetCPUAllocationRatio(ctx context.Context, req SetCPUAllocationRatioRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/setCpuAllocationRatio" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/set_cpu_allocation_ratio_for_vm.go b/pkg/cloudbroker/grid/set_cpu_allocation_ratio_for_vm.go new file mode 100644 index 0000000..fb745d5 --- /dev/null +++ b/pkg/cloudbroker/grid/set_cpu_allocation_ratio_for_vm.go @@ -0,0 +1,42 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCPUAllocationRatioForVMRequest for setting CPU allocation ratio for computes +type SetCPUAllocationRatioForVMRequest struct { + // Grid ID + // Required: true + GridID uint64 `url:"gridId" json:"gridId" validate:"required"` + + // Default CPU allocation ratio for computes + // Required: true + Ratio float64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetCPUAllocationRatioForVM sets CPU allocation ratio for computes +func (g Grid) SetCPUAllocationRatioForVM(ctx context.Context, req SetCPUAllocationRatioForVMRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/setCpuAllocationRatioForVM" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/set_mem_allocation_ratio.go b/pkg/cloudbroker/grid/set_mem_allocation_ratio.go new file mode 100644 index 0000000..2184506 --- /dev/null +++ b/pkg/cloudbroker/grid/set_mem_allocation_ratio.go @@ -0,0 +1,42 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetMemAllocationRatioRequest struct to set memory allocation +type SetMemAllocationRatioRequest struct { + // Grid (platform) ID + // Required: true + GID uint64 `url:"gridId" json:"gridId" validate:"required"` + + // Allocation ratio + // Required: true + Ratio float64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetMemAllocationRatio sets memory allocation ratio +func (g Grid) SetMemAllocationRatio(ctx context.Context, req SetMemAllocationRatioRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/grid/setMemAllocationRatio" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/set_password_policy.go b/pkg/cloudbroker/grid/set_password_policy.go new file mode 100644 index 0000000..99a6d88 --- /dev/null +++ b/pkg/cloudbroker/grid/set_password_policy.go @@ -0,0 +1,70 @@ +package grid + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetPasswordPolicyRequest struct to set password policy for a grid +type SetPasswordPolicyRequest struct { + // ID of the grid + // Required: true + GID uint64 `url:"gridId" json:"gridId" validate:"required"` + + // Available numbers in the password + // Default value : true + // Required: true + Digits interface{} `url:"digits" json:"digits" validate:"isBool"` + + // Available special characters in the password + // Default value : false + // Required: true + SpecialSymbols bool `url:"specialSymbols" json:"specialSymbols"` + + // Number of characters in the password + // Default value : 9 + // Required: true + PasswordLength uint64 `url:"passwordLength" json:"passwordLength"` + + // Capital letters in the password are available + // Default value : true + // Required: true + Uppercase interface{} `url:"uppercase" json:"uppercase" validate:"isBool"` +} + +// RemoveCustomBackupPath set set password policy for a grid +func (g Grid) SetPasswordPolicy(ctx context.Context, req SetPasswordPolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + if req.PasswordLength == 0 { + req.PasswordLength = 9 + } + + if req.Digits == nil { + req.Digits = true + } + + if req.Uppercase == nil { + req.Uppercase = true + } + + url := "/cloudbroker/grid/setPasswordPolicy" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/grid/status.go b/pkg/cloudbroker/grid/status.go new file mode 100644 index 0000000..deea0fb --- /dev/null +++ b/pkg/cloudbroker/grid/status.go @@ -0,0 +1,41 @@ +package grid + +import ( + "context" + "net/http" + "strconv" +) + +// Status check if current environment is active +func (g Grid) Status(ctx context.Context) (bool, error) { + url := "/cloudbroker/grid/status" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// StatusGET check if current environment is active +func (g Grid) StatusGET(ctx context.Context) (bool, error) { + url := "/cloudbroker/grid/status" + + res, err := g.client.DecortApiCall(ctx, http.MethodGet, url, nil) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/group.go b/pkg/cloudbroker/group.go new file mode 100644 index 0000000..19550c2 --- /dev/null +++ b/pkg/cloudbroker/group.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/group" + +// Accessing the Group method group +func (cb *CloudBroker) Group() *group.Group { + return group.New(cb.client) +} diff --git a/pkg/cloudbroker/group/get.go b/pkg/cloudbroker/group/get.go new file mode 100644 index 0000000..36aca81 --- /dev/null +++ b/pkg/cloudbroker/group/get.go @@ -0,0 +1,46 @@ +package group + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get details of the specified group. +type GetRequest struct { + // Group ID + // Required: true + GroupID string `url:"groupId" json:"groupId" validate:"required"` +} + +// Get gets details of the specified group as an ItemGroup struct +func (g Group) Get(ctx context.Context, req GetRequest) (*ItemGroup, error) { + res, err := g.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ItemGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets details of the specified group as an array of bytes +func (g Group) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/group/get" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/group/group.go b/pkg/cloudbroker/group/group.go new file mode 100644 index 0000000..02e2e01 --- /dev/null +++ b/pkg/cloudbroker/group/group.go @@ -0,0 +1,15 @@ +package group + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to group +type Group struct { + client interfaces.Caller +} + +// Builder for group endpoints +func New(client interfaces.Caller) *Group { + return &Group{ + client: client, + } +} diff --git a/pkg/cloudbroker/group/list.go b/pkg/cloudbroker/group/list.go new file mode 100644 index 0000000..bef5b25 --- /dev/null +++ b/pkg/cloudbroker/group/list.go @@ -0,0 +1,68 @@ +package group + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of group instances. +type ListRequest struct { + // Find by id. + // Requires: false + ByID string `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by list users. + // Required: false + User string `url:"user,omitempty" json:"user,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number. + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size, maximum - 100. + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by active True or False. + // Default: true + // Required: false + Active bool `url:"active" json:"active"` +} + +// List gets list of group instances as a ListGroups struct +func (g Group) List(ctx context.Context, req ListRequest) (*ListGroups, error) { + + res, err := g.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ListGroups{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// ListRaw gets list of group instances as an array of bytes +func (g Group) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/group/list" + + res, err := g.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/group/models.go b/pkg/cloudbroker/group/models.go new file mode 100644 index 0000000..5096eed --- /dev/null +++ b/pkg/cloudbroker/group/models.go @@ -0,0 +1,42 @@ +package group + +type ItemGroup struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"meta"` + + // Is active + Actice bool `json:"active"` + + // Description + Description string `json:"description"` + + // Domain + Domain string `json:"domain"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID string `json:"guid"` + + // ID + ID string + + // Last check + LastCheck uint64 `json:"lastcheck"` + + // Roles + Roles []interface{} `json:"roles"` + + // Users + Users []string `json:"users"` +} + +type ListGroups struct { + Data []ItemGroup `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/image.go b/pkg/cloudbroker/image.go new file mode 100644 index 0000000..b570f99 --- /dev/null +++ b/pkg/cloudbroker/image.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/image" +) + +// Accessing the Image method group +func (cb *CloudBroker) Image() *image.Image { + return image.New(cb.client) +} diff --git a/pkg/cloudbroker/image/computeci_set.go b/pkg/cloudbroker/image/computeci_set.go new file mode 100644 index 0000000..ba1ff39 --- /dev/null +++ b/pkg/cloudbroker/image/computeci_set.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ComputeCISetRequest struct to set compute CI +type ComputeCISetRequest struct { + // ID of the image + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // ID of the compute CI + // Required: true + ComputeCIID uint64 `url:"computeciId" json:"computeciId" validate:"required"` +} + +// ComputeCISet sets compute CI ID for image +func (i Image) ComputeCISet(ctx context.Context, req ComputeCISetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/computeciSet" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/computeci_unset.go b/pkg/cloudbroker/image/computeci_unset.go new file mode 100644 index 0000000..df28b2d --- /dev/null +++ b/pkg/cloudbroker/image/computeci_unset.go @@ -0,0 +1,38 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ComputeCIUnsetRequest struct to unset compute CI +type ComputeCIUnsetRequest struct { + // ID of the image + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` +} + +// ComputeCIUnset unsets compute CI ID from image +func (i Image) ComputeCIUnset(ctx context.Context, req ComputeCIUnsetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/computeciUnset" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/create_cdrom_image.go b/pkg/cloudbroker/image/create_cdrom_image.go new file mode 100644 index 0000000..fc0b782 --- /dev/null +++ b/pkg/cloudbroker/image/create_cdrom_image.go @@ -0,0 +1,77 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateCDROMImageRequest struct to create CD-ROM image +type CreateCDROMImageRequest struct { + // Name of the rescue disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // URL where to download ISO from + // Required: true + URL string `url:"url" json:"url" validate:"required,url"` + + // Grid (platform) ID where this CD-ROM image should be create in + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Account ID to make the image exclusive + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Storage endpoint provider ID for place rescue CD + // Required: false + SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"` + + // Pool for place rescue CD + // Required: false + PoolName string `url:"pool_name,omitempty" json:"pool_name,omitempty"` + + // Username for remote media download + // Required: false + UsernameDL string `url:"usernameDL,omitempty" json:"usernameDL,omitempty"` + + // Password for remote media download + // Required: false + PasswordDl string `url:"passwordDL,omitempty" json:"passwordDL,omitempty"` + + // Binary architecture of this image + // Should be one of: + // - X86_64 + // Required: false + Architecture string `url:"architecture,omitempty" json:"architecture,omitempty"` + + // List of types of compute suitable for image. + // Example: [ "KVM_X86" ] + // Required: false + Drivers []string `url:"drivers,omitempty" json:"drivers,omitempty" validate:"max=2,imageDrivers"` +} + +// CreateCDROMImage creates CD-ROM image from an ISO identified by URL +func (i Image) CreateCDROMImage(ctx context.Context, req CreateCDROMImageRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/createCDROMImage" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/create_image.go b/pkg/cloudbroker/image/create_image.go new file mode 100644 index 0000000..aa388cd --- /dev/null +++ b/pkg/cloudbroker/image/create_image.go @@ -0,0 +1,115 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create image +type CreateRequest struct { + // Name of the rescue disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // URL where to download media from + // Required: true + URL string `url:"url" json:"url" validate:"required,url"` + + // Grid (platform) ID where this template should be create in + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Boot type of image + // Should be one of: + // - bios + // - UEFI + // Required: true + BootType string `url:"boottype" json:"boottype" validate:"required,imageBootType"` + + // Image type + // Should be one of: + // - linux + // - windows + // - or other + // Required: true + ImageType string `url:"imagetype" json:"imagetype" validate:"required,imageType"` + + // Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming + // Should be: + // - eth + // - ens (default value) + // Required: false + NetworkInterfaceNaming string `url:"networkInterfaceNaming,omitempty" json:"networkInterfaceNaming,omitempty" validate:"omitempty,networkInterfaceNaming"` + + // Does this machine supports hot resize + // Required: false + HotResize bool `url:"hotresize,omitempty" json:"hotresize,omitempty"` + + // Optional username for the image + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Optional password for the image + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // Account ID to make the image exclusive + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Username for upload binary media + // Required: false + UsernameDL string `url:"usernameDL,omitempty" json:"usernameDL,omitempty"` + + // Password for upload binary media + // Required: false + PasswordDL string `url:"passwordDL,omitempty" json:"passwordDL,omitempty"` + + // Storage endpoint provider ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool for image create + // Required: false + PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // Binary architecture of this image + // Should be one of: + // - X86_64 + // Required: false + Architecture string `url:"architecture,omitempty" json:"architecture,omitempty"` + + // List of types of compute suitable for image + // Example: [ "KVM_X86" ] + // Required: required + Drivers []string `url:"drivers" json:"drivers" validate:"min=1,max=2,imageDrivers"` + + // Bootable image or not + // Required: false + Bootable bool `url:"bootable,omitempty" json:"bootable,omitempty"` +} + +// CreateImage creates image from a media identified by URL +func (i Image) CreateImage(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/createImage" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/create_virtual.go b/pkg/cloudbroker/image/create_virtual.go new file mode 100644 index 0000000..1afe63e --- /dev/null +++ b/pkg/cloudbroker/image/create_virtual.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateVirtualRequest struct to create virtual image +type CreateVirtualRequest struct { + // Name of the virtual image to create + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of real image to link this virtual image to upon creation + // Required: true + TargetID uint64 `url:"targetId" json:"targetId" validate:"required"` +} + +// CreateVirtual creates virtual image +func (i Image) CreateVirtual(ctx context.Context, req CreateVirtualRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/createVirtual" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/delete.go b/pkg/cloudbroker/image/delete.go new file mode 100644 index 0000000..ac6a9c0 --- /dev/null +++ b/pkg/cloudbroker/image/delete.go @@ -0,0 +1,38 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete image +type DeleteRequest struct { + // ID of the image to delete + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` +} + +// Delete deletes image by ID +func (i Image) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/delete" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/delete_cdrom_image.go b/pkg/cloudbroker/image/delete_cdrom_image.go new file mode 100644 index 0000000..f10a2eb --- /dev/null +++ b/pkg/cloudbroker/image/delete_cdrom_image.go @@ -0,0 +1,38 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteCDROMImageRequest struct to delete CD-ROM image +type DeleteCDROMImageRequest struct { + // ID of the CD-ROM image to delete + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` +} + +// DeleteCDROMImage deletes a CD-ROM image +func (i Image) DeleteCDROMImage(ctx context.Context, req DeleteCDROMImageRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/deleteCDROMImage" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/delete_images.go b/pkg/cloudbroker/image/delete_images.go new file mode 100644 index 0000000..8469290 --- /dev/null +++ b/pkg/cloudbroker/image/delete_images.go @@ -0,0 +1,38 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteImagesRequest struct to delete images +type DeleteImagesRequest struct { + // List of images to be deleted + // Required: true + ImageIDs []uint64 `url:"imageIds" json:"imageIds" validate:"min=1"` +} + +// DeleteImages deletes images +func (i Image) DeleteImages(ctx context.Context, req DeleteImagesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/deleteImages" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/disable.go b/pkg/cloudbroker/image/disable.go new file mode 100644 index 0000000..13a78ee --- /dev/null +++ b/pkg/cloudbroker/image/disable.go @@ -0,0 +1,38 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable image +type DisableRequest struct { + // ID of image to be disabled + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` +} + +// Disable disables image +func (i Image) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/disable" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/edit.go b/pkg/cloudbroker/image/edit.go new file mode 100644 index 0000000..82fc56a --- /dev/null +++ b/pkg/cloudbroker/image/edit.go @@ -0,0 +1,69 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EditRequest struct to edit image +type EditRequest struct { + // ID of the image to edit + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming + // Should be: + // - eth + // - ens (default value) + // Required: false + NetworkInterfaceNaming string `url:"networkInterfaceNaming,omitempty" json:"networkInterfaceNaming,omitempty" validate:"omitempty,networkInterfaceNaming"` + + // Name for the image + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Username for the image + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Password for the image + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // Account ID to make the image exclusive + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Does this machine supports hot resize + // Required: false + HotResize bool `url:"hotresize,omitempty" json:"hotresize,omitempty"` + + // Does this image boot OS + // Required: false + Bootable bool `url:"bootable,omitempty" json:"bootable,omitempty"` +} + +// Edit edits an existing image +func (i Image) Edit(ctx context.Context, req EditRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/edit" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/enable.go b/pkg/cloudbroker/image/enable.go new file mode 100644 index 0000000..3849c94 --- /dev/null +++ b/pkg/cloudbroker/image/enable.go @@ -0,0 +1,38 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable image +type EnableRequest struct { + // ID of image to be enabled + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` +} + +// Enable enables image +func (i Image) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/enable" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/filter.go b/pkg/cloudbroker/image/filter.go new file mode 100644 index 0000000..d85504b --- /dev/null +++ b/pkg/cloudbroker/image/filter.go @@ -0,0 +1,69 @@ +package image + +// FilterById returns ListImages with specified ID. +func (li ListImages) FilterByID(id uint64) ListImages { + predicate := func(ri ItemImage) bool { + return ri.ID == id + } + + return li.FilterFunc(predicate) +} + +// FilterByName returns ListImages with specified Name. +func (li ListImages) FilterByName(name string) ListImages { + predicate := func(ri ItemImage) bool { + return ri.Name == name + } + + return li.FilterFunc(predicate) +} + +// FilterByStatus returns ListImages with specified Status. +func (li ListImages) FilterByStatus(status string) ListImages { + predicate := func(ri ItemImage) bool { + return ri.Status == status + } + + return li.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListImages with specified TechStatus. +func (li ListImages) FilterByTechStatus(techStatus string) ListImages { + predicate := func(ri ItemImage) bool { + return ri.TechStatus == techStatus + } + + return li.FilterFunc(predicate) +} + +// FilterByBootType returns ListImages with specified BootType. +func (li ListImages) FilterByBootType(bootType string) ListImages { + predicate := func(ri ItemImage) bool { + return ri.BootType == bootType + } + + return li.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListImages based on a user-specified predicate. +func (li ListImages) FilterFunc(predicate func(ItemImage) bool) ListImages { + var result ListImages + + for _, item := range li.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + return result +} + +// FindOne returns first found RecordImage +// If none was found, returns an empty struct. +func (li ListImages) FindOne() ItemImage { + if len(li.Data) == 0 { + return ItemImage{} + } + + return li.Data[0] +} diff --git a/pkg/cloudbroker/image/filter_test.go b/pkg/cloudbroker/image/filter_test.go new file mode 100644 index 0000000..079bdcb --- /dev/null +++ b/pkg/cloudbroker/image/filter_test.go @@ -0,0 +1,195 @@ +package image + +import "testing" + +var images = ListImages{ + Data: []ItemImage{ + { + UNCPath: "", + AccountID: 0, + ACL: []ACL{}, + Architecture: "X86_64", + BootType: "bios", + Bootable: true, + ComputeCIID: 0, + DeletedTime: 0, + Description: "", + Drivers: []string{ + "KVM_X86", + }, + Enabled: true, + GID: 212, + GUID: 9882, + History: []History{}, + HotResize: true, + ID: 9882, + LastModified: 0, + LinkTo: 0, + Milestones: 363491, + Name: "u16", + Password: "", + Pool: "vmstor", + PresentTo: []uint64{}, + ProviderName: "", + PurgeAttempts: 0, + ReferenceID: "sample_reference_id_u16", + ResID: "b321318-3214as-324-213-fdas", + ResName: "templates/image_9882", + RescueCD: false, + SEPID: 2504, + SharedWith: []uint64{}, + Size: 5, + Status: "CREATED", + TechStatus: "ALLOCATED", + Type: "linux", + URL: "http://sample_url:8000/u16", + Username: "", + Version: "", + Virtual: false, + }, + { + UNCPath: "", + AccountID: 0, + ACL: []ACL{}, + Architecture: "X86_64", + BootType: "bois", + Bootable: true, + ComputeCIID: 0, + DeletedTime: 0, + Description: "", + Drivers: []string{ + "KVM_X86", + }, + Enabled: false, + GID: 212, + GUID: 9884, + History: []History{}, + HotResize: false, + ID: 9884, + LastModified: 0, + LinkTo: 0, + Milestones: 363499, + Name: "alpine-virt-3.17", + Password: "", + Pool: "vmstor", + PresentTo: []uint64{}, + ProviderName: "", + PurgeAttempts: 0, + ReferenceID: "sample_reference_id_alpine", + ResID: "31d1d410-74f1-4e09-866b-046a5a8433c3", + ResName: "templates/image_9884", + RescueCD: false, + SEPID: 2504, + SharedWith: []uint64{}, + Size: 1, + Status: "CREATED", + TechStatus: "ALLOCATED", + Type: "linux", + URL: "http://sample_url:8000/alpine-virt-3", + Username: "", + Version: "", + Virtual: true, + }, + { + UNCPath: "", + AccountID: 1, + ACL: []ACL{}, + Architecture: "X86_64", + BootType: "bios", + Bootable: true, + ComputeCIID: 0, + DeletedTime: 0, + Description: "", + Drivers: []string{ + "KVM_X86", + }, + Enabled: true, + GID: 212, + GUID: 9885, + History: []History{}, + HotResize: true, + ID: 9885, + LastModified: 0, + LinkTo: 0, + Milestones: 363513, + Name: "test", + Password: "", + Pool: "vmstor", + PresentTo: []uint64{}, + ProviderName: "", + PurgeAttempts: 0, + ReferenceID: "sample_reference_id_test", + ResID: "1f53b815-1ac9-4a4b-af98-a0a3b69a34bb", + ResName: "templates/image_9885", + RescueCD: false, + SEPID: 2505, + SharedWith: []uint64{}, + Size: 4, + Status: "DESTROYED", + TechStatus: "ALLOCATED", + Type: "linux", + URL: "http://sample_url:8000/test", + Username: "", + Version: "", + Virtual: false, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := images.FilterByID(9885).FindOne() + + if actual.ID != 9885 { + t.Fatal("expected ID 9885, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := images.FilterByName("u16").FindOne() + + if actual.Name != "u16" { + t.Fatal("expected Name 'u16', found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := images.FilterByStatus("CREATED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "CREATED" { + t.Fatal("expected Status 'CREATED', found: ", item.Status) + } + } +} + +func TestFilterByBootType(t *testing.T) { + actual := images.FilterByBootType("bios") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.BootType != "bios" { + t.Fatal("expected BootType 'bios', found: ", item.BootType) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := images.FilterFunc(func(ri ItemImage) bool { + return ri.Virtual == true + }) + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + if actual.Data[0].Virtual != true { + t.Fatal("expected Virtual true, found false") + } +} diff --git a/pkg/cloudbroker/image/get.go b/pkg/cloudbroker/image/get.go new file mode 100644 index 0000000..545a375 --- /dev/null +++ b/pkg/cloudbroker/image/get.go @@ -0,0 +1,46 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get image details +type GetRequest struct { + // ID of image + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` +} + +// Get gets image details by ID as a RecordImage struct +func (i Image) Get(ctx context.Context, req GetRequest) (*RecordImage, error) { + res, err := i.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordImage{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets image details by ID as an array of bytes +func (i Image) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/get" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/image/grant_access.go b/pkg/cloudbroker/image/grant_access.go new file mode 100644 index 0000000..67f6a06 --- /dev/null +++ b/pkg/cloudbroker/image/grant_access.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GrantAccessRequest struct to share image with accounts +type GrantAccessRequest struct { + // ID of the image to share + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // ID of the accounts for share image + // Required: true + AccountIDs []uint64 `url:"accounts" json:"accounts" validate:"required"` +} + +// GrantAccess shares specified image with specified accounts +func (i Image) GrantAccess(ctx context.Context, req GrantAccessRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/grantAccess" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/ids.go b/pkg/cloudbroker/image/ids.go new file mode 100644 index 0000000..0b931bc --- /dev/null +++ b/pkg/cloudbroker/image/ids.go @@ -0,0 +1,28 @@ +package image + +// IDs gets array of ImageIDs from ListImages struct +func (li ListImages) IDs() []uint64 { + res := make([]uint64, 0, len(li.Data)) + for _, i := range li.Data { + res = append(res, i.ID) + } + return res +} + +// IDs gets array of StackIDs from ListStacks struct +func (ls ListStacks) IDs() []uint64 { + res := make([]uint64, 0, len(ls.Data)) + for _, h := range ls.Data { + res = append(res, h.ID) + } + return res +} + +// IDs gets array of HistoryIDs from ListHistory struct +func (lh ListHistory) IDs() []uint64 { + res := make([]uint64, 0, len(lh)) + for _, h := range lh { + res = append(res, h.ID) + } + return res +} diff --git a/pkg/cloudbroker/image/image.go b/pkg/cloudbroker/image/image.go new file mode 100644 index 0000000..64512e4 --- /dev/null +++ b/pkg/cloudbroker/image/image.go @@ -0,0 +1,16 @@ +// Lists all the images. A image is a template which can be used to deploy machines +package image + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to image +type Image struct { + client interfaces.Caller +} + +// Builder for image endpoint +func New(client interfaces.Caller) *Image { + return &Image{ + client: client, + } +} diff --git a/pkg/cloudbroker/image/link.go b/pkg/cloudbroker/image/link.go new file mode 100644 index 0000000..0f20332 --- /dev/null +++ b/pkg/cloudbroker/image/link.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// LinkRequest struct to link virtual image to another image +type LinkRequest struct { + // ID of the virtual image + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // ID of real image to link this virtual image to + // Required: true + TargetID uint64 `url:"targetId" json:"targetId" validate:"required"` +} + +// Link links virtual image to another image in the platform +func (i Image) Link(ctx context.Context, req LinkRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/link" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/list.go b/pkg/cloudbroker/image/list.go new file mode 100644 index 0000000..662412f --- /dev/null +++ b/pkg/cloudbroker/image/list.go @@ -0,0 +1,107 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of available images +type ListRequest struct { + // Filter images by storage endpoint provider ID + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by architecture + // Required: false + Architecture string `url:"architecture,omitempty" json:"architecture,omitempty"` + + // Find by type + // Required: false + TypeImage string `url:"typeImage,omitempty" json:"typeImage,omitempty"` + + // Find by image size + // Required: false + ImageSize uint64 `url:"imageSize,omitempty" json:"imageSize,omitempty"` + + // Find by SEP name + // Required: false + SEPName string `url:"sepName,omitempty" json:"sepName,omitempty"` + + // Find by pool + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Find by public True or False + // Required: false + Public interface{} `url:"public,omitempty" json:"public,omitempty" validate:"omitempty,isBool"` + + // Find by hot resize True or False + // Required: false + HotResize interface{} `url:"hotResize,omitempty" json:"hotResize,omitempty" validate:"omitempty,isBool"` + + // Find by bootable True or False + // Required: false + Bootable interface{} `url:"bootable,omitempty" json:"bootable,omitempty" validate:"omitempty,isBool"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by enabled True or False + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` +} + +// List gets list of information about images as a ListImages struct +func (i Image) List(ctx context.Context, req ListRequest) (*ListImages, error) { + + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListImages{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of information about images as an array of bytes +func (i Image) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/list" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/image/list_stacks.go b/pkg/cloudbroker/image/list_stacks.go new file mode 100644 index 0000000..0ddbe80 --- /dev/null +++ b/pkg/cloudbroker/image/list_stacks.go @@ -0,0 +1,65 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListStacksRequest struct to get list of stack +type ListStacksRequest struct { + // Image ID + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` +} + +// ListStacks gets list stack by image ID +func (i Image) ListStacks(ctx context.Context, req ListStacksRequest) (*ListStacks, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/listStacks" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListStacks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/image/models.go b/pkg/cloudbroker/image/models.go new file mode 100644 index 0000000..6e72dfa --- /dev/null +++ b/pkg/cloudbroker/image/models.go @@ -0,0 +1,479 @@ +package image + +// Detailed information about image +type RecordImage struct { + // UNC path + UNCPath string `json:"UNCPath"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Access Control List + ACL ListACL `json:"acl"` + + // Architecture + Architecture string `json:"architecture"` + + // Boot type + BootType string `json:"bootType"` + + // Bootable + Bootable bool `json:"bootable"` + + // CdPresentedTo + CdPresentedTo interface{} `json:"cdPresentedTo"` + + // Compute CI ID + ComputeCIID uint64 `json:"computeciId"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Drivers + Drivers []string `json:"drivers"` + + // Enabled + Enabled bool `json:"enabled"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // List history + History ListHistory `json:"history"` + + // Hot resize + HotResize bool `json:"hotResize"` + + // ID + ID uint64 `json:"id"` + + // Last modified + LastModified uint64 `json:"lastModified"` + + // Link to + LinkTo uint64 `json:"linkTo"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // NetworkInterfaceNaming + NetworkInterfaceNaming string `json:"networkInterfaceNaming"` + + // Password + Password string `json:"password"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Provider name + ProviderName string `json:"provider_name"` + + // Purge attempts + PurgeAttempts uint64 `json:"purgeAttempts"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Resource ID + ResID string `json:"resId"` + + // Resource name + ResName string `json:"resName"` + + // Rescue CD + RescueCD bool `json:"rescuecd"` + + // SEP ID + SEPID uint64 `json:"sepId"` + + // List shared with + SharedWith []uint64 `json:"sharedWith"` + + // Size + Size uint64 `json:"size"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // URL + URL string `json:"url"` + + // Username + Username string `json:"username"` + + // Version + Version string `json:"version"` +} + +// Detailed information about item of images list +type ItemImage struct { + // UNC path + UNCPath string `json:"UNCPath"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Access Control List + ACL ListACL `json:"acl"` + + // Architecture + Architecture string `json:"architecture"` + + // Boot type + BootType string `json:"bootType"` + + // Bootable + Bootable bool `json:"bootable"` + + // CdPresentedTo + CdPresentedTo interface{} `json:"cdPresentedTo"` + + // Compute CI ID + ComputeCIID uint64 `json:"computeciId"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Drivers + Drivers []string `json:"drivers"` + + // Enabled + Enabled bool `json:"enabled"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // List history + History ListHistory `json:"history"` + + // Hot resize + HotResize bool `json:"hotResize"` + + // ID + ID uint64 `json:"id"` + + // Last modified + LastModified uint64 `json:"lastModified"` + + // Link to + LinkTo uint64 `json:"linkTo"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // NetworkInterfaceNaming + NetworkInterfaceNaming string `json:"networkInterfaceNaming"` + + // Password + Password string `json:"password"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo []uint64 `json:"presentTo"` + + // Provider name + ProviderName string `json:"provider_name"` + + // Purge attempts + PurgeAttempts uint64 `json:"purgeAttempts"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Resource ID + ResID string `json:"resId"` + + // Resource name + ResName string `json:"resName"` + + // Rescue CD + RescueCD bool `json:"rescuecd"` + + // SEP ID + SEPID uint64 `json:"sepId"` + + // List shared with + SharedWith []uint64 `json:"sharedWith"` + + // Size + Size uint64 `json:"size"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + // URL + URL string `json:"url"` + + // Username + Username string `json:"username"` + + // Version + Version string `json:"version"` + + // Virtual + Virtual bool `json:"virtual"` +} + +// List images +type ListImages struct { + // Data + Data []ItemImage `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Access Control List +type ACL struct { + // Explicit + Explicit bool `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Right + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// List ACL +type ListACL []ACL + +// History information +type History struct { + // GUID + GUID GUID `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Timestamp + Timestamp uint64 `json:"timestamp"` +} + +type GUID string + +func (r *GUID) UnmarshalJSON(b []byte) error { + + if b[0] == '"' { + *r = GUID(string(b[1 : len(b)-1])) + return nil + } + + *r = GUID(string(b)) + + return nil +} + +// List history +type ListHistory []History + +// List stacks +type ListStacks struct { + // Data + Data []ItemListStacks `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about image +type ItemListStacks struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // API URL + APIURL string `json:"apiUrl"` + + // API key + APIKey string `json:"apikey"` + + // App ID + AppID string `json:"appId"` + + // CPU allocation ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // Description + Description string `json:"desc"` + + // Descr + Descr string `json:"descr"` + + // Drivers + Drivers []string `json:"drivers"` + + // Eco + Eco interface{} `json:"eco"` + + // Error + Error uint64 `json:"error"` + + // Grid ID + GID uint64 `json:"gid"` + + // GID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // List image IDs + Images []uint64 `json:"images"` + + // Login + Login string `json:"login"` + + // Mem allocation ratio + MemAllocationRatio float64 `json:"mem_allocation_ratio"` + + // Name + Name string `json:"name"` + + // Packegas + Packages Packages `json:"packages"` + + // Password + Password string `json:"passwd"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` +} + +// Package +type Packages struct { + // LibvirtBin + LibvirtBin LibvirtBin `json:"libvirt-bin"` + + // LibvirtDaemon + LibvirtDaemon LibvirtDaemon `json:"libvirt-daemon"` + + // Lvm2Lockd + Lvm2Lockd Lvm2Lockd `json:"lvm2-lockd"` + + // OpenvswitchCommon + OpenvswitchCommon OpenvswitchCommon `json:"openvswitch-common"` + + // OpenvswitchSwitch + OpenvswitchSwitch OpenvswitchSwitch `json:"openvswitch-switch"` + + // QemuSystemX86 + QemuSystemX86 QemuSystemX86 `json:"qemu-system-x86"` + + // Sanlock + Sanlock Sanlock `json:"sanlock"` +} + +// LibvirtBin +type LibvirtBin struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +type LibvirtDaemon struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// Lvm2Lockd +type Lvm2Lockd struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// OpenvswitchCommon +type OpenvswitchCommon struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// OpenvswitchSwitch +type OpenvswitchSwitch struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// QemuSystemX86 +type QemuSystemX86 struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// Sanlock +type Sanlock struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} diff --git a/pkg/cloudbroker/image/rename.go b/pkg/cloudbroker/image/rename.go new file mode 100644 index 0000000..bdbb59d --- /dev/null +++ b/pkg/cloudbroker/image/rename.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RenameRequest struct to rename image +type RenameRequest struct { + // ID of the virtual image to rename + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // New name + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +// Rename renames image by ID +func (i Image) Rename(ctx context.Context, req RenameRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/rename" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/revoke_access.go b/pkg/cloudbroker/image/revoke_access.go new file mode 100644 index 0000000..2072808 --- /dev/null +++ b/pkg/cloudbroker/image/revoke_access.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RevokeAccessRequest struct to unshare image with accounts +type RevokeAccessRequest struct { + // ID of the image to unshare + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // ID of the accounts for unshare image + // Required: true + AccountIDs []uint64 `url:"accounts" json:"accounts" validate:"required"` +} + +// RevokeAccess unshares specified image with specified accounts +func (i Image) RevokeAccess(ctx context.Context, req RevokeAccessRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/revokeAccess" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/serialize.go b/pkg/cloudbroker/image/serialize.go new file mode 100644 index 0000000..982aac4 --- /dev/null +++ b/pkg/cloudbroker/image/serialize.go @@ -0,0 +1,43 @@ +package image + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (li ListImages) Serialize(params ...string) (serialization.Serialized, error) { + if len(li.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(li, prefix, indent) + } + + return json.Marshal(li) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ri RecordImage) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ri, prefix, indent) + } + + return json.Marshal(ri) +} diff --git a/pkg/cloudbroker/image/share.go b/pkg/cloudbroker/image/share.go new file mode 100644 index 0000000..17d356f --- /dev/null +++ b/pkg/cloudbroker/image/share.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ShareRequest struct to share image +type ShareRequest struct { + // ID of the image to share + // Required: true + ImageId uint64 `url:"imageId" json:"imageId" validate:"required"` + + // List of account IDs + // Required: true + AccountIDs []uint64 `url:"accounts" json:"accounts" validate:"min=1"` +} + +// Share shares image with accounts +func (i Image) Share(ctx context.Context, req ShareRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/share" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/sync_create_image.go b/pkg/cloudbroker/image/sync_create_image.go new file mode 100644 index 0000000..d5b402c --- /dev/null +++ b/pkg/cloudbroker/image/sync_create_image.go @@ -0,0 +1,111 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SyncCreateRequest struct to sync create image +type SyncCreateRequest struct { + // Name of the rescue disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // URL where to download media from + // Required: true + URL string `url:"url" json:"url" validate:"required"` + + // Boot type of image + // Should be one of: + // - bios + // - UEFI + // Required: true + BootType string `url:"boottype" json:"boottype" validate:"required,imageBootType"` + + // Image type + // Should be one of: + // - linux + // - windows + // - or other + // Required: true + ImageType string `url:"imagetype" json:"imagetype" validate:"required,imageType"` + + // Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming + // Should be: + // - eth + // - ens (default value) + // Required: false + NetworkInterfaceNaming string `url:"networkInterfaceNaming,omitempty" json:"networkInterfaceNaming,omitempty" validate:"omitempty,networkInterfaceNaming"` + + // Does this machine supports hot resize + // Required: false + HotResize bool `url:"hotresize,omitempty" json:"hotresize,omitempty"` + + // Optional username for the image + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Optional password for the image + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // Account ID to make the image exclusive + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Username for upload binary media + // Required: false + UsernameDL string `url:"usernameDL,omitempty" json:"usernameDL,omitempty"` + + // Password for upload binary media + // Required: false + PasswordDL string `url:"passwordDL,omitempty" json:"passwordDL,omitempty"` + + // Storage endpoint provider ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool for image create + // Required: false + PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // Binary architecture of this image + // Should be one of: + // - X86_64 + // Required: false + Architecture string `url:"architecture,omitempty" json:"architecture,omitempty"` + + // List of types of compute suitable for image + // Example: [ "KVM_X86" ] + // Required: true + Drivers []string `url:"drivers" json:"drivers" validate:"min=1,max=2,imageDrivers"` + + // Bootable image or not + // Required: false + Bootable bool `url:"bootable,omitempty" json:"bootable,omitempty"` +} + +// SyncCreate creates image from a media identified by URL (in synchronous mode) +func (i Image) SyncCreate(ctx context.Context, req SyncCreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/syncCreateImage" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/update_nodes.go b/pkg/cloudbroker/image/update_nodes.go new file mode 100644 index 0000000..55fa33f --- /dev/null +++ b/pkg/cloudbroker/image/update_nodes.go @@ -0,0 +1,42 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateNodesRequest struct to update nodes +type UpdateNodesRequest struct { + // Image ID + // Required: true + ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` + + // List of stacks + // Required: false + EnabledStacks []uint64 `url:"enabledStacks,omitempty" json:"enabledStacks,omitempty"` +} + +// UpdateNodes updates image availability on nodes +func (i Image) UpdateNodes(ctx context.Context, req UpdateNodesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/updateNodes" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/image/upload_image_file.go b/pkg/cloudbroker/image/upload_image_file.go new file mode 100644 index 0000000..1268eae --- /dev/null +++ b/pkg/cloudbroker/image/upload_image_file.go @@ -0,0 +1,39 @@ +package image + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "os" +) + +// UploadImageFileResponse struct to enable image +type UploadImageFileResponse struct { + // ImageFileUri + ImageFileUri string `json:"image_file_uri"` +} + +// UploadImageFile uploads file image to platform +func (i Image) UploadImageFile(ctx context.Context, filePath string) (string, error) { + file, err := os.ReadFile(filePath) + if err != nil { + return "", fmt.Errorf("can not read file %v", err) + } + + url := "/cloudbroker/image/uploadImageFile" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, file) + if err != nil { + return "", err + } + + result := UploadImageFileResponse{} + + err = json.Unmarshal(res, &result) + if err != nil { + return "", err + } + + return result.ImageFileUri, nil +} diff --git a/pkg/cloudbroker/k8ci.go b/pkg/cloudbroker/k8ci.go new file mode 100644 index 0000000..df2b1d1 --- /dev/null +++ b/pkg/cloudbroker/k8ci.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/k8ci" +) + +// Accessing the K8CI method group +func (cb *CloudBroker) K8CI() *k8ci.K8CI { + return k8ci.New(cb.client) +} diff --git a/pkg/cloudbroker/k8ci/access_add.go b/pkg/cloudbroker/k8ci/access_add.go new file mode 100644 index 0000000..2dcf0ad --- /dev/null +++ b/pkg/cloudbroker/k8ci/access_add.go @@ -0,0 +1,36 @@ +package k8ci + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessAddRequest struct for adding permission to access to account for a k8ci +type AccessAddRequest struct { + // ID of the K8 catalog item to add access for + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` + + // Account ID to add to the sharedWith access list + // Required: true + AccountId uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Add accountId to sharedWith access list for k8ci. +func (k K8CI) AccessAdd(ctx context.Context, req AccessAddRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/accessAdd" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/k8ci/access_remove.go b/pkg/cloudbroker/k8ci/access_remove.go new file mode 100644 index 0000000..729d941 --- /dev/null +++ b/pkg/cloudbroker/k8ci/access_remove.go @@ -0,0 +1,36 @@ +package k8ci + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessRemoveRequest struct for removing permission to access to account for a k8ci +type AccessRemoveRequest struct { + // ID of the K8 catalog item to remove access for + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` + + // Account ID to be removed from the sharedWith access list + // Required: true + AccountId uint64 `url:"accountId" json:"accountId" validate:"required"` +} + +// Remove accountId from sharedWith access list for k8ci. +func (k K8CI) AccessRemove(ctx context.Context, req AccessRemoveRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/accessRemove" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/k8ci/create.go b/pkg/cloudbroker/k8ci/create.go new file mode 100644 index 0000000..3055ae3 --- /dev/null +++ b/pkg/cloudbroker/k8ci/create.go @@ -0,0 +1,86 @@ +package k8ci + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create K8CI instance +type CreateRequest struct { + // Name of catalog item + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Version tag + // Required: true + Version string `url:"version" json:"version" validate:"required"` + + // Optional description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Image ID for master K8S node + // Required: true + MasterImageID uint64 `url:"masterImageId" json:"masterImageId" validate:"required"` + + // Compute driver + // Should be one of: + // - KVM_X86 + // - etc + // Required: true + MasterDriver string `url:"masterDriver" json:"masterDriver" validate:"driver"` + + // Image ID for worker K8S node + // Required: true + WorkerImageID uint64 `url:"workerImageId" json:"workerImageId" validate:"required"` + + // Compute driver + // Should be one of + // - KVM_X86 + // - etc + // Required: true + WorkerDriver string `url:"workerDriver" json:"workerDriver" validate:"driver"` + + // List of account IDs, which have access to this item. + // If empty, any account has access + // Required: false + SharedWith []uint64 `url:"sharedWith,omitempty" json:"sharedWith,omitempty"` + + // Policy limit on maximum number of master nodes + // Required: true + MaxMasterCount uint64 `url:"maxMasterCount" json:"maxMasterCount" validate:"required"` + + // Policy limit on maximum number of worker nodes + // Required: true + MaxWorkerCount uint64 `url:"maxWorkerCount" json:"maxWorkerCount" validate:"required"` + + // Network plugins + // Values of slice must be flannel, weavenet or calico + //Required: true + NetworkPlugins []string `url:"networkPlugins" json:"networkPlugins" validate:"required,networkPlugins"` +} + +// Create creates a new K8CI instance +func (k K8CI) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/create" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8ci/delete.go b/pkg/cloudbroker/k8ci/delete.go new file mode 100644 index 0000000..08fd03c --- /dev/null +++ b/pkg/cloudbroker/k8ci/delete.go @@ -0,0 +1,42 @@ +package k8ci + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete K8CI +type DeleteRequest struct { + // K8CI ID + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` + + // Delete permanently or not + // Required: false + Permanently bool `url:"permanently" json:"permanently"` +} + +// Delete deletes K8CI by ID +func (k K8CI) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/delete" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8ci/disable.go b/pkg/cloudbroker/k8ci/disable.go new file mode 100644 index 0000000..1022209 --- /dev/null +++ b/pkg/cloudbroker/k8ci/disable.go @@ -0,0 +1,38 @@ +package k8ci + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable K8CI +type DisableRequest struct { + // K8CI ID + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` +} + +// Disable disables K8CI +func (k K8CI) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/disable" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8ci/enable.go b/pkg/cloudbroker/k8ci/enable.go new file mode 100644 index 0000000..962f325 --- /dev/null +++ b/pkg/cloudbroker/k8ci/enable.go @@ -0,0 +1,38 @@ +package k8ci + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable K8CI +type EnableRequest struct { + // K8CI ID + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` +} + +// Enable enables K8CI +func (k K8CI) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/enable" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8ci/filter.go b/pkg/cloudbroker/k8ci/filter.go new file mode 100644 index 0000000..92e8337 --- /dev/null +++ b/pkg/cloudbroker/k8ci/filter.go @@ -0,0 +1,80 @@ +package k8ci + +// FilterByID returns ListK8CI with specified ID. +func (lkc ListK8CI) FilterByID(id uint64) ListK8CI { + predicate := func(ikc ItemK8CI) bool { + return ikc.RecordK8CIList.ID == id + } + + return lkc.FilterFunc(predicate) +} + +// FilterByName returns ListK8CI with specified Name. +func (lkc ListK8CI) FilterByName(name string) ListK8CI { + predicate := func(ikc ItemK8CI) bool { + return ikc.RecordK8CIList.Name == name + } + + return lkc.FilterFunc(predicate) +} + +// FilterByStatus returns ListK8CI with specified Status. +func (lkc ListK8CI) FilterByStatus(status string) ListK8CI { + predicate := func(ikc ItemK8CI) bool { + return ikc.Status == status + } + + return lkc.FilterFunc(predicate) +} + +// FilterByWorkerImageID returns ListK8CI with specified WorkerImageID. +func (lkc ListK8CI) FilterByWorkerImageID(workerImageID uint64) ListK8CI { + predicate := func(ikc ItemK8CI) bool { + return ikc.WorkerImageID == workerImageID + } + + return lkc.FilterFunc(predicate) +} + +// FilterByLBImageID returns ListK8CI with specified LBImageID. +func (lkc ListK8CI) FilterByLBImageID(lbImageID uint64) ListK8CI { + predicate := func(ikc ItemK8CI) bool { + return ikc.LBImageID == lbImageID + } + + return lkc.FilterFunc(predicate) +} + +// FilterByMasterImageID returns ListK8CI with specified MasterImageID. +func (lkc ListK8CI) FilterByMasterImageID(masterImageID uint64) ListK8CI { + predicate := func(ikc ItemK8CI) bool { + return ikc.MasterImageID == masterImageID + } + + return lkc.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListK8CI based on a user-specified predicate. +func (lkc ListK8CI) FilterFunc(predicate func(ItemK8CI) bool) ListK8CI { + var result ListK8CI + + for _, item := range lkc.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemK8CI +// If none was found, returns an empty struct. +func (lkc ListK8CI) FindOne() ItemK8CI { + if len(lkc.Data) == 0 { + return ItemK8CI{} + } + + return lkc.Data[0] +} diff --git a/pkg/cloudbroker/k8ci/filter_test.go b/pkg/cloudbroker/k8ci/filter_test.go new file mode 100644 index 0000000..fbaa6d4 --- /dev/null +++ b/pkg/cloudbroker/k8ci/filter_test.go @@ -0,0 +1,147 @@ +package k8ci + +import "testing" + +var k8ciItems = ListK8CI{ + Data: []ItemK8CI{ + { + CreatedTime: 123902139, + RecordK8CIList: RecordK8CIList{ + Description: "", + GID: 0, + GUID: 1, + ID: 1, + LBImageID: 5, + MasterDriver: "KVM_X86", + MasterImageID: 120, + MaxMasterCount: 2, + MaxWorkerCount: 3, + Name: "purple_snake", + SharedWith: []uint64{}, + Status: "ENABLED", + Version: "1", + WorkerDriver: "KVM_X86", + WorkerImageID: 120, + }, + }, + { + CreatedTime: 123902232, + RecordK8CIList: RecordK8CIList{ + Description: "", + GID: 0, + GUID: 2, + ID: 2, + LBImageID: 10, + MasterDriver: "KVM_X86", + MasterImageID: 121, + MaxMasterCount: 3, + MaxWorkerCount: 5, + Name: "green_giant", + SharedWith: []uint64{}, + Status: "DISABLED", + Version: "2", + WorkerDriver: "KVM_X86", + WorkerImageID: 121, + }, + }, + { + CreatedTime: 123902335, + RecordK8CIList: RecordK8CIList{ + Description: "", + GID: 0, + GUID: 3, + ID: 3, + LBImageID: 12, + MasterDriver: "KVM_X86", + MasterImageID: 98, + MaxMasterCount: 5, + MaxWorkerCount: 9, + Name: "magenta_cloud", + SharedWith: []uint64{}, + Status: "ENABLED", + Version: "3", + WorkerDriver: "KVM_X86", + WorkerImageID: 98, + }, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := k8ciItems.FilterByID(2).FindOne() + + if actual.ID != 2 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := k8ciItems.FilterByName("magenta_cloud").FindOne() + + if actual.Name != "magenta_cloud" { + t.Fatal("expected Name 'magenta_cloud', found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := k8ciItems.FilterByStatus("ENABLED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterByWorkerImageID(t *testing.T) { + actual := k8ciItems.FilterByWorkerImageID(98).FindOne() + + if actual.WorkerImageID != 98 { + t.Fatal("expected WorkerImageID 98, found: ", actual.WorkerImageID) + } +} + +func TestFilterByLBImageID(t *testing.T) { + actual := k8ciItems.FilterByLBImageID(10).FindOne() + + if actual.LBImageID != 10 { + t.Fatal("expected LBImageID 10, found: ", actual.LBImageID) + } +} + +func TestFilterByMasterImageID(t *testing.T) { + actual := k8ciItems.FilterByMasterImageID(120).FindOne() + + if actual.MasterImageID != 120 { + t.Fatal("expected MasterImageID 120, found: ", actual.MasterImageID) + } +} + +func TestFilterFunc(t *testing.T) { + actual := k8ciItems.FilterFunc(func(ikc ItemK8CI) bool { + return ikc.CreatedTime > 123902139 + }) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedTime < 123902139 { + t.Fatal("expected CreatedTime greater than 123902139, found: ", item.CreatedTime) + } + } +} + +func TestSortingByCreatedTime(t *testing.T) { + actual := k8ciItems.SortByCreatedTime(true) + + if actual.Data[0].CreatedTime != 123902335 && actual.Data[2].CreatedTime != 123902139 { + t.Fatal("expected inverse sort, found normal") + } +} diff --git a/pkg/cloudbroker/k8ci/get.go b/pkg/cloudbroker/k8ci/get.go new file mode 100644 index 0000000..796037a --- /dev/null +++ b/pkg/cloudbroker/k8ci/get.go @@ -0,0 +1,46 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about K8CI +type GetRequest struct { + // ID of the K8 catalog item to get + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` +} + +// Get gets details of the specified K8 catalog item as a RecordK8CI struct +func (k K8CI) Get(ctx context.Context, req GetRequest) (*RecordK8CI, error) { + res, err := k.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + item := RecordK8CI{} + + err = json.Unmarshal(res, &item) + if err != nil { + return nil, err + } + + return &item, nil +} + +// GetRaw gets details of the specified K8 catalog item as an array of bytes +func (k K8CI) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/get" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/k8ci/ids.go b/pkg/cloudbroker/k8ci/ids.go new file mode 100644 index 0000000..75a3c8e --- /dev/null +++ b/pkg/cloudbroker/k8ci/ids.go @@ -0,0 +1,10 @@ +package k8ci + +// IDs gets array of K8CIIDs from ListK8CI struct +func (lk8ci ListK8CI) IDs() []uint64 { + res := make([]uint64, 0, len(lk8ci.Data)) + for _, i := range lk8ci.Data { + res = append(res, i.ID) + } + return res +} \ No newline at end of file diff --git a/pkg/cloudbroker/k8ci/k8ci.go b/pkg/cloudbroker/k8ci/k8ci.go new file mode 100644 index 0000000..c6a65c4 --- /dev/null +++ b/pkg/cloudbroker/k8ci/k8ci.go @@ -0,0 +1,18 @@ +// API to manage K8CI instances +package k8ci + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to K8CI +type K8CI struct { + client interfaces.Caller +} + +// Builder for K8CI endpoints +func New(client interfaces.Caller) *K8CI { + return &K8CI{ + client: client, + } +} diff --git a/pkg/cloudbroker/k8ci/list.go b/pkg/cloudbroker/k8ci/list.go new file mode 100644 index 0000000..f9b8b21 --- /dev/null +++ b/pkg/cloudbroker/k8ci/list.go @@ -0,0 +1,84 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list information about images +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by worker driver + // Required: false + WorkerDriver string `url:"workerDriver,omitempty" json:"workerDriver,omitempty"` + + // Find by master driver + // Required: false + MasterDriver string `url:"masterDriver,omitempty" json:"masterDriver,omitempty"` + + // Find by network plugin + // Required: false + NetworkPlugins string `url:"netPlugins,omitempty" json:"masterDrnetPluginsiver,omitempty"` + + // List disabled items as well + // Required: false + IncludeDisabled bool `url:"includeDisabled,omitempty" json:"includeDisabled,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all k8ci catalog items available to the current user as a ListK8CI struct +func (k K8CI) List(ctx context.Context, req ListRequest) (*ListK8CI, error) { + + res, err := k.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListK8CI{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all k8ci catalog items available to the current user as an array of bytes +func (k K8CI) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/list" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/k8ci/list_deleted.go b/pkg/cloudbroker/k8ci/list_deleted.go new file mode 100644 index 0000000..6feb525 --- /dev/null +++ b/pkg/cloudbroker/k8ci/list_deleted.go @@ -0,0 +1,69 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list information about deleted k8ci items +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"k8cId,omitempty" json:"k8cId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by worker driver + // Required: false + WorkerDriver string `url:"workerDriver,omitempty" json:"workerDriver,omitempty"` + + // Find by master driver + // Required: false + MasterDriver string `url:"masterDriver,omitempty" json:"masterDriver,omitempty"` + + // Find by network plugin + // Required: false + NetworkPlugins string `url:"netPlugins,omitempty" json:"netPlugins,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list all deleted k8ci catalog items available to the current user +func (k K8CI) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListK8CI, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/listDeleted" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListK8CI{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/k8ci/models.go b/pkg/cloudbroker/k8ci/models.go new file mode 100644 index 0000000..647956b --- /dev/null +++ b/pkg/cloudbroker/k8ci/models.go @@ -0,0 +1,120 @@ +package k8ci + +// Main information about K8CI in List +type ItemK8CI struct { + // Created time + CreatedTime uint64 `json:"createdTime"` + // Detailed information about K8CI + RecordK8CIList +} + +// List K8CI +type ListK8CI struct { + //Data + Data []ItemK8CI `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about K8CI in List +type RecordK8CIList struct { + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Load balancer image ID + LBImageID uint64 `json:"lbImageId"` + + // Master driver + MasterDriver string `json:"masterDriver"` + + // Master image ID + MasterImageID uint64 `json:"masterImageId"` + + // Max master count + MaxMasterCount uint64 `json:"maxMasterCount"` + + // Max worker count + MaxWorkerCount uint64 `json:"maxWorkerCount"` + + // Name + Name string `json:"name"` + + // Shared with + SharedWith []uint64 `json:"sharedWith"` + + // Status + Status string `json:"status"` + + // Version + Version string `json:"version"` + + // Worker driver + WorkerDriver string `json:"workerDriver"` + + // Worker image ID + WorkerImageID uint64 `json:"workerImageId"` +} + +// Detailed information about K8CI +type RecordK8CI struct { + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Load balancer image ID + LBImageID uint64 `json:"lbImageId"` + + // Master driver + MasterDriver string `json:"masterDriver"` + + // Master image ID + MasterImageID uint64 `json:"masterImageId"` + + // Max master count + MaxMasterCount uint64 `json:"maxMasterCount"` + + // Max worker count + MaxWorkerCount uint64 `json:"maxWorkerCount"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + //NetworkPlugins + NetworkPlugins []string `json:"networkPlugins"` + + // Shared with + SharedWith []uint64 `json:"sharedWith"` + + // Status + Status string `json:"status"` + + // Version + Version string `json:"version"` + + // Worker driver + WorkerDriver string `json:"workerDriver"` + + // Worker image ID + WorkerImageID uint64 `json:"workerImageId"` +} diff --git a/pkg/cloudbroker/k8ci/restore.go b/pkg/cloudbroker/k8ci/restore.go new file mode 100644 index 0000000..1f71ba6 --- /dev/null +++ b/pkg/cloudbroker/k8ci/restore.go @@ -0,0 +1,38 @@ +package k8ci + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore K8CI +type RestoreRequest struct { + // K8CI ID + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` +} + +// Restore restores K8CI +func (k K8CI) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8ci/restore" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8ci/serialize.go b/pkg/cloudbroker/k8ci/serialize.go new file mode 100644 index 0000000..c420ca7 --- /dev/null +++ b/pkg/cloudbroker/k8ci/serialize.go @@ -0,0 +1,43 @@ +package k8ci + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lkc ListK8CI) Serialize(params ...string) (serialization.Serialized, error) { + if len(lkc.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lkc, prefix, indent) + } + + return json.Marshal(lkc) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ikc ItemK8CI) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ikc, prefix, indent) + } + + return json.Marshal(ikc) +} diff --git a/pkg/cloudbroker/k8ci/sorting.go b/pkg/cloudbroker/k8ci/sorting.go new file mode 100644 index 0000000..865c239 --- /dev/null +++ b/pkg/cloudbroker/k8ci/sorting.go @@ -0,0 +1,22 @@ +package k8ci + +import "sort" + +// SortByCreatedTime sorts ListK8CI by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lkc ListK8CI) SortByCreatedTime(inverse bool) ListK8CI { + if len(lkc.Data) < 2 { + return lkc + } + + sort.Slice(lkc.Data, func(i, j int) bool { + if inverse { + return lkc.Data[i].CreatedTime > lkc.Data[j].CreatedTime + } + + return lkc.Data[i].CreatedTime < lkc.Data[j].CreatedTime + }) + + return lkc +} diff --git a/pkg/cloudbroker/k8s.go b/pkg/cloudbroker/k8s.go new file mode 100644 index 0000000..3ea65a0 --- /dev/null +++ b/pkg/cloudbroker/k8s.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/k8s" +) + +// Accessing the K8S method group +func (cb *CloudBroker) K8S() *k8s.K8S { + return k8s.New(cb.client) +} diff --git a/pkg/cloudbroker/k8s/create.go b/pkg/cloudbroker/k8s/create.go new file mode 100644 index 0000000..34bfda0 --- /dev/null +++ b/pkg/cloudbroker/k8s/create.go @@ -0,0 +1,202 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create K8S +type CreateRequest struct { + // Name of kubernetes cluster + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Resource group ID for cluster placement + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // ID of kubernetes catalog item (K8CI) for cluster + // Required: true + K8CIID uint64 `url:"k8ciId" json:"k8ciId" validate:"required"` + + // Name for first worker group created with cluster + // Required: true + WorkerGroupName string `url:"workerGroupName" json:"workerGroupName" validate:"required,workerGroupName"` + + // Network plugin + // Must be one of these values: flannel, weavenet, calico + // Required: true + NetworkPlugin string `url:"networkPlugin" json:"networkPlugin" validate:"required,networkPlugin"` + + // ID of SEP to create boot disks for master nodes. + // Uses images SEP ID if not set + // Required: false + MasterSEPID uint64 `url:"masterSepId,omitempty" json:"masterSepId,omitempty"` + + // Pool to use if master SEP ID is set, can be also empty if needed to be chosen by system + // Required: false + MasterSEPPool string `url:"masterSepPool,omitempty" json:"masterSepPool,omitempty"` + + // ID of SEP to create boot disks for default worker nodes group. + // Uses images SEP ID if not set + // Required: false + WorkerSEPID uint64 `url:"workerSepId,omitempty" json:"workerSepId,omitempty"` + + // Pool to use if worker SEP ID is set, can be also empty if needed to be chosen by system + // Required: false + WorkerSEPPool string `url:"workerSepPool,omitempty" json:"workerSepPool,omitempty"` + + // List of strings with labels for default worker group + // i.e: ["label1=value1", "label2=value2"] + // Required: false + Labels []string `url:"labels,omitempty" json:"labels,omitempty"` + + // List of strings with taints for default worker group + // i.e: ["key1=value1:NoSchedule", "key2=value2:NoExecute"] + // Required: false + Taints []string `url:"taints,omitempty" json:"taints,omitempty"` + + // List of strings with annotations for worker group + // i.e: ["key1=value1", "key2=value2"] + // Required: false + Annotations []string `url:"annotations,omitempty" json:"annotations,omitempty"` + + // Number of master nodes to create + // Required: false + MasterNum uint64 `url:"masterNum,omitempty" json:"masterNum,omitempty"` + + // Master node CPU count + // Required: false + MasterCPU uint64 `url:"masterCpu,omitempty" json:"masterCpu,omitempty"` + + // Master node RAM volume in MB + // Required: false + MasterRAM uint64 `url:"masterRam,omitempty" json:"masterRam,omitempty"` + + // Master node boot disk size in GB If 0 is specified, size is defined by the OS image size + // Required: false + MasterDisk uint64 `url:"masterDisk,omitempty" json:"masterDisk,omitempty"` + + // Number of worker nodes to create in default worker group + // Required: false + WorkerNum uint64 `url:"workerNum,omitempty" json:"workerNum,omitempty"` + + // Worker node CPU count + // Required: false + WorkerCPU uint64 `url:"workerCpu,omitempty" json:"workerCpu,omitempty"` + + // Worker node RAM volume in MB + // Required: false + WorkerRAM uint64 `url:"workerRam,omitempty" json:"workerRam,omitempty"` + + // Worker node boot disk size in GB. If 0 is specified, size is defined by the OS image size + // Required: false + WorkerDisk uint64 `url:"workerDisk,omitempty" json:"workerDisk,omitempty"` + + // ID of the external network to connect load balancer and cluster VINS. If 0 is specified, external network selects automatically to + // Required: false + ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"` + + // ID of the ViNS to connect k8s cluster. If nothing is specified, ViNS will be created automatically + // Required: false + VinsId uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Create kubernetes cluster with masters nodes behind load balancer if true. + // Otherwise give all cluster nodes direct external addresses from selected external network + // Required: false + WithLB bool `url:"withLB" json:"withLB"` + + // Custom sysctl values for Load Balancer instance. Applied on boot + // Required: false + LbSysctlParams []map[string]interface{} `url:"lbSysctlParams,omitempty" json:"lbSysctlParams,omitempty"` + + // Use Highly Available schema for LB deploy + // Required: false + HighlyAvailable bool `url:"highlyAvailableLB,omitempty" json:"highlyAvailableLB,omitempty"` + + // Optional extra Subject Alternative Names (SANs) to use for the API Server serving certificate. Can be both IP addresses and DNS names + // Required: false + AdditionalSANs []string `url:"additionalSANs,omitempty" json:"additionalSANs,omitempty"` + + // Is used to define settings and actions that should be performed before any other component in the cluster starts. + // It allows you to configure things like node registration, network setup, and other initialization tasks. insert a valid JSON string with all levels of nesting + // Required: false + InitConfiguration string `url:"initConfiguration,omitempty" json:"initConfiguration,omitempty"` + + // Is used to define global settings and configurations for the entire cluster. + // It includes parameters such as cluster name, DNS settings, authentication methods, and other cluster-wide configurations. + // Insert a valid JSON string with all levels of nesting + // Required: false + ClusterConfiguration string `url:"clusterConfiguration,omitempty" json:"clusterConfiguration,omitempty"` + + // Is used to configure the behavior and settings of the Kubelet, which is the primary node agent that runs on each node in the cluster. + // It includes parameters such as node IP address, resource allocation, pod eviction policies, and other Kubelet-specific configurations. + // Insert a valid JSON string with all levels of nesting + // Required: false + KubeletConfiguration string `url:"kubeletConfiguration,omitempty" json:"kubeletConfiguration,omitempty"` + + // Is used to configure the behavior and settings of the Kube-proxy, which is responsible for network proxying and load balancing within the cluster. + // It includes parameters such as proxy mode, cluster IP ranges, and other Kube-proxy specific configurations. + // Insert a valid JSON string with all levels of nesting + // Required: false + KubeProxyConfiguration string `url:"kubeProxyConfiguration,omitempty" json:"kubeProxyConfiguration,omitempty"` + + // Is used to configure the behavior and settings for joining a node to a cluster. + // It includes parameters such as the cluster's control plane endpoint, token, and certificate key. insert a valid JSON string with all levels of nesting + // Required: false + JoinConfiguration string `url:"joinConfiguration,omitempty" json:"joinConfiguration,omitempty"` + + // Text description of this kubernetes cluster + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Meta data for working group computes, format YAML "user_data": 1111 + // Required: false + UserData string `url:"userData,omitempty" json:"userData,omitempty"` + + // Use only selected ExtNet for infrastructure connections + // Required: false + ExtNetOnly bool `url:"extnetOnly,omitempty" json:"extnetOnly,omitempty"` + + // Insert ssl certificate in x509 pem format + // Required: false + OidcCertificate string `url:"oidcCertificate,omitempty" json:"oidcCertificate,omitempty"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` +} + +// GetRAM returns RAM values +func (r CreateRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 2) + + res["MasterRAM"] = r.MasterRAM + res["WorkerRAM"] = r.WorkerRAM + + return res +} + +// Create creates a new kubernetes cluster in the specified resource group +func (k K8S) Create(ctx context.Context, req CreateRequest) (string, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/create" + + res, err := k.client.DecortApiCallMP(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/delete.go b/pkg/cloudbroker/k8s/delete.go new file mode 100644 index 0000000..0028e9b --- /dev/null +++ b/pkg/cloudbroker/k8s/delete.go @@ -0,0 +1,43 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete kubernetes cluster +type DeleteRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // True if cluster is destroyed permanently. + // Otherwise it can be restored from recycle bin + // Required: false + Permanently bool `url:"permanently" json:"permanently"` +} + +// Delete deletes kubernetes cluster +func (k K8S) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/delete" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/delete_master_from_group.go b/pkg/cloudbroker/k8s/delete_master_from_group.go new file mode 100644 index 0000000..fd11c89 --- /dev/null +++ b/pkg/cloudbroker/k8s/delete_master_from_group.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteMasterFromGroupRequest struct to delete master from group +type DeleteMasterFromGroupRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the masters compute group + // Required: true + MasterGroupID uint64 `url:"masterGroupId" json:"masterGroupId" validate:"required"` + + // List of Compute IDs of master nodes to delete + // Required: true + MasterIDs []uint64 `url:"masterIds" json:"masterIds" validate:"min=1"` +} + +// DeleteMasterFromGroup deletes compute from masters group in selected kubernetes cluster +func (k K8S) DeleteMasterFromGroup(ctx context.Context, req DeleteMasterFromGroupRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/deleteMasterFromGroup" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/delete_worker_from_group.go b/pkg/cloudbroker/k8s/delete_worker_from_group.go new file mode 100644 index 0000000..da99ccf --- /dev/null +++ b/pkg/cloudbroker/k8s/delete_worker_from_group.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteWorkerFromGroupRequest struct to delete worker from group +type DeleteWorkerFromGroupRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // Compute ID of worker node to delete + // Required: true + WorkerID uint64 `url:"workerId" json:"workerId" validate:"required"` +} + +// DeleteWorkerFromGroup deletes worker compute from workers group in selected kubernetes cluster +func (k K8S) DeleteWorkerFromGroup(ctx context.Context, req DeleteWorkerFromGroupRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/deleteWorkerFromGroup" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/disable.go b/pkg/cloudbroker/k8s/disable.go new file mode 100644 index 0000000..ef1c884 --- /dev/null +++ b/pkg/cloudbroker/k8s/disable.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable kubernetes cluster +type DisableRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Disable disables kubernetes cluster by ID +func (k K8S) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/disable" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/enable.go b/pkg/cloudbroker/k8s/enable.go new file mode 100644 index 0000000..a6bcf6a --- /dev/null +++ b/pkg/cloudbroker/k8s/enable.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable kubernetes cluster +type EnableRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Enable enables kubernetes cluster by ID +func (k K8S) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/enable" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/filter.go b/pkg/cloudbroker/k8s/filter.go new file mode 100644 index 0000000..076bcce --- /dev/null +++ b/pkg/cloudbroker/k8s/filter.go @@ -0,0 +1,98 @@ +package k8s + +// FilterByID returns ListK8S with specified ID. +func (lkc ListK8S) FilterByID(id uint64) ListK8S { + predicate := func(ikc ItemK8S) bool { + return ikc.ID == id + } + + return lkc.FilterFunc(predicate) +} + +// FilterByName returns ListK8S with specified Name. +func (lkc ListK8S) FilterByName(name string) ListK8S { + predicate := func(ikc ItemK8S) bool { + return ikc.Name == name + } + + return lkc.FilterFunc(predicate) +} + +// FilterByAccountID returns ListK8S with specified AccountID. +func (lkc ListK8S) FilterByAccountID(accountID uint64) ListK8S { + predicate := func(ikc ItemK8S) bool { + return ikc.AccountID == accountID + } + + return lkc.FilterFunc(predicate) +} + +// FilterByRGID returns ListK8S with specified RGID. +func (lkc ListK8S) FilterByRGID(rgID uint64) ListK8S { + predicate := func(ikc ItemK8S) bool { + return ikc.RGID == rgID + } + + return lkc.FilterFunc(predicate) +} + +// FilterByStatus returns ListK8S with specified Status. +func (lkc ListK8S) FilterByStatus(status string) ListK8S { + predicate := func(ikc ItemK8S) bool { + return ikc.Status == status + } + + return lkc.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListK8S with specified TechStatus. +func (lkc ListK8S) FilterByTechStatus(techStatus string) ListK8S { + predicate := func(ikc ItemK8S) bool { + return ikc.TechStatus == techStatus + } + + return lkc.FilterFunc(predicate) +} + +// FilterByCreatedBy returns ListK8S created by specified user. +func (lkc ListK8S) FilterByCreatedBy(createdBy string) ListK8S { + predicate := func(ikc ItemK8S) bool { + return ikc.CreatedBy == createdBy + } + + return lkc.FilterFunc(predicate) +} + +// FilterByDeletedBy returns ListK8S deleted by specified user. +func (lkc ListK8S) FilterByDeletedBy(deletedBy string) ListK8S { + predicate := func(ikc ItemK8S) bool { + return ikc.DeletedBy == deletedBy + } + + return lkc.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListK8S based on a user-specified predicate. +func (lkc ListK8S) FilterFunc(predicate func(ItemK8S) bool) ListK8S { + var result ListK8S + + for _, item := range lkc.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemK8S +// If none was found, returns an empty struct. +func (lkc ListK8S) FindOne() ItemK8S { + if len(lkc.Data) == 0 { + return ItemK8S{} + } + + return lkc.Data[0] +} diff --git a/pkg/cloudbroker/k8s/filter_test.go b/pkg/cloudbroker/k8s/filter_test.go new file mode 100644 index 0000000..23c7233 --- /dev/null +++ b/pkg/cloudbroker/k8s/filter_test.go @@ -0,0 +1,200 @@ +package k8s + +import "testing" + +var k8sItems = ListK8S{ + Data: []ItemK8S{ + { + AccountID: 1, + AccountName: "test_1", + ACL: []interface{}{}, + BServiceID: 1, + CIID: 1, + Config: nil, + CreatedBy: "test_user", + CreatedTime: 132454563, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExtNetID: 1, + GID: 0, + GUID: 1, + ID: 1, + LBID: 1, + Milestones: 999999, + Name: "k8s_1", + RGID: 1, + RGName: "rg_1", + ServiceAccount: ServiceAccount{}, + SSHKey: "sample_key", + Status: "ENABLED", + TechStatus: "STARTED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 0, + WorkersGroup: []RecordK8SGroup{}, + }, + { + AccountID: 2, + AccountName: "test_2", + ACL: []interface{}{}, + BServiceID: 2, + CIID: 2, + Config: nil, + CreatedBy: "test_user", + CreatedTime: 132454638, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExtNetID: 2, + GID: 0, + GUID: 2, + ID: 2, + LBID: 2, + Milestones: 999999, + Name: "k8s_2", + RGID: 2, + RGName: "rg_2", + ServiceAccount: ServiceAccount{}, + SSHKey: "sample_key", + Status: "ENABLED", + TechStatus: "STARTED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 0, + WorkersGroup: []RecordK8SGroup{}, + }, + { + AccountID: 3, + AccountName: "test_3", + ACL: []interface{}{}, + BServiceID: 3, + CIID: 3, + Config: nil, + CreatedBy: "test_user", + CreatedTime: 132454682, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExtNetID: 3, + GID: 0, + GUID: 3, + ID: 3, + LBID: 3, + Milestones: 999999, + Name: "k8s_3", + RGID: 3, + RGName: "rg_3", + ServiceAccount: ServiceAccount{}, + SSHKey: "sample_key", + Status: "DISABLED", + TechStatus: "STOPPED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 0, + WorkersGroup: []RecordK8SGroup{}, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := k8sItems.FilterByID(1).FindOne() + + if actual.ID != 1 { + t.Fatal("expected 1 ID, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := k8sItems.FilterByName("k8s_3").FindOne() + + if actual.Name != "k8s_3" { + t.Fatal("expected Name 'k8s_3', found: ", actual.Name) + } +} + +func TestFilterByAccountID(t *testing.T) { + actual := k8sItems.FilterByAccountID(2).FindOne() + + if actual.AccountID != 2 { + t.Fatal("expected AccountID 2, found: ", actual.AccountID) + } +} + +func TestFilterByRGID(t *testing.T) { + actual := k8sItems.FilterByRGID(3).FindOne() + + if actual.RGID != 3 { + t.Fatal("expected RGID 3, found: ", actual.RGID) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := k8sItems.FilterByStatus("ENABLED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterByTechStatus(t *testing.T) { + actual := k8sItems.FilterByTechStatus("STARTED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.TechStatus != "STARTED" { + t.Fatal("expected TechStatus 'STARTED', found: ", item.TechStatus) + } + } +} + +func TestFilterByCreatedBy(t *testing.T) { + actual := k8sItems.FilterByCreatedBy("test_user") + + if len(actual.Data) != 3 { + t.Fatal("expected 3 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedBy != "test_user" { + t.Fatal("expected CreatedBy 'test_user', found: ", item.CreatedBy) + } + } +} + +func TestFilterByDeletedBy(t *testing.T) { + actual := k8sItems.FilterByDeletedBy("test_user") + + if len(actual.Data) != 0 { + t.Fatal("expected 0 found, actual: ", len(actual.Data)) + } +} + +func TestFilterFunc(t *testing.T) { + actual := k8sItems.FilterFunc(func(iks ItemK8S) bool { + return iks.AccountName == "test_2" + }). + FindOne() + + if actual.AccountName != "test_2" { + t.Fatal("expected AccountName 'test_2', found: ", actual.AccountName) + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := k8sItems.SortByCreatedTime(false) + + if actual.Data[0].CreatedTime != 132454563 || actual.Data[2].CreatedTime != 132454682 { + t.Fatal("expected ascending sort, seems to be inversed") + } +} diff --git a/pkg/cloudbroker/k8s/find_group_by_label.go b/pkg/cloudbroker/k8s/find_group_by_label.go new file mode 100644 index 0000000..8b50bca --- /dev/null +++ b/pkg/cloudbroker/k8s/find_group_by_label.go @@ -0,0 +1,49 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FindGroupByLabelRequest struct for get information about group of kubernetes cluster +type FindGroupByLabelRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // List of labels to search + // Required: true + Labels []string `url:"labels" json:"labels" validate:"min=1"` + + // If true and more than one label provided, select only groups that have all provided labels. + // If false - groups that have at least one label + // Required: false + Strict bool `url:"strict,omitempty" json:"strict,omitempty"` +} + +// FindGroupByLabel finds worker group information by one on more labels +func (k K8S) FindGroupByLabel(ctx context.Context, req FindGroupByLabelRequest) (ListK8SGroup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/findGroupByLabel" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListK8SGroup{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/k8s/get.go b/pkg/cloudbroker/k8s/get.go new file mode 100644 index 0000000..89f6fd1 --- /dev/null +++ b/pkg/cloudbroker/k8s/get.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about kubernetes cluster +type GetRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Get gets information about kubernetes cluster as a RecordK8S struct +func (k K8S) Get(ctx context.Context, req GetRequest) (*RecordK8S, error) { + res, err := k.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordK8S{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about kubernetes cluster as an array of bytes +func (k K8S) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/get" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/k8s/get_config.go b/pkg/cloudbroker/k8s/get_config.go new file mode 100644 index 0000000..fc0d586 --- /dev/null +++ b/pkg/cloudbroker/k8s/get_config.go @@ -0,0 +1,35 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetConfigRequest struct to get configuration of kubernetes cluster +type GetConfigRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// GetConfig gets configuration data to access kubernetes cluster +func (k K8S) GetConfig(ctx context.Context, req GetConfigRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/getConfig" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/get_node_annotations.go b/pkg/cloudbroker/k8s/get_node_annotations.go new file mode 100644 index 0000000..cabcce0 --- /dev/null +++ b/pkg/cloudbroker/k8s/get_node_annotations.go @@ -0,0 +1,39 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetNodeAnnotationsRequest struct to get node annotations +type GetNodeAnnotationsRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Node ID + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// GetNodeAnnotations gets kubernetes cluster worker node annotations +func (k K8S) GetNodeAnnotations(ctx context.Context, req GetNodeAnnotationsRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/getNodeAnnotations" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/get_node_labels.go b/pkg/cloudbroker/k8s/get_node_labels.go new file mode 100644 index 0000000..43257cb --- /dev/null +++ b/pkg/cloudbroker/k8s/get_node_labels.go @@ -0,0 +1,39 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetNodeLabelsRequest struct to get worker node labels +type GetNodeLabelsRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Compute ID of worker node + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// GetNodeLabels gets kubernetes cluster worker node labels +func (k K8S) GetNodeLabels(ctx context.Context, req GetNodeLabelsRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/getNodeLabels" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/get_node_taints.go b/pkg/cloudbroker/k8s/get_node_taints.go new file mode 100644 index 0000000..50cc436 --- /dev/null +++ b/pkg/cloudbroker/k8s/get_node_taints.go @@ -0,0 +1,39 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetNodeTaintsRequest struct to get node taints +type GetNodeTaintsRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Node ID + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// GetNodeTaints gets kubernetes cluster worker node taints +func (k K8S) GetNodeTaints(ctx context.Context, req GetNodeTaintsRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/getNodeTaints" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/get_worker_nodes_meta_data.go b/pkg/cloudbroker/k8s/get_worker_nodes_meta_data.go new file mode 100644 index 0000000..31eee81 --- /dev/null +++ b/pkg/cloudbroker/k8s/get_worker_nodes_meta_data.go @@ -0,0 +1,36 @@ +package k8s + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetWorkerNodesMetaDataRequest struct to get worker group metadata by ID +type GetWorkerNodesMetaDataRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` +} + +// GetWorkerNodesMetaData gets worker group metadata by ID +func (k K8S) GetWorkerNodesMetaData(ctx context.Context, req GetWorkerNodesMetaDataRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/getWorkerNodesMetaData" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/k8s/ids.go b/pkg/cloudbroker/k8s/ids.go new file mode 100644 index 0000000..10d465f --- /dev/null +++ b/pkg/cloudbroker/k8s/ids.go @@ -0,0 +1,30 @@ +package k8s + +// IDs gets array of K8SIDs from ListK8S struct +func (lk ListK8S) IDs() []uint64 { + res := make([]uint64, 0, len(lk.Data)) + for _, k := range lk.Data { + res = append(res, k.ID) + } + return res +} + +// IDs gets array of K8SWorkerGroupIDs from ListK8SGroups struct +func (lwg ListK8SGroup) IDs() []uint64 { + res := make([]uint64, 0, len(lwg)) + for _, wg := range lwg { + res = append(res, wg.ID) + } + return res +} + +// IDs gets array of Worker or Master ComputesIDs from ListDetailedInfo struct +func (ldi ListDetailedInfo) IDs() []uint64 { + res := make([]uint64, 0, len(ldi)) + for _, di := range ldi { + res = append(res, di.ID) + } + return res +} + + diff --git a/pkg/cloudbroker/k8s/k8s.go b/pkg/cloudbroker/k8s/k8s.go new file mode 100644 index 0000000..1fbf45f --- /dev/null +++ b/pkg/cloudbroker/k8s/k8s.go @@ -0,0 +1,18 @@ +// API for kubernetes clusters management +package k8s + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to K8S +type K8S struct { + client interfaces.Caller +} + +// Builder for K8S endpoints +func New(client interfaces.Caller) *K8S { + return &K8S{ + client: client, + } +} diff --git a/pkg/cloudbroker/k8s/list.go b/pkg/cloudbroker/k8s/list.go new file mode 100644 index 0000000..d93b383 --- /dev/null +++ b/pkg/cloudbroker/k8s/list.go @@ -0,0 +1,92 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list information K8S +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by IP address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by lbId + // Required: false + LBID uint64 `url:"lbId,omitempty" json:"lbId,omitempty"` + + // Find by basicServiceId + // Required: false + BasicServiceID uint64 `url:"basicServiceId,omitempty" json:"basicServiceId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by techStatus + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Include deleted clusters in result + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all kubernetes clusters as a ListK8S struct +func (k K8S) List(ctx context.Context, req ListRequest) (*ListK8S, error) { + + res, err := k.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListK8S{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all kubernetes clusters as an array of bytes +func (k K8S) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/list" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/k8s/list_deleted.go b/pkg/cloudbroker/k8s/list_deleted.go new file mode 100644 index 0000000..4eecc72 --- /dev/null +++ b/pkg/cloudbroker/k8s/list_deleted.go @@ -0,0 +1,77 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted kubernetes cluster +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by IP address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by lbId + // Required: false + LBID uint64 `url:"lbId,omitempty" json:"lbId,omitempty"` + + // Find by basicServiceId + // Required: false + BasicServiceID uint64 `url:"basicServiceId,omitempty" json:"basicServiceId,omitempty"` + + // Find by techStatus + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets all deleted kubernetes clusters +func (k K8S) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListK8S, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/listDeleted" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListK8S{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/k8s/models.go b/pkg/cloudbroker/k8s/models.go new file mode 100644 index 0000000..ed063bb --- /dev/null +++ b/pkg/cloudbroker/k8s/models.go @@ -0,0 +1,328 @@ +package k8s + +// Deteiled information +type ItemDetailedInfo struct { + // External Ip + ExternalIp string `json:"externalip"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` +} + +// List detailed information +type ListDetailedInfo []ItemDetailedInfo + +// Detailed information about K8S group +type RecordK8SGroup struct { + // List annotations + Annotations []string `json:"annotations"` + + // Number of CPU + CPU uint64 `json:"cpu"` + + // List detailed information + DetailedInfo ListDetailedInfo `json:"detailedInfo"` + + // Disk + Disk uint64 `json:"disk"` + + // GUID + GUID string `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // List labels + Labels []string `json:"labels"` + + // Name + Name string `json:"name"` + + // Number + Num uint64 `json:"num"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // List taints + Taints []string `json:"taints"` +} + +// List K8S groups +type ListK8SGroup []RecordK8SGroup + +// Detailed information about K8S +type RecordK8S struct { + // Access Control List + ACL RecordACLGroup `json:"ACL"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Basic service ID + BServiceID uint64 `json:"bserviceId"` + + // CI ID + CIID uint64 `json:"ciId"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Only external network + ExtnetOnly bool `json:"extnetOnly"` + + // Highly available LB + HighlyAvailableLB bool `json:"highlyAvailableLB"` + + // Address Virtual Internet Protocol + AddressVIP K8SAddressVIP `json:"addressVip"` + + // ID + ID uint64 `json:"id"` + + // K8CI name + K8CIName string `json:"k8ciName"` + + // Detailed information about K8S groups + K8SGroups RecordK8SGroups `json:"k8sGroups"` + + // Load balancer ID + LBID uint64 `json:"lbId"` + + // Name + Name string `json:"name"` + + // Network plugin + NetworkPlugin string `json:"networkPlugin"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // With LB + WithLB bool `json:"withLB"` +} + +// Detailed info about address of the Virtual Internet Protocol +type K8SAddressVIP struct { + // Backend IP + BackendIP string `json:"backendIp"` + + // Frontend IP + FrontendIP string `json:"frontendIp"` +} + +// Detailed info about K8S groups +type RecordK8SGroups struct { + // Master group + Masters MasterGroup `json:"masters"` + + // Worker group + Workers ListK8SGroup `json:"workers"` +} + +// Detailed information about master group +type MasterGroup struct { + // Number of CPU + CPU uint64 `json:"cpu"` + + // Detailed information + DetailedInfo ListDetailedInfo `json:"detailedInfo"` + + // Disk + Disk uint64 `json:"disk"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Number + Num uint64 `json:"num"` + + // Number of RAM + RAM uint64 `json:"ram"` +} + +// Detailed information of access control +type RecordACLGroup struct { + // Account ACL + AccountACL ListACL `json:"accountAcl"` + + // K8S ACL + K8SACL ListACL `json:"k8sAcl"` + + // RG ACL + RGACL ListACL `json:"rgAcl"` +} + +// Access Control List +type ACL struct { + // Explicit + Explicit bool `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Right + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// List ACL +type ListACL []ACL + +// Main information about K8S +type ItemK8S struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Access Control List + ACL []interface{} `json:"acl"` + + // Basic service ID + BServiceID uint64 `json:"bserviceId"` + + // CI ID + CIID uint64 `json:"ciId"` + + // Config + Config interface{} `json:"config"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // External network ID + ExtNetID uint64 `json:"extnetId"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Load balancer ID + LBID uint64 `json:"lbId"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network plugin + NetworkPlugin string `json:"networkPlugin"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Service account + ServiceAccount ServiceAccount `json:"serviceAccount"` + + // SSH key + SSHKey string `json:"sshKey"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // VINS ID + VINSID uint64 `json:"vinsId"` + + // List workers + WorkersGroup ListK8SGroup `json:"workersGroups"` +} + +// Service account +type ServiceAccount struct { + // GUID + GUID string `json:"guid"` + + // Password + Password string `json:"password"` + + // Username + Username string `json:"username"` +} + +// List K8S +type ListK8S struct { + // Data + Data []ItemK8S `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/k8s/restore.go b/pkg/cloudbroker/k8s/restore.go new file mode 100644 index 0000000..eb62354 --- /dev/null +++ b/pkg/cloudbroker/k8s/restore.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore kubernetes cluster +type RestoreRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Restore restores kubernetes cluster from recycle bin +func (k K8S) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/restore" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/serialize.go b/pkg/cloudbroker/k8s/serialize.go new file mode 100644 index 0000000..b4395f9 --- /dev/null +++ b/pkg/cloudbroker/k8s/serialize.go @@ -0,0 +1,43 @@ +package k8s + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lkc ListK8S) Serialize(params ...string) (serialization.Serialized, error) { + if len(lkc.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lkc, prefix, indent) + } + + return json.Marshal(lkc) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ikc ItemK8S) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ikc, prefix, indent) + } + + return json.Marshal(ikc) +} diff --git a/pkg/cloudbroker/k8s/sorting.go b/pkg/cloudbroker/k8s/sorting.go new file mode 100644 index 0000000..1bad9a4 --- /dev/null +++ b/pkg/cloudbroker/k8s/sorting.go @@ -0,0 +1,60 @@ +package k8s + +import "sort" + +// SortByCreatedTime sorts ListK8S by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lkc ListK8S) SortByCreatedTime(inverse bool) ListK8S { + if len(lkc.Data) < 2 { + return lkc + } + + sort.Slice(lkc.Data, func(i, j int) bool { + if inverse { + return lkc.Data[i].CreatedTime > lkc.Data[j].CreatedTime + } + + return lkc.Data[i].CreatedTime < lkc.Data[j].CreatedTime + }) + + return lkc +} + +// SortByUpdatedTime sorts ListK8S by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lkc ListK8S) SortByUpdatedTime(inverse bool) ListK8S { + if len(lkc.Data) < 2 { + return lkc + } + + sort.Slice(lkc.Data, func(i, j int) bool { + if inverse { + return lkc.Data[i].UpdatedTime > lkc.Data[j].UpdatedTime + } + + return lkc.Data[i].UpdatedTime < lkc.Data[j].UpdatedTime + }) + + return lkc +} + +// SortByDeletedTime sorts ListK8S by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lkc ListK8S) SortByDeletedTime(inverse bool) ListK8S { + if len(lkc.Data) < 2 { + return lkc + } + + sort.Slice(lkc.Data, func(i, j int) bool { + if inverse { + return lkc.Data[i].DeletedTime > lkc.Data[j].DeletedTime + } + + return lkc.Data[i].DeletedTime < lkc.Data[j].DeletedTime + }) + + return lkc +} diff --git a/pkg/cloudbroker/k8s/start.go b/pkg/cloudbroker/k8s/start.go new file mode 100644 index 0000000..2a39f77 --- /dev/null +++ b/pkg/cloudbroker/k8s/start.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StartRequest struct to start kubernetes cluster +type StartRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Start starts kubernetes cluster by ID +func (k K8S) Start(ctx context.Context, req StartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/start" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/stop.go b/pkg/cloudbroker/k8s/stop.go new file mode 100644 index 0000000..104f1d8 --- /dev/null +++ b/pkg/cloudbroker/k8s/stop.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StopRequest struct to stop kubernetes cluster +type StopRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` +} + +// Stop stops kubernetes cluster by ID +func (k K8S) Stop(ctx context.Context, req StopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/stop" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/update.go b/pkg/cloudbroker/k8s/update.go new file mode 100644 index 0000000..f22e6b0 --- /dev/null +++ b/pkg/cloudbroker/k8s/update.go @@ -0,0 +1,48 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update kubernetes cluster +type UpdateRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // New name to set. + // If empty string is passed, name is not updated + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New description to set. + // If empty string is passed, description is not updated + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +// Update updates name or description of kubernetes cluster +func (k K8S) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/update" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/update_worker_nodes_meta_data.go b/pkg/cloudbroker/k8s/update_worker_nodes_meta_data.go new file mode 100644 index 0000000..78dc201 --- /dev/null +++ b/pkg/cloudbroker/k8s/update_worker_nodes_meta_data.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateWorkerNodesMetaDataRequest struct to add worker to a kubernetes cluster +type UpdateWorkerNodesMetaDataRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // Meta data for working group computes, format YAML "user_data": 1111 + // Required: true + UserData string `url:"userData" json:"userData" validate:"required"` +} + +// UpdateWorkerNodesMetaData adds worker nodes to a kubernetes cluster +func (k K8S) UpdateWorkerNodesMetaData(ctx context.Context, req UpdateWorkerNodesMetaDataRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/updateWorkerNodesMetaData" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/worker_add.go b/pkg/cloudbroker/k8s/worker_add.go new file mode 100644 index 0000000..c14b073 --- /dev/null +++ b/pkg/cloudbroker/k8s/worker_add.go @@ -0,0 +1,52 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkerAddRequest struct to add worker to a kubernetes cluster +type WorkerAddRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // How many worker nodes to add + // Required: false + Num uint64 `url:"num,omitempty" json:"num,omitempty"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` +} + +// WorkerAdd adds worker nodes to a kubernetes cluster +func (k K8S) WorkerAdd(ctx context.Context, req WorkerAddRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/workerAdd" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + result := make([]uint64, 0) + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/worker_reset.go b/pkg/cloudbroker/k8s/worker_reset.go new file mode 100644 index 0000000..1d1cbc0 --- /dev/null +++ b/pkg/cloudbroker/k8s/worker_reset.go @@ -0,0 +1,46 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkerResetRequest struct for hard reset kubernetes cluster +type WorkerResetRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // Compute ID of worker node to reset + // Required: true + WorkerID uint64 `url:"workerId" json:"workerId" validate:"required"` +} + +// WorkerReset hard reset (compute start + stop) worker node of the kubernetes cluster +func (k K8S) WorkerReset(ctx context.Context, req WorkerResetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/workerReset" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/worker_restart.go b/pkg/cloudbroker/k8s/worker_restart.go new file mode 100644 index 0000000..b42c19f --- /dev/null +++ b/pkg/cloudbroker/k8s/worker_restart.go @@ -0,0 +1,45 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkerRestartRequest struct to restart worker node +type WorkerRestartRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the workers compute group + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` + + // Compute ID of worker node to restart + // Required: true + WorkerID uint64 `url:"workerId" json:"workerId" validate:"required"` +} + +// WorkerRestart soft restart (reboot OS) worker node of the kubernetes cluster +func (k8s K8S) WorkerRestart(ctx context.Context, req WorkerRestartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/workerRestart" + + res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + return result, nil +} diff --git a/pkg/cloudbroker/k8s/workers_group_add.go b/pkg/cloudbroker/k8s/workers_group_add.go new file mode 100644 index 0000000..c4704f1 --- /dev/null +++ b/pkg/cloudbroker/k8s/workers_group_add.go @@ -0,0 +1,98 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkersGroupAddRequest struct to add workers group +type WorkersGroupAddRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Worker group name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of SEP to create boot disks for default worker nodes group. + // Uses images SEP ID if not set + // Required: false + WorkerSEPID uint64 `url:"workerSepId,omitempty" json:"workerSepId,omitempty"` + + // Pool to use if worker SEP ID is set, can be also empty if + // needed to be chosen by system + // Required: false + WorkerSEPPool string `url:"workerSepPool,omitempty" json:"workerSepPool,omitempty"` + + // List of strings with labels for worker group + // i.e: ["label1=value1", "label2=value2"] + // Required: false + Labels []string `url:"labels,omitempty" json:"labels,omitempty"` + + // List of strings with taints for worker group + // i.e: ["key1=value1:NoSchedule", "key2=value2:NoExecute"] + // Required: false + Taints []string `url:"taints,omitempty" json:"taints,omitempty"` + + // List of strings with annotations for worker group + // i.e: ["key1=value1", "key2=value2"] + // Required: false + Annotations []string `url:"annotations,omitempty" json:"annotations,omitempty"` + + // Number of worker nodes to create + // Required: false + WorkerNum uint64 `url:"workerNum,omitempty" json:"workerNum,omitempty"` + + // Worker node CPU count + // Required: false + WorkerCPU uint64 `url:"workerCpu,omitempty" json:"workerCpu,omitempty"` + + // Worker node RAM volume in MB + // Required: false + WorkerRAM uint64 `url:"workerRam,omitempty" json:"workerRam,omitempty"` + + // Worker node boot disk size in GB If 0 is specified, size is defined by the OS image size + // Required: false + WorkerDisk uint64 `url:"workerDisk,omitempty" json:"workerDisk,omitempty"` + + // Meta data for working group computes, format YAML "user_data": 1111 + // Required: false + UserData string `url:"userData,omitempty" json:"userData,omitempty"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` +} + +// GetRAM returns RAM field values +func (r WorkersGroupAddRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["WorkerRAM"] = r.WorkerRAM + + return res +} + +// WorkersGroupAdd adds workers group to kubernetes cluster +func (k K8S) WorkersGroupAdd(ctx context.Context, req WorkersGroupAddRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/workersGroupAdd" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/workers_group_delete.go b/pkg/cloudbroker/k8s/workers_group_delete.go new file mode 100644 index 0000000..afb669a --- /dev/null +++ b/pkg/cloudbroker/k8s/workers_group_delete.go @@ -0,0 +1,42 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkersGroupDeleteRequest struct to delete workers group +type WorkersGroupDeleteRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Worker group ID + // Required: true + WorkersGroupID uint64 `url:"workersGroupId" json:"workersGroupId" validate:"required"` +} + +// WorkersGroupDelete deletes workers group from kubernetes cluster +func (k K8S) WorkersGroupDelete(ctx context.Context, req WorkersGroupDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/workersGroupDelete" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/k8s/workers_group_get_by_name.go b/pkg/cloudbroker/k8s/workers_group_get_by_name.go new file mode 100644 index 0000000..e83e961 --- /dev/null +++ b/pkg/cloudbroker/k8s/workers_group_get_by_name.go @@ -0,0 +1,44 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// WorkersGroupGetByNameRequest struct to get information about worker group +type WorkersGroupGetByNameRequest struct { + // Kubernetes cluster ID + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // Worker group name + // Required: true + GroupName string `url:"groupName" json:"groupName" validate:"required"` +} + +// WorkersGroupGetByName gets worker group metadata by name +func (k K8S) WorkersGroupGetByName(ctx context.Context, req WorkersGroupGetByNameRequest) (*RecordK8SGroup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/workersGroupGetByName" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordK8SGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/kvmx86.go b/pkg/cloudbroker/kvmx86.go new file mode 100644 index 0000000..c195900 --- /dev/null +++ b/pkg/cloudbroker/kvmx86.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/kvmx86" +) + +// Accessing the KVMX86 method group +func (cb *CloudBroker) KVMX86() *kvmx86.KVMX86 { + return kvmx86.New(cb.client) +} diff --git a/pkg/cloudbroker/kvmx86/create.go b/pkg/cloudbroker/kvmx86/create.go new file mode 100644 index 0000000..1cbaddc --- /dev/null +++ b/pkg/cloudbroker/kvmx86/create.go @@ -0,0 +1,247 @@ +package kvmx86 + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type Interface struct { + // Network type + // Should be one of: + // - VINS + // - EXTNET + // - VFNIC + // - DPDK + NetType string `url:"netType" json:"netType" validate:"required,kvmx86NetType"` + + // Network ID for connect to, + // for EXTNET - external network ID, + // for VINS - VINS ID, + NetID uint64 `url:"netId" json:"netId" validate:"required"` + + // IP address to assign to this VM when connecting to the specified network + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // Maximum transmission unit, must be 1-9216 + // Used only to DPDK net type + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` +} + +// DataDisk detailed struct for DataDisks field in CreateRequest, CreateBlankRequest and MassCreateRequest +type DataDisk struct { + // Name for disk + // Required: true + DiskName string `url:"diskName" json:"diskName" validate:"required"` + + // Disk size in GB + // Required: true + Size uint64 `url:"size" json:"size" validate:"required"` + + // Storage endpoint provider ID + // By default the same with boot disk + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool name + // By default will be chosen automatically + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Optional description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Specify image id for create disk from template + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` +} + +// CreateRequest struct to create KVM x86 VM +type CreateRequest struct { + // ID of the resource group, which will own this VM + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of this VM. + // Must be unique among all VMs (including those in DELETED state) in target resource group + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Number CPUs to allocate to this VM + // Required: true + CPU uint64 `url:"cpu" json:"cpu" validate:"required"` + + // Volume of RAM in MB to allocate to this VM + // Required: true + RAM uint64 `url:"ram" json:"ram" validate:"required"` + + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + + // ID of the OS image to base this VM on; + // Could be boot disk image or CD-ROM image + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` + + // Size of the boot disk in GB + // Required: false + BootDisk uint64 `url:"bootDisk,omitempty" json:"bootDisk,omitempty"` + + // ID of SEP to create boot disk on. + // Uses images SEP ID if not set + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool to use if SEP ID is set, can be also empty if needed to be chosen by system + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Slice of structs with data disk description. Each disk has parameters: required - diskName, size; optional - sepId, pool, desc and imageId. + // If not specified, compute will be created without disks. + // To create compute without disks, pass initialized empty slice . + // Required: false + DataDisks []DataDisk `url:"-" json:"dataDisks,omitempty" validate:"omitempty,dive"` + + // Slice of structs with net interface description. + // If not specified, compute will be created with default interface from RG. + // To create compute without interfaces, pass initialized empty slice . + // Required: false + Interfaces []Interface `url:"-" json:"interfaces,omitempty" validate:"omitempty,dive"` + + // Input data for cloud-init facility + // Required: false + Userdata string `url:"userdata,omitempty" json:"userdata,omitempty"` + + // Text description of this VM + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Start VM upon success + // Required: false + Start bool `url:"start" json:"start"` + + // Stack ID + // Required: false + StackID uint64 `url:"stackId,omitempty" json:"stackId,omitempty"` + + // System name + // Required: false + IS string `url:"IS,omitempty" json:"IS,omitempty"` + + // Compute purpose + // Required: false + IPAType string `url:"ipaType,omitempty" json:"ipaType,omitempty"` + + // Custom fields for Compute. Must be dict + // Required: false + CustomField string `url:"customFields,omitempty" json:"customFields,omitempty"` + + //Type of compute Stateful (KVM_X86) or Stateless (SVA_KVM_X86) + // Required: false + Driver string `url:"driver,omitempty" json:"driver,omitempty"` + + // Rule for VM placement with NUMA affinity. + // Possible values - none (placement without NUMA affinity), + // strict (strictly with NUMA affinity, if not possible - do not start VM), + // loose (use NUMA affinity if possible) + // Required: false + // Default: none + NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"` + + // Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node + // Required: false + // Default: false + CPUPin bool `url:"cpupin" json:"cpupin"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node + // Required: false + // Default: false + HPBacked bool `url:"hpBacked" json:"hpBacked"` +} + +// GetRAM returns RAM field values +func (r CreateRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +type wrapperCreateRequest struct { + CreateRequest + Interfaces []string `url:"interfaces,omitempty"` + DataDisks []string `url:"dataDisks,omitempty"` +} + +// Create creates KVM PowerPC VM based on specified OS image +func (k KVMX86) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var interfaces []string + + if req.Interfaces != nil && len(req.Interfaces) != 0 { + interfaces = make([]string, 0, len(req.Interfaces)) + + for i := range req.Interfaces { + b, err := json.Marshal(req.Interfaces[i]) + if err != nil { + return 0, err + } + + interfaces = append(interfaces, string(b)) + } + } else if req.Interfaces != nil && len(req.Interfaces) == 0 { + interfaces = []string{"[]"} + } + + var dataDisks []string + + if req.DataDisks != nil && len(req.DataDisks) != 0 { + dataDisks = make([]string, 0, len(req.DataDisks)) + + for i := range req.DataDisks { + b, err := json.Marshal(req.DataDisks[i]) + if err != nil { + return 0, err + } + + dataDisks = append(dataDisks, string(b)) + } + } + + reqWrapped := wrapperCreateRequest{ + CreateRequest: req, + Interfaces: interfaces, + DataDisks: dataDisks, + } + + url := "/cloudbroker/kvmx86/create" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/kvmx86/create_blank.go b/pkg/cloudbroker/kvmx86/create_blank.go new file mode 100644 index 0000000..cc562ca --- /dev/null +++ b/pkg/cloudbroker/kvmx86/create_blank.go @@ -0,0 +1,148 @@ +package kvmx86 + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateBlankRequest struct to create KVM x86 VM from scratch +type CreateBlankRequest struct { + // ID of the resource group, which will own this VM + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of this VM. + // Must be unique among all VMs (including those in DELETED state) in target resource group + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Number CPUs to allocate to this VM + // Required: true + CPU uint64 `url:"cpu" json:"cpu" validate:"required"` + + // Volume of RAM in MB to allocate to this VM + // Required: true + RAM uint64 `url:"ram" json:"ram" validate:"required"` + + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + + // Size of the boot disk in GB + // Required: false + BootDisk uint64 `url:"bootDisk,omitempty" json:"bootDisk,omitempty"` + + // ID of SEP to create boot disk on. + // Uses images SEP ID if not set + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool to use if sepId is set, can be also empty if needed to be chosen by system + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Slice of structs with data disk description. Each disk has parameters: required - diskName, size; optional - sepId, pool, desc and imageId. + // If not specified, compute will be created without disks. + // To create compute without disks, pass initialized empty slice . + // Required: false + DataDisks []DataDisk `url:"-" json:"dataDisks,omitempty" validate:"omitempty,dive"` + + // Slice of structs with net interface description. + // If not specified, compute will be created with default interface from RG. + // To create compute without interfaces, pass initialized empty slice . + // Required: false + Interfaces []Interface `url:"-" json:"interfaces,omitempty" validate:"omitempty,dive"` + + // Text description of this VM + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + //Type of compute Stateful (KVM_X86) or Stateless (SVA_KVM_X86) + // Required: false + Driver string `url:"driver,omitempty" json:"driver,omitempty"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` +} + +// GetRAM returns RAM field values +func (r CreateBlankRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +type wrapperCreateBlankRequest struct { + CreateBlankRequest + Interfaces []string `url:"interfaces,omitempty"` + DataDisks []string `url:"dataDisks,omitempty"` +} + +// CreateBlank creates KVM x86 VM from scratch +func (k KVMX86) CreateBlank(ctx context.Context, req CreateBlankRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var interfaces []string + + if req.Interfaces != nil && len(req.Interfaces) != 0 { + interfaces = make([]string, 0, len(req.Interfaces)) + + for i := range req.Interfaces { + b, err := json.Marshal(req.Interfaces[i]) + if err != nil { + return 0, err + } + + interfaces = append(interfaces, string(b)) + } + } else if req.Interfaces != nil && len(req.Interfaces) == 0 { + interfaces = []string{"[]"} + } + + var dataDisks []string + + if req.DataDisks != nil && len(req.DataDisks) != 0 { + dataDisks = make([]string, 0, len(req.DataDisks)) + + for i := range req.DataDisks { + b, err := json.Marshal(req.DataDisks[i]) + if err != nil { + return 0, err + } + + dataDisks = append(dataDisks, string(b)) + } + } + + reqWrapped := wrapperCreateBlankRequest{ + CreateBlankRequest: req, + Interfaces: interfaces, + DataDisks: dataDisks, + } + + url := "/cloudbroker/kvmx86/createBlank" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil + +} diff --git a/pkg/cloudbroker/kvmx86/kvmx86.go b/pkg/cloudbroker/kvmx86/kvmx86.go new file mode 100644 index 0000000..6cff0f9 --- /dev/null +++ b/pkg/cloudbroker/kvmx86/kvmx86.go @@ -0,0 +1,16 @@ +// API to manage KVM x86 compute instances (x86 VMs) +package kvmx86 + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to KVMX86 +type KVMX86 struct { + client interfaces.Caller +} + +// Builder for KVMX86 endpoints +func New(client interfaces.Caller) *KVMX86 { + return &KVMX86{ + client: client, + } +} diff --git a/pkg/cloudbroker/kvmx86/mass_create.go b/pkg/cloudbroker/kvmx86/mass_create.go new file mode 100644 index 0000000..a026bd8 --- /dev/null +++ b/pkg/cloudbroker/kvmx86/mass_create.go @@ -0,0 +1,231 @@ +package kvmx86 + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassCreateRequest struct to mass create KVM x86 +type MassCreateRequest struct { + // ID of the resource group, which will own this VM + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of this VM. + // Must be unique among all VMs (including those in DELETED state) in target resource group + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Number of VMs + // Required: true + Count uint64 `url:"count" json:"count" validate:"required"` + + // Number CPUs to allocate to this VM + // Required: true + CPU uint64 `url:"cpu" json:"cpu" validate:"required"` + + // Volume of RAM in MB to allocate to this VM + // Required: true + RAM uint64 `url:"ram" json:"ram" validate:"required"` + + // If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state + // Required: false + WithoutBootDisk bool `url:"withoutBootDisk" json:"withoutBootDisk"` + + // ID of the OS image to base this VM on; Could be boot disk image or CD-ROM image + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` + + // Size of the boot disk in GB + // Required: false + BootDisk uint64 `url:"bootDisk,omitempty" json:"bootDisk,omitempty"` + + // ID of SEP to create boot disk on. + // Uses images SEP ID if not set + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Pool to use if SEP ID is set, can be also empty if needed to be chosen by system + // Required: false + Pool string `url:"pool,omitempty" json:"pool,omitempty"` + + // Slice of structs with data disk description. Each disk has parameters: required - diskName, size; optional - sepId, pool, desc and imageId. + // If not specified, compute will be created without disks. + // To create compute without disks, pass initialized empty slice . + // Required: false + DataDisks []DataDisk `url:"-" json:"dataDisks,omitempty" validate:"omitempty,dive"` + + // Slice of structs with net interface description. + // If not specified, compute will be created with default interface from RG. + // To create compute without interfaces, pass initialized empty slice . + // Required: false + Interfaces []Interface `url:"-" json:"interfaces,omitempty" validate:"omitempty,dive"` + + // Input data for cloud-init facility + // Required: false + UserData string `url:"userdata,omitempty" json:"userdata,omitempty"` + + // Text description of this VM + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Start after create of not + // Required: false + Start bool `url:"start" json:"start"` + + // Type of the emulated system, Q35 or i440fx + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` +} + +type asyncWrapperMassCreateRequest struct { + wrapperMassCreateRequest + AsyncMode bool `url:"asyncMode"` +} + +// GetRAM returns RAM field values +func (r MassCreateRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +type wrapperMassCreateRequest struct { + MassCreateRequest + Interfaces []string `url:"interfaces,omitempty"` + DataDisks []string `url:"dataDisks,omitempty"` +} + +// MassCreate creates KVM x86 computes based on specified OS image +func (k KVMX86) MassCreate(ctx context.Context, req MassCreateRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + var interfaces []string + + if req.Interfaces != nil && len(req.Interfaces) != 0 { + interfaces = make([]string, 0, len(req.Interfaces)) + + for i := range req.Interfaces { + b, err := json.Marshal(req.Interfaces[i]) + if err != nil { + return nil, err + } + + interfaces = append(interfaces, string(b)) + } + } else if req.Interfaces != nil && len(req.Interfaces) == 0 { + interfaces = []string{"[]"} + } + + var dataDisks []string + + if req.DataDisks != nil && len(req.DataDisks) != 0 { + dataDisks = make([]string, 0, len(req.DataDisks)) + + for i := range req.DataDisks { + b, err := json.Marshal(req.DataDisks[i]) + if err != nil { + return nil, err + } + + dataDisks = append(dataDisks, string(b)) + } + } + + reqWrapped := wrapperMassCreateRequest{ + MassCreateRequest: req, + Interfaces: interfaces, + DataDisks: dataDisks, + } + + finalReq := asyncWrapperMassCreateRequest{wrapperMassCreateRequest: reqWrapped, AsyncMode: false} + + url := "/cloudbroker/kvmx86/massCreate" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, finalReq) + if err != nil { + return nil, err + } + + computes := make([]uint64, 0) + + err = json.Unmarshal(res, &computes) + if err != nil { + return nil, err + } + + return computes, nil +} + +// MassCreate creates KVM x86 computes based on specified OS image in async mode +func (k KVMX86) MassCreateAsync(ctx context.Context, req MassCreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + var interfaces []string + + if req.Interfaces != nil && len(req.Interfaces) != 0 { + interfaces = make([]string, 0, len(req.Interfaces)) + + for i := range req.Interfaces { + b, err := json.Marshal(req.Interfaces[i]) + if err != nil { + return "", err + } + + interfaces = append(interfaces, string(b)) + } + } else if req.Interfaces != nil && len(req.Interfaces) == 0 { + interfaces = []string{"[]"} + } + + var dataDisks []string + + if req.DataDisks != nil && len(req.DataDisks) != 0 { + dataDisks = make([]string, 0, len(req.DataDisks)) + + for i := range req.DataDisks { + b, err := json.Marshal(req.DataDisks[i]) + if err != nil { + return "", err + } + + dataDisks = append(dataDisks, string(b)) + } + } + + reqWrapped := wrapperMassCreateRequest{ + MassCreateRequest: req, + Interfaces: interfaces, + DataDisks: dataDisks, + } + + finalReq := asyncWrapperMassCreateRequest{wrapperMassCreateRequest: reqWrapped, AsyncMode: true} + + url := "/cloudbroker/kvmx86/massCreate" + + res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, finalReq) + if err != nil { + return "", err + } + + var computes string + + err = json.Unmarshal(res, &computes) + if err != nil { + return "", err + } + + return computes, nil +} diff --git a/pkg/cloudbroker/lb.go b/pkg/cloudbroker/lb.go new file mode 100644 index 0000000..c5eb7f2 --- /dev/null +++ b/pkg/cloudbroker/lb.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/lb" + +// Accessing the LB method group +func (cb *CloudBroker) LB() *lb.LB { + return lb.New(cb.client) +} diff --git a/pkg/cloudbroker/lb/backend_create.go b/pkg/cloudbroker/lb/backend_create.go new file mode 100644 index 0000000..3101a28 --- /dev/null +++ b/pkg/cloudbroker/lb/backend_create.go @@ -0,0 +1,90 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendCreateRequest struct to create backend +type BackendCreateRequest struct { + // ID of the load balancer instance to backendCreate + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must be unique among all backends of this load balancer - name of the new backend to create + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Algorithm + // Should be one of: + // - roundrobin + // - static-rr + // - leastconn + // Required: false + Algorithm string `url:"algorithm,omitempty" json:"algorithm,omitempty" validate:"omitempty,lbAlgorithm"` + + // Interval in milliseconds between two consecutive availability + // checks of the server that is considered available + // Required: false + Inter uint64 `url:"inter,omitempty" json:"inter,omitempty"` + + // Interval in milliseconds between two consecutive checks to + // restore the availability of a server that is currently considered unavailable + // Required: false + DownInter uint64 `url:"downinter,omitempty" json:"downinter,omitempty"` + + // Number of checks that the server must pass in order to get the available status + // and be included in the balancing scheme again + // Required: false + Rise uint64 `url:"rise,omitempty" json:"rise,omitempty"` + + // Number of consecutive failed availability checks, + // after which the previously considered available server receives the status of + // unavailable and is temporarily excluded from the balancing scheme + // Required: false + Fall uint64 `url:"fall,omitempty" json:"fall,omitempty"` + + // Interval in milliseconds from the moment the server receives the available status, + // after which the number of actually allowed connections to this server will be returned to 100% of the set limit + // Required: false + SlowStart uint64 `url:"slowstart,omitempty" json:"slowstart,omitempty"` + + // Limit of simultaneous connections to the server. When this limit is reached, + // the server is temporarily excluded from the balancing scheme + // Required: false + MaxConn uint64 `url:"maxconn,omitempty" json:"maxconn,omitempty"` + + // Limit of connections waiting in the queue. + // When this limit is reached, all subsequent connections will be forwarded to other servers + // Required: false + MaxQueue uint64 `url:"maxqueue,omitempty" json:"maxqueue,omitempty"` + + // Server weight for use in weight balancing algorithms + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` +} + +// BackendCreate creates new backend on the specified load balancer +func (lb LB) BackendCreate(ctx context.Context, req BackendCreateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/backendCreate" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/backend_delete.go b/pkg/cloudbroker/lb/backend_delete.go new file mode 100644 index 0000000..426133a --- /dev/null +++ b/pkg/cloudbroker/lb/backend_delete.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendDeleteRequest struct to delete backend +type BackendDeleteRequest struct { + // ID of the load balancer instance to BackendDelete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Cannot be emtpy string - name of the backend to delete + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` +} + +// BackendDelete deletes backend from the specified load balancer. +// Warning: you cannot undo this action! +func (lb LB) BackendDelete(ctx context.Context, req BackendDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/backendDelete" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/backend_server_add.go b/pkg/cloudbroker/lb/backend_server_add.go new file mode 100644 index 0000000..19b88f2 --- /dev/null +++ b/pkg/cloudbroker/lb/backend_server_add.go @@ -0,0 +1,95 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendServerAddRequest struct to add server definition to the backend +type BackendServerAddRequest struct { + // ID of the load balancer instance to BackendServerAdd + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must match one of the existing backens - name of the backend to add servers to + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Must be unique among all servers defined for this backend - name of the server definition to add + // Required: true + ServerName string `url:"serverName" json:"serverName" validate:"required"` + + // IP address of the server + // Required: true + Address string `url:"address" json:"address" validate:"required"` + + // Port number on the server + // Required: true + Port uint64 `url:"port" json:"port" validate:"required"` + + // Set to disabled if this server should be used regardless of its state + // Required: false + Check string `url:"check,omitempty" json:"check,omitempty"` + + // Interval in milliseconds between two consecutive availability checks of the server that is considered available + // Required: false + Inter uint64 `url:"inter,omitempty" json:"inter,omitempty"` + + // Interval in milliseconds between two consecutive checks to restore + // the availability of a server that is currently considered unavailable + // Required: false + DownInter uint64 `url:"downinter,omitempty" json:"downinter,omitempty"` + + // Number of checks that the server must pass in order to get + // the available status and be included in the balancing scheme again + // Required: false + Rise uint64 `url:"rise,omitempty" json:"rise,omitempty"` + + // Number of consecutive failed availability checks, + // after which the previously considered available server receives the status of unavailable and + // is temporarily excluded from the balancing scheme + // Required: false + Fall uint64 `url:"fall,omitempty" json:"fall,omitempty"` + + // Interval in milliseconds from the moment the server receives the available status, + // after which the number of actually allowed connections to this server will be returned to 100% of the set limit + // Required: false + SlowStart uint64 `url:"slowstart,omitempty" json:"slowstart,omitempty"` + + // Limit of simultaneous connections to the server. When this limit is reached, the server is temporarily excluded from the balancing scheme + // Required: false + MaxConn uint64 `url:"maxconn,omitempty" json:"maxconn,omitempty"` + + // Limit of connections waiting in the queue. When this limit is reached, all subsequent connections will be forwarded to other servers + // Required: false + MaxQueue uint64 `url:"maxqueue,omitempty" json:"maxqueue,omitempty"` + + // Server weight for use in weight balancing algorithms + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` +} + +// BackendServerAdd adds server definition to the backend on the specified load balancer +func (lb LB) BackendServerAdd(ctx context.Context, req BackendServerAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/backendServerAdd" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/backend_server_delete.go b/pkg/cloudbroker/lb/backend_server_delete.go new file mode 100644 index 0000000..4c6f940 --- /dev/null +++ b/pkg/cloudbroker/lb/backend_server_delete.go @@ -0,0 +1,47 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendServerDeleteRequest struct to delete server definition +type BackendServerDeleteRequest struct { + // ID of the load balancer instance to BackendServerDelete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must match one of the existing backens - name of the backend to add servers to + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Must be unique among all servers defined for this backend - name of the server definition to add + // Required: true + ServerName string `url:"serverName" json:"serverName" validate:"required"` +} + +// BackendServerDelete deletes server definition from the backend on the specified load balancer. +// Warning: you cannot undo this action! +func (lb LB) BackendServerDelete(ctx context.Context, req BackendServerDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/backendServerDelete" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/backend_server_update.go b/pkg/cloudbroker/lb/backend_server_update.go new file mode 100644 index 0000000..0fd3614 --- /dev/null +++ b/pkg/cloudbroker/lb/backend_server_update.go @@ -0,0 +1,95 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendServerUpdateRequest struct to update server +type BackendServerUpdateRequest struct { + // ID of the load balancer instance to BackendServerAdd + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must match one of the existing backens - name of the backend to add servers to + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Must be unique among all servers defined for this backend - name of the server definition to add + // Required: true + ServerName string `url:"serverName" json:"serverName" validate:"required"` + + // IP address of the server + // Required: true + Address string `url:"address" json:"address" validate:"required"` + + // Port number on the server + // Required: true + Port uint64 `url:"port" json:"port" validate:"required"` + + // Set to disabled if this server should be used regardless of its state + // Required: false + Check string `url:"check,omitempty" json:"check,omitempty"` + + // Interval in milliseconds between two consecutive availability checks of the server that is considered available + // Required: false + Inter uint64 `url:"inter,omitempty" json:"inter,omitempty"` + + // Interval in milliseconds between two consecutive checks to restore + // the availability of a server that is currently considered unavailable + // Required: false + DownInter uint64 `url:"downinter,omitempty" json:"downinter,omitempty"` + + // Number of checks that the server must pass in order to get + // the available status and be included in the balancing scheme again + // Required: false + Rise uint64 `url:"rise,omitempty" json:"rise,omitempty"` + + // Number of consecutive failed availability checks, + // after which the previously considered available server receives the status of unavailable and + // is temporarily excluded from the balancing scheme + // Required: false + Fall uint64 `url:"fall,omitempty" json:"fall,omitempty"` + + // Interval in milliseconds from the moment the server receives the available status, + // after which the number of actually allowed connections to this server will be returned to 100% of the set limit + // Required: false + SlowStart uint64 `url:"slowstart,omitempty" json:"slowstart,omitempty"` + + // Limit of simultaneous connections to the server. When this limit is reached, the server is temporarily excluded from the balancing scheme + // Required: false + MaxConn uint64 `url:"maxconn,omitempty" json:"maxconn,omitempty"` + + // Limit of connections waiting in the queue. When this limit is reached, all subsequent connections will be forwarded to other servers + // Required: false + MaxQueue uint64 `url:"maxqueue,omitempty" json:"maxqueue,omitempty"` + + // Server weight for use in weight balancing algorithms + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` +} + +// BackendServerUpdate updates server definition on the backend of load balancer +func (lb LB) BackendServerUpdate(ctx context.Context, req BackendServerUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/backendServerUpdate" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/backend_update.go b/pkg/cloudbroker/lb/backend_update.go new file mode 100644 index 0000000..239e82e --- /dev/null +++ b/pkg/cloudbroker/lb/backend_update.go @@ -0,0 +1,90 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// BackendUpdateRequest struct to update backend +type BackendUpdateRequest struct { + // ID of the load balancer instance to backendCreate + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must be unique among all backends of this load balancer - name of the new backend to create + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` + + // Algorithm + // Should be one of: + // - roundrobin + // - static-rr + // - leastconn + // Required: false + Algorithm string `url:"algorithm,omitempty" json:"algorithm,omitempty" validate:"omitempty,lbAlgorithm"` + + // Interval in milliseconds between two consecutive availability + // checks of the server that is considered available + // Required: false + Inter uint64 `url:"inter,omitempty" json:"inter,omitempty"` + + // Interval in milliseconds between two consecutive checks to + // restore the availability of a server that is currently considered unavailable + // Required: false + DownInter uint64 `url:"downinter,omitempty" json:"downinter,omitempty"` + + // Number of checks that the server must pass in order to get the available status + // and be included in the balancing scheme again + // Required: false + Rise uint64 `url:"rise,omitempty" json:"rise,omitempty"` + + // Number of consecutive failed availability checks, + // after which the previously considered available server receives the status of + // unavailable and is temporarily excluded from the balancing scheme + // Required: false + Fall uint64 `url:"fall,omitempty" json:"fall,omitempty"` + + // Interval in milliseconds from the moment the server receives the available status, + // after which the number of actually allowed connections to this server will be returned to 100% of the set limit + // Required: false + SlowStart uint64 `url:"slowstart,omitempty" json:"slowstart,omitempty"` + + // Limit of simultaneous connections to the server. When this limit is reached, + // the server is temporarily excluded from the balancing scheme + // Required: false + MaxConn uint64 `url:"maxconn,omitempty" json:"maxconn,omitempty"` + + // Limit of connections waiting in the queue. + // When this limit is reached, all subsequent connections will be forwarded to other servers + // Required: false + MaxQueue uint64 `url:"maxqueue,omitempty" json:"maxqueue,omitempty"` + + // Server weight for use in weight balancing algorithms + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` +} + +// BackendUpdate updates existing backend on the specified load balancer. Note that backend name cannot be changed +func (lb LB) BackendUpdate(ctx context.Context, req BackendUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/backendUpdate" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/config_reset.go b/pkg/cloudbroker/lb/config_reset.go new file mode 100644 index 0000000..e4b81ec --- /dev/null +++ b/pkg/cloudbroker/lb/config_reset.go @@ -0,0 +1,39 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ConfigResetRequest struct to reset config +type ConfigResetRequest struct { + // ID of the load balancer instance to ConfigReset + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// ConfigReset resets current software configuration of the specified load balancer. +// Warning: this action cannot be undone! +func (lb LB) ConfigReset(ctx context.Context, req ConfigResetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/configReset" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/create.go b/pkg/cloudbroker/lb/create.go new file mode 100644 index 0000000..6ff95b3 --- /dev/null +++ b/pkg/cloudbroker/lb/create.go @@ -0,0 +1,98 @@ +package lb + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create load balancer +type CreateRequest struct { + // ID of the resource group where this load balancer instance will be located + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of the load balancer. + // Must be unique among all load balancers in this resource group + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // External network to connect this load balancer to + // Required: true, can be 0 + ExtNetID int64 `url:"extnetId" json:"extnetId"` + + // Internal network (VINS) to connect this load balancer to + // Required: true, can be 0 + VINSID uint64 `url:"vinsId" json:"vinsId"` + + // Start now Load balancer + // Required: true + Start bool `url:"start" json:"start" validate:"required"` + + // Custom sysctl values for Load Balancer instance. Applied on boot + // Required: false + SysctlParams []map[string]interface{} `url:"-" json:"sysctlParams,omitempty" validate:"omitempty,dive"` + + // Use Highly Available schema for LB deploy + // Required: false + HighlyAvailable bool `url:"highlyAvailable,omitempty" json:"highlyAvailable,omitempty"` + + // Text description of this load balancer + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` +} + +type wrapperCreateRequest struct { + CreateRequest + Params []string `url:"sysctlParams,omitempty"` +} + +// Create method will create a new load balancer instance +func (lb LB) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + if req.ExtNetID == 0 && req.VINSID == 0 { + return 0, errors.New("vinsId and extNetId cannot be both in the value 0") + } + + var params []string + + if len(req.SysctlParams) != 0 { + params = make([]string, 0, len(req.SysctlParams)) + for _, m := range req.SysctlParams { + encodeStr, err := json.Marshal(m) + if err != nil { + return 0, err + } + params = append(params, string(encodeStr)) + } + } else { + params = []string{} + } + + reqWrapped := wrapperCreateRequest{ + CreateRequest: req, + Params: params, + } + + url := "/cloudbroker/lb/create" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/delete.go b/pkg/cloudbroker/lb/delete.go new file mode 100644 index 0000000..7db4062 --- /dev/null +++ b/pkg/cloudbroker/lb/delete.go @@ -0,0 +1,42 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete load balancer +type DeleteRequest struct { + // ID of the load balancer instance to delete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Set to true to delete load balancer immediately bypassing recycle bin + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes specified load balancer +func (lb LB) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/delete" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/disable.go b/pkg/cloudbroker/lb/disable.go new file mode 100644 index 0000000..17c572f --- /dev/null +++ b/pkg/cloudbroker/lb/disable.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable load balancer +type DisableRequest struct { + // ID of the load balancer instance to disable + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Disable disables specified load balancer instance +func (lb LB) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/disable" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/enable.go b/pkg/cloudbroker/lb/enable.go new file mode 100644 index 0000000..6380165 --- /dev/null +++ b/pkg/cloudbroker/lb/enable.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable load balancer +type EnableRequest struct { + // ID of the load balancer instance to enable + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Enable enables specified load balancer instance +func (lb LB) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/enable" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/filter.go b/pkg/cloudbroker/lb/filter.go new file mode 100644 index 0000000..f14dc13 --- /dev/null +++ b/pkg/cloudbroker/lb/filter.go @@ -0,0 +1,82 @@ +package lb + +import ( + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/k8s" +) + +// FilterByID returns ListLB with specified ID. +func (ll ListLB) FilterByID(id uint64) ListLB { + predicate := func(rlb ItemLBList) bool { + return rlb.ID == id + } + + return ll.FilterFunc(predicate) +} + +// FilterByName returns ListLB with specified Name. +func (ll ListLB) FilterByName(name string) ListLB { + predicate := func(rlb ItemLBList) bool { + return rlb.Name == name + } + + return ll.FilterFunc(predicate) +} + +// FilterByExtNetID returns ListLB with specified ExtNetID. +func (ll ListLB) FilterByExtNetID(extNetID int64) ListLB { + predicate := func(rlb ItemLBList) bool { + return rlb.ExtNetID == extNetID + } + + return ll.FilterFunc(predicate) +} + +// FilterByK8SID returns ListLB used by specified K8S cluster. +func (ll ListLB) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListLB, error) { + caller := k8s.New(decortClient) + + req := k8s.GetRequest{ + K8SID: k8sID, + } + + cluster, err := caller.Get(ctx, req) + if err != nil { + return nil, err + } + + predicate := func(rlb ItemLBList) bool { + return cluster.LBID == rlb.ID + } + + res := ll.FilterFunc(predicate) + + return &res, nil +} + +// FilterFunc allows filtering ListLB based on a user-specified predicate. +func (ll ListLB) FilterFunc(predicate func(ItemLBList) bool) ListLB { + var result ListLB + + for _, item := range ll.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found RecordLB +// If none was found, returns an empty struct. +func (ll ListLB) FindOne() ItemLBList { + if len(ll.Data) == 0 { + return ItemLBList{} + } + + return ll.Data[0] +} diff --git a/pkg/cloudbroker/lb/filter_test.go b/pkg/cloudbroker/lb/filter_test.go new file mode 100644 index 0000000..2ee1981 --- /dev/null +++ b/pkg/cloudbroker/lb/filter_test.go @@ -0,0 +1,137 @@ +package lb + +import "testing" + +var lbs = ListLB{ + Data: []ItemLBList{ + { + HAMode: true, + ACL: []interface{}{}, + Backends: []ItemBackend{}, + CreatedBy: "test_user_1", + CreatedTime: 1636667448, + DeletedBy: "", + DeletedTime: 0, + Description: "", + DPAPIPassword: "0000", + DPAPIUser: "api_user", + ExtNetID: 2522, + Frontends: []ItemFrontend{}, + GID: 212, + GUID: 1, + ID: 1, + Milestones: 129000, + Name: "k8s-lb-test-1", + PrimaryNode: Node{}, + RGID: 25090, + RGName: "", + SecondaryNode: Node{}, + Status: "ENABLED", + TechStatus: "STARTED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 101, + }, + { + HAMode: false, + ACL: []interface{}{}, + Backends: []ItemBackend{}, + CreatedBy: "test_user_2", + CreatedTime: 1636667506, + DeletedBy: "", + DeletedTime: 0, + Description: "", + DPAPIPassword: "0000", + DPAPIUser: "api_user_2", + ExtNetID: 2524, + Frontends: []ItemFrontend{}, + GID: 212, + GUID: 2, + ID: 2, + Milestones: 129013, + Name: "k8s-lb-test-2", + PrimaryNode: Node{}, + RGID: 25092, + RGName: "", + SecondaryNode: Node{}, + Status: "ENABLED", + TechStatus: "STOPPED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 102, + }, + { + HAMode: true, + ACL: []interface{}{}, + Backends: []ItemBackend{}, + CreatedBy: "te2t_user_3", + CreatedTime: 1636667534, + DeletedBy: "", + DeletedTime: 0, + Description: "", + DPAPIPassword: "0000", + DPAPIUser: "api_user_3", + ExtNetID: 2536, + Frontends: []ItemFrontend{}, + GID: 212, + GUID: 3, + ID: 3, + Milestones: 129025, + Name: "k8s-lb-test-3", + PrimaryNode: Node{}, + RGID: 25106, + RGName: "", + SecondaryNode: Node{}, + Status: "DISABLED", + TechStatus: "STOPPED", + UpdatedBy: "", + UpdatedTime: 0, + VINSID: 118, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := lbs.FilterByID(2).FindOne() + + if actual.ID != 2 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := lbs.FilterByName("k8s-lb-test-3").FindOne() + + if actual.Name != "k8s-lb-test-3" { + t.Fatal("expected Name 'k8s-lb-test-3', found: ", actual.Name) + } +} + +func TestFilterByExtNetID(t *testing.T) { + actual := lbs.FilterByExtNetID(2522).FindOne() + + if actual.ExtNetID != 2522 { + t.Fatal("expected ExtNetID 2522, found: ", actual.ExtNetID) + } +} + +func TestFilterFunc(t *testing.T) { + actual := lbs.FilterFunc(func(rl ItemLBList) bool { + return rl.Status == "DISABLED" + }) + + for _, item := range actual.Data { + if item.Status != "DISABLED" { + t.Fatal("expected Status 'DISABLED', found: ", item.Status) + } + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := lbs.SortByCreatedTime(true) + + if actual.Data[0].CreatedTime != 1636667534 || actual.Data[2].CreatedTime != 1636667448 { + t.Fatal("expected descending order, found ascending") + } +} diff --git a/pkg/cloudbroker/lb/frontend_bind.go b/pkg/cloudbroker/lb/frontend_bind.go new file mode 100644 index 0000000..2e6bebb --- /dev/null +++ b/pkg/cloudbroker/lb/frontend_bind.go @@ -0,0 +1,57 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendBindRequest struct for frontend bind +type FrontendBindRequest struct { + // ID of the load balancer instance to FrontendBind + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Name of the frontend to update + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` + + // Name of the binding to update + // Required: true + BindingName string `url:"bindingName" json:"bindingName" validate:"required"` + + // If specified must be within the IP range of either Ext Net or ViNS, + // where this load balancer is connected - new IP address to use for this binding. + // If omitted, current IP address is retained + // Required: true + BindingAddress string `url:"bindingAddress" json:"bindingAddress" validate:"required"` + + // New port number to use for this binding. + // If omitted, current port number is retained + // Required: true + BindingPort uint64 `url:"bindingPort" json:"bindingPort" validate:"required"` +} + +// FrontendBind bind frontend from specified load balancer instance +func (lb LB) FrontendBind(ctx context.Context, req FrontendBindRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/frontendBind" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/frontend_bind_delete.go b/pkg/cloudbroker/lb/frontend_bind_delete.go new file mode 100644 index 0000000..862ca9a --- /dev/null +++ b/pkg/cloudbroker/lb/frontend_bind_delete.go @@ -0,0 +1,46 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendBindDeleteRequest struct to delete bind +type FrontendBindDeleteRequest struct { + // ID of the load balancer instance to FrontendBindDelete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Name of the frontend to delete + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` + + // Name of the binding to delete + // Required: true + BindingName string `url:"bindingName" json:"bindingName" validate:"required"` +} + +// FrontendBindDelete deletes binding from the specified load balancer frontend +func (lb LB) FrontendBindDelete(ctx context.Context, req FrontendBindDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/frontendBindDelete" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/frontend_binding_update.go b/pkg/cloudbroker/lb/frontend_binding_update.go new file mode 100644 index 0000000..dacf9e7 --- /dev/null +++ b/pkg/cloudbroker/lb/frontend_binding_update.go @@ -0,0 +1,57 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendBindUpdateRequest struct for update binding +type FrontendBindUpdateRequest struct { + // ID of the load balancer instance to FrontendBindUpdate + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Name of the frontend to update + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` + + // Name of the binding to update + // Required: true + BindingName string `url:"bindingName" json:"bindingName" validate:"required"` + + // If specified must be within the IP range of either Ext Net or ViNS, + // where this load balancer is connected - new IP address to use for this binding. + // If omitted, current IP address is retained + // Required: true + BindingAddress string `url:"bindingAddress" json:"bindingAddress" validate:"required"` + + // New port number to use for this binding. + // If omitted, current port number is retained + // Required: true + BindingPort uint64 `url:"bindingPort" json:"bindingPort" validate:"required"` +} + +// FrontendBindUpdate updates binding for the specified load balancer frontend +func (lb LB) FrontendBindUpdate(ctx context.Context, req FrontendBindUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/frontendBindingUpdate" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/frontend_create.go b/pkg/cloudbroker/lb/frontend_create.go new file mode 100644 index 0000000..33ddddc --- /dev/null +++ b/pkg/cloudbroker/lb/frontend_create.go @@ -0,0 +1,48 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendCreateRequest struct for create frontend +type FrontendCreateRequest struct { + // ID of the load balancer instance to FrontendCreate + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Must be unique among all frontends of + // this load balancer - name of the new frontend to create + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` + + // Should be one of the backends existing on + // this load balancer - name of the backend to use + // Required: true + BackendName string `url:"backendName" json:"backendName" validate:"required"` +} + +// FrontendCreate creates new frontend on the specified load balancer +func (l LB) FrontendCreate(ctx context.Context, req FrontendCreateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/frontendCreate" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/frontend_delete.go b/pkg/cloudbroker/lb/frontend_delete.go new file mode 100644 index 0000000..225a25b --- /dev/null +++ b/pkg/cloudbroker/lb/frontend_delete.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// FrontendDeleteRequest struct for delete frontend +type FrontendDeleteRequest struct { + // ID of the load balancer instance to FrontendDelete + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Name of the frontend to delete + // Required: true + FrontendName string `url:"frontendName" json:"frontendName" validate:"required"` +} + +// FrontendDelete deletes frontend from the specified load balancer. +// Warning: you cannot undo this action! +func (lb LB) FrontendDelete(ctx context.Context, req FrontendDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/frontendDelete" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/get.go b/pkg/cloudbroker/lb/get.go new file mode 100644 index 0000000..3bb553b --- /dev/null +++ b/pkg/cloudbroker/lb/get.go @@ -0,0 +1,46 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about load balancer +type GetRequest struct { + // ID of the load balancer to get details for + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Get gets detailed information about load balancer as a RecordLB struct +func (lb LB) Get(ctx context.Context, req GetRequest) (*RecordLB, error) { + res, err := lb.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordLB{} + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets detailed information about load balancer as an array of bytes +func (lb LB) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/get" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/lb/ids.go b/pkg/cloudbroker/lb/ids.go new file mode 100644 index 0000000..a610613 --- /dev/null +++ b/pkg/cloudbroker/lb/ids.go @@ -0,0 +1,10 @@ +package lb + +// IDs gets array of LBIDs from ListLB struct +func (llb ListLB) IDs() []uint64 { + res := make([]uint64, 0, len(llb.Data)) + for _, lb := range llb.Data { + res = append(res, lb.ID) + } + return res +} diff --git a/pkg/cloudbroker/lb/lb.go b/pkg/cloudbroker/lb/lb.go new file mode 100644 index 0000000..b47aaff --- /dev/null +++ b/pkg/cloudbroker/lb/lb.go @@ -0,0 +1,16 @@ +// API to manage load balancer instance +package lb + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to load balancer +type LB struct { + client interfaces.Caller +} + +// Builder for load balancer +func New(client interfaces.Caller) *LB { + return &LB{ + client: client, + } +} diff --git a/pkg/cloudbroker/lb/list.go b/pkg/cloudbroker/lb/list.go new file mode 100644 index 0000000..f8804ec --- /dev/null +++ b/pkg/cloudbroker/lb/list.go @@ -0,0 +1,92 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of load balancers +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by frontend Ip + // Required: false + FrontIP string `url:"frontIp,omitempty" json:"frontIp,omitempty"` + + // Find by backend Ip + // Required: false + BackIP string `url:"backIp,omitempty" json:"backIp,omitempty"` + + // Included deleted load balancers + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all load balancers as a ListLB struct +func (lb LB) List(ctx context.Context, req ListRequest) (*ListLB, error) { + + res, err := lb.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListLB{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all load balancers as an array of bytes +func (lb LB) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/list" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/lb/list_deleted.go b/pkg/cloudbroker/lb/list_deleted.go new file mode 100644 index 0000000..5d7b4ce --- /dev/null +++ b/pkg/cloudbroker/lb/list_deleted.go @@ -0,0 +1,77 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted load balancers +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group ID + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by frontend Ip + // Required: false + FrontIP string `url:"frontIp,omitempty" json:"frontIp,omitempty"` + + // Find by backend Ip + // Required: false + BackIP string `url:"backIp,omitempty" json:"backIp,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: true + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list of deleted load balancers +func (lb LB) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListLB, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/listDeleted" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListLB{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/lb/make_highly_available.go b/pkg/cloudbroker/lb/make_highly_available.go new file mode 100644 index 0000000..7aa08e9 --- /dev/null +++ b/pkg/cloudbroker/lb/make_highly_available.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// HighlyAvailableRequest struct to make Load Balancer Highly available +type HighlyAvailableRequest struct { + // ID of the LB instance + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// HighlyAvailable makes Load Balancer Highly available +func (l LB) HighlyAvailable(ctx context.Context, req HighlyAvailableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/makeHighlyAvailable" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil || result != req.LBID { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/lb/models.go b/pkg/cloudbroker/lb/models.go new file mode 100644 index 0000000..11382cd --- /dev/null +++ b/pkg/cloudbroker/lb/models.go @@ -0,0 +1,335 @@ +package lb + +// Server settings +type ServerSettings struct { + // Inter + Inter uint64 `json:"inter"` + + // GUID + GUID string `json:"guid"` + + // DownInter + DownInter uint64 `json:"downinter"` + + // Rise + Rise uint64 `json:"rise"` + + // Fall + Fall uint64 `json:"fall"` + + // SlowStart + SlowStart uint64 `json:"slowstart"` + + // Max connections + MaxConn uint64 `json:"maxconn"` + + // Max queue + MaxQueue uint64 `json:"maxqueue"` + + // Weight + Weight uint64 `json:"weight"` +} + +// Main information about server +type ItemServer struct { + + // Address + Address string `json:"address"` + + // Check + Check string `json:"check"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Port + Port uint64 `json:"port"` + + // Server settings + ServerSettings ServerSettings `json:"serverSettings"` +} + +// List of servers +type ListServers []ItemServer + +// Main information about backend +type ItemBackend struct { + // Algorithm + Algorithm string `json:"algorithm"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Server settings + ServerDefaultSettings ServerSettings `json:"serverDefaultSettings"` + + // List of servers + Servers ListServers `json:"servers"` +} + +// List of backends +type ListBackends []ItemBackend + +// Main information about frontend +type ItemFrontend struct { + // Backend + Backend string `json:"backend"` + + // List of bindings + Bindings ListBindings `json:"bindings"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` +} + +// List of frontends +type ListFrontends []ItemFrontend + +// Main information about bindings +type ItemBinding struct { + // Address + Address string `json:"address"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Port + Port uint64 `json:"port"` +} + +// List of bindings +type ListBindings []ItemBinding + +// Main information about node +type Node struct { + // Backend IP + BackendIP string `json:"backendIp"` + + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Frontend IP + FrontendIP string `json:"frontendIp"` + + // GUID + GUID string `json:"guid"` + + // MGMTIP + MGMTIP string `json:"mgmtIp"` + + // Network ID + NetworkID uint64 `json:"networkId"` +} + +// List of load balancers +type ListLB struct { + // Data + Data []ItemLBList `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about load balancer +type RecordLB struct { + // HAMode + HAMode bool `json:"HAmode"` + + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Access Control List + ACL interface{} `json:"acl"` + + // BackendHAIP + BackendHAIP string `json:"backendHAIP"` + + // List of load balancer backends + Backends ListBackends `json:"backends"` + + // Description + Description string `json:"desc"` + + // DPAPI password + DPAPIPassword string `json:"dpApiPassword"` + + // DPAPI user + DPAPIUser string `json:"dpApiUser"` + + // External network ID + ExtNetID int64 `json:"extnetId"` + + // FrontendHAIP + FrontendHAIP string `json:"frontendHAIP"` + + // List of load balancer frontends + Frontends ListFrontends `json:"frontends"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Manager Id + ManagerId uint64 `json:"managerId"` + + // Manager Type + ManagerType string `json:"managerType"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Part K8s + PartK8s bool `json:"partK8s"` + + // Primary node + PrimaryNode Node `json:"primaryNode"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Secondary node + SecondaryNode Node `json:"secondaryNode"` + + // Status + Status string `json:"status"` + + // Sysctl Params + SysctlParams interface{} `json:"sysctlParams"` + + // Tech status + TechStatus string `json:"techStatus"` + + // User Managed flag + UserManaged bool `json:"userManaged"` + + // VINS ID + VINSID uint64 `json:"vinsId"` +} + +// Detailed information about load balancer in List +type ItemLBList struct { + // HAMode + HAMode bool `json:"HAmode"` + + // Access Control List + ACL interface{} `json:"acl"` + + // BackendHAIP + BackendHAIP string `json:"backendHAIP"` + + // List of load balancer backends + Backends ListBackends `json:"backends"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // DPAPI password + DPAPIPassword string `json:"dpApiPassword"` + + // DPAPI user + DPAPIUser string `json:"dpApiUser"` + + // External network ID + ExtNetID int64 `json:"extnetId"` + + // FrontendHAIP + FrontendHAIP string `json:"frontendHAIP"` + + // List of load balancer frontends + Frontends ListFrontends `json:"frontends"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // ManagerId + ManagerId uint64 `json:"managerId"` + + // Name + ManagerType string `json:"managerType"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // PartK8s + PartK8s bool `json:"partK8s"` + + // Primary node + PrimaryNode Node `json:"primaryNode"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Secondary node + SecondaryNode Node `json:"secondaryNode"` + + // Status + Status string `json:"status"` + + // Sysctl Params + SysctlParams interface{} `json:"sysctlParams"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed flag + UserManaged bool `json:"userManaged"` + + // VINS ID + VINSID uint64 `json:"vinsId"` +} diff --git a/pkg/cloudbroker/lb/restart.go b/pkg/cloudbroker/lb/restart.go new file mode 100644 index 0000000..c0849e2 --- /dev/null +++ b/pkg/cloudbroker/lb/restart.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestartRequest struct to restart load balancer +type RestartRequest struct { + // ID of the load balancer instance to restart + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // restart secondary and primary nodes sequentially in HA mode + // Default: true + // Required: false + Safe bool `url:"safe" json:"safe"` +} + +// Restart restarts specified load balancer instance +func (lb LB) Restart(ctx context.Context, req RestartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/restart" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/restore.go b/pkg/cloudbroker/lb/restore.go new file mode 100644 index 0000000..0d15671 --- /dev/null +++ b/pkg/cloudbroker/lb/restore.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore load balancer +type RestoreRequest struct { + // ID of the load balancer instance to restore + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Restore restores load balancer from recycle bin +func (lb LB) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/restore" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/serialize.go b/pkg/cloudbroker/lb/serialize.go new file mode 100644 index 0000000..4869b82 --- /dev/null +++ b/pkg/cloudbroker/lb/serialize.go @@ -0,0 +1,43 @@ +package lb + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ll ListLB) Serialize(params ...string) (serialization.Serialized, error) { + if len(ll.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ll, prefix, indent) + } + + return json.Marshal(ll) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (rlb RecordLB) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(rlb, prefix, indent) + } + + return json.Marshal(rlb) +} diff --git a/pkg/cloudbroker/lb/sorting.go b/pkg/cloudbroker/lb/sorting.go new file mode 100644 index 0000000..addadeb --- /dev/null +++ b/pkg/cloudbroker/lb/sorting.go @@ -0,0 +1,60 @@ +package lb + +import "sort" + +// SortByCreatedTime sorts ListLB by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ll ListLB) SortByCreatedTime(inverse bool) ListLB { + if len(ll.Data) < 2 { + return ll + } + + sort.Slice(ll.Data, func(i, j int) bool { + if inverse { + return ll.Data[i].CreatedTime > ll.Data[j].CreatedTime + } + + return ll.Data[i].CreatedTime < ll.Data[j].CreatedTime + }) + + return ll +} + +// SortByUpdatedTime sorts ListLB by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ll ListLB) SortByUpdatedTime(inverse bool) ListLB { + if len(ll.Data) < 2 { + return ll + } + + sort.Slice(ll.Data, func(i, j int) bool { + if inverse { + return ll.Data[i].UpdatedTime > ll.Data[j].UpdatedTime + } + + return ll.Data[i].UpdatedTime < ll.Data[j].UpdatedTime + }) + + return ll +} + +// SortByDeletedTime sorts ListLB by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (ll ListLB) SortByDeletedTime(inverse bool) ListLB { + if len(ll.Data) < 2 { + return ll + } + + sort.Slice(ll.Data, func(i, j int) bool { + if inverse { + return ll.Data[i].DeletedTime > ll.Data[j].DeletedTime + } + + return ll.Data[i].DeletedTime < ll.Data[j].DeletedTime + }) + + return ll +} diff --git a/pkg/cloudbroker/lb/start.go b/pkg/cloudbroker/lb/start.go new file mode 100644 index 0000000..a907e6e --- /dev/null +++ b/pkg/cloudbroker/lb/start.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StartRequest struct to start load balancer +type StartRequest struct { + // ID of the LB instance to start + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Start starts specified load balancer instance +func (lb LB) Start(ctx context.Context, req StartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/start" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/stop.go b/pkg/cloudbroker/lb/stop.go new file mode 100644 index 0000000..1224509 --- /dev/null +++ b/pkg/cloudbroker/lb/stop.go @@ -0,0 +1,38 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StopRequest struct to stop load balancer +type StopRequest struct { + // ID of the LB instance to stop + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` +} + +// Stop stops specified load balancer instance +func (lb LB) Stop(ctx context.Context, req StopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/stop" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/update.go b/pkg/cloudbroker/lb/update.go new file mode 100644 index 0000000..8568210 --- /dev/null +++ b/pkg/cloudbroker/lb/update.go @@ -0,0 +1,43 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update load balancer +type UpdateRequest struct { + // ID of the load balancer to update + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // New description of this load balancer. + // If omitted, current description is retained + // Required: true + Description string `url:"desc" json:"desc" validate:"required"` +} + +// Update updates some of load balancer attributes +func (lb LB) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/update" + + res, err := lb.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/lb/update_sysctl_params.go b/pkg/cloudbroker/lb/update_sysctl_params.go new file mode 100644 index 0000000..3a8791b --- /dev/null +++ b/pkg/cloudbroker/lb/update_sysctl_params.go @@ -0,0 +1,67 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateSysctParamsRequest struct to update sysct params for lb +type UpdateSysctParamsRequest struct { + // ID of the LB instance + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // Custom sysctl values for Load Balancer instance. Applied on boot + // Required: true + SysctlParams []map[string]interface{} `url:"-" json:"sysctlParams" validate:"required,dive"` +} + +type wrapperUpdateSysctParamsRequest struct { + UpdateSysctParamsRequest + Params []string `url:"sysctlParams" validate:"required"` +} + +// UpdateSysctParams method will create a new load balancer instance +func (l LB) UpdateSysctlParams(ctx context.Context, req UpdateSysctParamsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + var params []string + + if len(req.SysctlParams) != 0 { + params = make([]string, 0, len(req.SysctlParams)) + for _, m := range req.SysctlParams { + encodeStr, err := json.Marshal(m) + if err != nil { + return false, err + } + params = append(params, string(encodeStr)) + } + } else { + params = []string{} + } + + reqWrapped := wrapperUpdateSysctParamsRequest{ + UpdateSysctParamsRequest: req, + Params: params, + } + + url := "/cloudbroker/lb/updateSysctlParams" + + res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/node.go b/pkg/cloudbroker/node.go new file mode 100644 index 0000000..33c94ff --- /dev/null +++ b/pkg/cloudbroker/node.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/node" + +// Accessing the Node method group +func (cb *CloudBroker) Node() *node.Node { + return node.New(cb.client) +} diff --git a/pkg/cloudbroker/node/apply_ipmi_action.go b/pkg/cloudbroker/node/apply_ipmi_action.go new file mode 100644 index 0000000..9bd54ca --- /dev/null +++ b/pkg/cloudbroker/node/apply_ipmi_action.go @@ -0,0 +1,37 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ApplyIpmiActionRequest struct to apply ipmi action on node +type ApplyIpmiActionRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // Action + // on of actions power_on shutdown force_shutdown reboot + // Required: true + Action string `url:"action" json:"action" validate:"required,action"` +} + +// ApplyIpmiAction applies ipmi action on node +func (n Node) ApplyIpmiAction(ctx context.Context, req ApplyIpmiActionRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/applyIpmiAction" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/consumption.go b/pkg/cloudbroker/node/consumption.go new file mode 100644 index 0000000..0e64077 --- /dev/null +++ b/pkg/cloudbroker/node/consumption.go @@ -0,0 +1,40 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ConsumptionRequest struct to get node summary by resources and consumption +type ConsumptionRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` +} + +// Consumption gets node summary by resources and consumption +func (n Node) Consumption(ctx context.Context, req ConsumptionRequest) (*ConsumptionInfo, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/consumption" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := ConsumptionInfo{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/node/decommission.go b/pkg/cloudbroker/node/decommission.go new file mode 100644 index 0000000..3375d80 --- /dev/null +++ b/pkg/cloudbroker/node/decommission.go @@ -0,0 +1,37 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DecommissionRequest struct to set node status to DECOMMISSIONED +type DecommissionRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // Force node decommission + // Default: false + // Required: false + Force bool `url:"force" json:"force"` +} + +// Decommission sets node status to DECOMMISSIONED +func (n Node) Decommission(ctx context.Context, req DecommissionRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/decommission" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/enable.go b/pkg/cloudbroker/node/enable.go new file mode 100644 index 0000000..a89f750 --- /dev/null +++ b/pkg/cloudbroker/node/enable.go @@ -0,0 +1,86 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable node from maintenance status to enabled +type EnableRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // Message + // Required: false + Message string `url:"message,omitempty" json:"message,omitempty"` + + // Do not check locks, iscsi-sessions or disk-present + // Default: true + // Required: false + Force interface{} `url:"force" json:"force" validate:"isBool" ` + + // Reason + // Required: false + Reason string `url:"reason,omitempty" json:"reason,omitempty"` +} + +type wrapperEnableRequest struct { + EnableRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Enable enables node from maintenance status to enabled +func (n Node) Enable(ctx context.Context, req EnableRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + if req.Force == nil { + req.Force = true + } + + reqWrapped := wrapperEnableRequest{ + EnableRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/node/enable" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} + +// EnableAsync enables node from maintenance status to enabled with AsyncMode +func (n Node) EnableAsync(ctx context.Context, req EnableRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + if req.Force == nil { + req.Force = true + } + + reqWrapped := wrapperEnableRequest{ + EnableRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/node/enable" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/enable_nodes.go b/pkg/cloudbroker/node/enable_nodes.go new file mode 100644 index 0000000..57ea1c5 --- /dev/null +++ b/pkg/cloudbroker/node/enable_nodes.go @@ -0,0 +1,36 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableNodesRequest struct to enable nodes from maintenance status to enabled +type EnableNodesRequest struct { + // List of Node IDs + // Required: true + NIDs []uint64 `url:"nids" json:"nids" validate:"required"` + + // Message + // Required: false + Message string `url:"message,omitempty" json:"message,omitempty"` +} + +// EnableNodes enables nodes from maintenance status to enabled +func (n Node) EnableNodes(ctx context.Context, req EnableNodesRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/enableNodes" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/filter.go b/pkg/cloudbroker/node/filter.go new file mode 100644 index 0000000..3de72d3 --- /dev/null +++ b/pkg/cloudbroker/node/filter.go @@ -0,0 +1,99 @@ +package node + +// FilterByID returns ListNodes with specified ID. +func (ln ListNodes) FilterByID(id uint64) ListNodes { + predicate := func(in ItemNode) bool { + return in.ID == id + } + + return ln.FilterFunc(predicate) +} + +// FilterByName returns ListNodes with specified Name. +func (ln ListNodes) FilterByName(name string) ListNodes { + predicate := func(in ItemNode) bool { + return in.Name == name + } + + return ln.FilterFunc(predicate) +} + +// FilterByVersion return ListNodes with specified version. +func (ln ListNodes) FilterByVersion(version string) ListNodes { + predicate := func(in ItemNode) bool { + return in.Version == version + } + + return ln.FilterFunc(predicate) +} + +// FilterByRelease returns ListNodes with specified Release. +func (ln ListNodes) FilterByRelease(release string) ListNodes { + predicate := func(in ItemNode) bool { + return in.Release == release + } + + return ln.FilterFunc(predicate) +} + +// FilterBySepID returns ListNodes with specified Sep ID. +func (ln ListNodes) FilterBySepID(sepId uint64) ListNodes { + predicate := func(in ItemNode) bool { + for _, s := range in.Seps { + if s == sepId { + return true + } + } + return false + } + + return ln.FilterFunc(predicate) +} + +// FilterByRole returns ListNodes with specified Role. +func (ln ListNodes) FilterByRole(role string) ListNodes { + predicate := func(in ItemNode) bool { + for _, r := range in.Roles { + if r == role { + return true + } + } + return false + } + + return ln.FilterFunc(predicate) +} + +// FilterByStatus return ListNodes with specified status. +func (ln ListNodes) FilterByStatus(status string) ListNodes { + predicate := func(in ItemNode) bool { + return in.Status == status + } + + return ln.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListNodes based on a user-specified predicate. +func (ln ListNodes) FilterFunc(predicate func(ItemNode) bool) ListNodes { + var result ListNodes + + for _, item := range ln.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemNode. +// If none was found, returns an empty struct. +func (ln ListNodes) FindOne() ItemNode { + if len(ln.Data) == 0 { + return ItemNode{} + } + + return ln.Data[0] +} diff --git a/pkg/cloudbroker/node/filter_test.go b/pkg/cloudbroker/node/filter_test.go new file mode 100644 index 0000000..c01894e --- /dev/null +++ b/pkg/cloudbroker/node/filter_test.go @@ -0,0 +1,155 @@ +package node + +import "testing" + +var nodes = ListNodes{ + Data: []ItemNode{ + { + GID: 212, + GUID: "1", + ID: 1, + Name: "node_1", + Release: "1.7_x86-64", + Roles: []string{"node", "controllernode"}, + Seps: []uint64{1, 2, 3}, + Status: "ENABLED", + Version: "4.0.0", + }, + { + GID: 212, + GUID: "2", + ID: 2, + Name: "node_2", + Release: "", + Roles: []string{"node"}, + Seps: []uint64{4, 5}, + Status: "CREATED", + Version: "3.8.8", + }, + { + GID: 212, + GUID: "3", + ID: 3, + Name: "node_3", + Release: "", + Roles: []string{"physical"}, + Seps: []uint64{1}, + Status: "CREATED", + Version: "3.8.9", + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + var testId uint64 = 1 + actual := nodes.FilterByID(testId).FindOne() + + if actual.ID != testId { + t.Fatalf("expected ID %d, found: %d", testId, actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + testName := "node_1" + actual := nodes.FilterByName(testName).FindOne() + + if actual.Name != testName { + t.Fatalf("expected Name '%s', found: %s", testName, actual.Name) + } +} + +func TestFilterByVersion(t *testing.T) { + testVersion := "4.0.0" + actual := nodes.FilterByVersion(testVersion).FindOne() + + if actual.Version != testVersion { + t.Fatalf("expected Version '%s', found: %s", testVersion, actual.Version) + } +} + +func TestFilterByRelease(t *testing.T) { + testRelease := "1.7_x86-64" + actual := nodes.FilterByRelease(testRelease).FindOne() + + if actual.Release != testRelease { + t.Fatalf("expected Release '%s', found: %s", testRelease, actual.Release) + } +} + +func TestFilterBySepID(t *testing.T) { + var testSepId uint64 = 1 + actual := nodes.FilterBySepID(testSepId) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + var found bool + + for _, sep := range item.Seps { + if sep == testSepId { + found = true + } + } + + if !found { + t.Fatalf("expected SepID %d, found seps: %v", testSepId, item.Seps) + } + } +} + +func TestFilterByRole(t *testing.T) { + testRole := "node" + actual := nodes.FilterByRole(testRole) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + var found bool + + for _, role := range item.Roles { + if role == testRole { + found = true + } + } + + if !found { + t.Fatalf("expected Role %s, found roles: %v", testRole, item.Roles) + } + } +} + +func TestFilterByStatus(t *testing.T) { + testStatus := "CREATED" + actual := nodes.FilterByStatus(testStatus) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != testStatus { + t.Fatalf("expected Status '%s', found: %s", testStatus, item.Status) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := nodes.FilterFunc(func(in ItemNode) bool { + return len(in.Roles) > 0 + }) + + if len(actual.Data) < 1 { + t.Fatal("expected 3, found: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if len(item.Roles) < 1 { + t.Fatal("expected Roles to contain at least 1 element, found empty") + } + } +} diff --git a/pkg/cloudbroker/node/get.go b/pkg/cloudbroker/node/get.go new file mode 100644 index 0000000..7dbeb54 --- /dev/null +++ b/pkg/cloudbroker/node/get.go @@ -0,0 +1,46 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about node +type GetRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` +} + +// Get gets node summary as a RecordNode struct +func (n Node) Get(ctx context.Context, req GetRequest) (*RecordNode, error) { + res, err := n.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordNode{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets node summary as an array of bytes +func (n Node) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/get" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/node/ids.go b/pkg/cloudbroker/node/ids.go new file mode 100644 index 0000000..03ed5ef --- /dev/null +++ b/pkg/cloudbroker/node/ids.go @@ -0,0 +1,10 @@ +package node + +// IDs gets array of Node IDs from ListNodes struct +func (ln ListNodes) IDs() []uint64 { + res := make([]uint64, 0, len(ln.Data)) + for _, n := range ln.Data { + res = append(res, n.ID) + } + return res +} diff --git a/pkg/cloudbroker/node/list.go b/pkg/cloudbroker/node/list.go new file mode 100644 index 0000000..2d51ef7 --- /dev/null +++ b/pkg/cloudbroker/node/list.go @@ -0,0 +1,84 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of nodes +type ListRequest struct { + // Find by node ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by node name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by node version + // Required: false + Version string `url:"version,omitempty" json:"version,omitempty"` + + // Find by node release + // Required: false + Release string `url:"release,omitempty" json:"release,omitempty"` + + // Find by sep ID + // Required: false + SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by node roles + // Required: false + Role string `url:"role,omitempty" json:"role,omitempty"` + + // Find by node status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all nodes as a ListNodes struct +func (n Node) List(ctx context.Context, req ListRequest) (*ListNodes, error) { + + res, err := n.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListNodes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all nodes as an array of bytes +func (n Node) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/list" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/node/maintenance.go b/pkg/cloudbroker/node/maintenance.go new file mode 100644 index 0000000..75ce25a --- /dev/null +++ b/pkg/cloudbroker/node/maintenance.go @@ -0,0 +1,50 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MaintenanceRequest struct to place node in maintenance state +type MaintenanceRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // VM Action + // Default: stop + // Required: false + VMAction string `url:"vmaction,omitempty" json:"vmaction,omitempty" validate:"omitempty,vmaction"` + + // Offline + // Default: false + // Required: false + Offline bool `url:"offline" json:"offline"` + + // VDiskAction + // Required: false + VDiskAction string `url:"vdiskaction,omitempty" json:"vdiskaction,omitempty"` + + // Reason + // Required: false + Reason string `url:"reason,omitempty" json:"reason,omitempty"` +} + +// Maintenance places node in maintenance state +func (n Node) Maintenance(ctx context.Context, req MaintenanceRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/maintenance" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/models.go b/pkg/cloudbroker/node/models.go new file mode 100644 index 0000000..6c16450 --- /dev/null +++ b/pkg/cloudbroker/node/models.go @@ -0,0 +1,316 @@ +package node + +// Node summary +type RecordNode struct { + // Consumption + Consumption ConsumptionInfo `json:"consumption"` + + // CPU Info + CpuInfo CpuInfo `json:"cpuInfo"` + + // CPU Allocation Ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // GID + GID uint64 `json:"gid"` + + // Node ID + ID uint64 `json:"id"` + + // IPAddr + IPAddr []string `json:"ipaddr"` + + // Isolated Cpus + IsolatedCpus []interface{} `json:"isolatedCpus"` + + // Name + Name string `json:"name"` + + // NeedReboot + NeedReboot bool `json:"needReboot"` + + // Nic Info + NicInfo ListNicInfo `json:"nicInfo"` + + // NumaTopology + NumaTopology NumaTopologyInfo `json:"numaTopology"` + + // ReservedCPUs + ReservedCPUs []interface{} `json:"reservedCpus"` + + // Roles + Roles []string `json:"roles"` + + // SriovEnabled + SriovEnabled bool `json:"sriovEnabled"` + + // StackID + StackID uint64 `json:"stackId"` + + // Status + Status string `json:"status"` + + // Version + Version string `json:"version"` +} + +// Resource consumption of the node +type ConsumptionInfo struct { + // Consumed resources + Consumed ConsumedResourcesInfo `json:"consumed"` + + // Free resources + Free FreeResourcesInfo `json:"free"` + + // Hostname + Hostname string `json:"hostname"` + + // Reserved resources + Reserved ResourcesInfo `json:"reserved"` + + // Total resources + Total ResourcesInfo `json:"total"` +} + +// Free Resources Info +type FreeResourcesInfo struct { + // RAM + RAM float64 `json:"RAM"` +} + +// Resources Info +type ResourcesInfo struct { + // RAM + RAM uint64 `json:"RAM"` +} + +// Consumed Resources Info +type ConsumedResourcesInfo struct { + // RAM + RAM uint64 `json:"RAM"` + + // Computes + Computes uint64 `json:"computes"` + + // Routers + Routers uint64 `json:"routers"` + + // VCPU + VCPU uint64 `json:"vCPU"` +} + +// Information about node CPU +type CpuInfo struct { + // Clock Speed + ClockSpeed float64 `json:"clockSpeed"` + + // CoreCount + CoreCount uint64 `json:"coreCount"` + + // PhysCount + PhysCount uint64 `json:"physCount"` +} + +// Main information about node +type ItemNode struct { + // Additional packages + AdditionalPkgs []interface{} `json:"additionalpkgs"` + + // CPU Info + CpuInfo CpuInfo `json:"cpuInfo"` + + // Description + Description string `json:"description"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID string `json:"guid"` + + // Hostkey + HostKey string `json:"hostkey"` + + // ID + ID uint64 `json:"id"` + + // IPAddr + IPAddr []string `json:"ipaddr"` + + // Isolated Cpus + IsolatedCpus []interface{} `json:"isolatedCpus"` + + // Last check + LastCheck uint64 `json:"lastcheck"` + + // Machine GUID + MachineGUID string `json:"machineguid"` + + // Mainboard SN + MainboardSN string `json:"mainboardSN"` + + // Memory + Memory uint64 `json:"memory"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Model + Model string `json:"model"` + + // Name + Name string `json:"name"` + + // NeedReboot + NeedReboot bool `json:"needReboot"` + + // NetAddr + NetAddr ListNetAddr `json:"netaddr"` + + // Network mode + NetworkMode string `json:"networkmode"` + + // Nic Info + NicInfo ListNicInfo `json:"nicInfo"` + + // Node UUID + NodeUUID string `json:"nodeUUID"` + + // NumaTopology + NumaTopology NumaTopologyInfo `json:"numaTopology"` + + // PeerBackup + PeerBackup uint64 `json:"peer_backup"` + + // PeerLog + PeerLog uint64 `json:"peer_log"` + + // PeerStats + PeerStats uint64 `json:"peer_stats"` + + // Pgpus + Pgpus []uint64 `json:"pgpus"` + + // PublicKeys + PublicKeys []string `json:"publickeys"` + + // Release + Release string `json:"release"` + + // ReservedCPUs + ReservedCPUs []interface{} `json:"reservedCpus"` + + // Roles + Roles []string `json:"roles"` + + // Seps + Seps []uint64 `json:"seps"` + + // SerialNum + SerialNum string `json:"serialNum"` + + // SriovEnabled + SriovEnabled bool `json:"sriovEnabled"` + + // StackID + StackID uint64 `json:"stackId"` + + // Status + Status string `json:"status"` + + // Tags + Tags []string `json:"tags"` + + // Type + Type string `json:"type"` + + // Version + Version string `json:"version"` +} + +// Numa Topology Info +type NumaTopologyInfo struct { + // NodeNum + NodeNum uint64 `json:"nodenum"` + + // Nodes + Nodes map[string]NodeInfo `json:"nodes"` +} + +// Node Info from NumaTopologyInfo +type NodeInfo struct { + // CPUList + CPUList []uint64 `json:"cpulist"` + + // Memory + Memory ItemMemory `json:"memory"` +} + +type ItemMemory struct { + // 1G + OneG uint64 `json:"1G"` + + // 2M + TwoM uint64 `json:"2M"` + + // Total + Total uint64 `json:"total"` +} + +type ListNicInfo []ItemNicInfo + +// Item Nic Info +type ItemNicInfo struct { + // Driver + Driver string `json:"driver"` + + // MaxVFS + MaxVFS uint64 `json:"maxvfs"` + + // NumaNode + NumaNode int64 `json:"numaNode"` + + // NumVFS + NumVFS uint64 `json:"numvfs"` + + // OSName + OSName string `json:"osName"` + + // PCISlot + PCISlot string `json:"pciSlot"` + + // VFList + VFList []interface{} `json:"vflist"` +} + +type ListNetAddr []ItemNetAddr + +// Item Net Addr +type ItemNetAddr struct { + // CIDR + CIDR []string `json:"cidr"` + + // Index + Index uint64 `json:"index"` + + // IP + IP []string `json:"ip"` + + // Mac + Mac string `json:"mac"` + + // MTU + MTU uint64 `json:"mtu"` + + // Name + Name string `json:"name"` +} + +// List of nodes +type ListNodes struct { + // Data + Data []ItemNode `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/node/node.go b/pkg/cloudbroker/node/node.go new file mode 100644 index 0000000..d1f2b72 --- /dev/null +++ b/pkg/cloudbroker/node/node.go @@ -0,0 +1,18 @@ +// API Actors for managing node. These actors are the final API for end users to manage nodes +package node + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to node +type Node struct { + client interfaces.Caller +} + +// Builder for node endpoints +func New(client interfaces.Caller) *Node { + return &Node{ + client: client, + } +} diff --git a/pkg/cloudbroker/node/restrict.go b/pkg/cloudbroker/node/restrict.go new file mode 100644 index 0000000..910a8c8 --- /dev/null +++ b/pkg/cloudbroker/node/restrict.go @@ -0,0 +1,37 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestrictRequest struct to set node status to 'RESTRICTED' +type RestrictRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // Migrate node + // Default: false + // Required: false + Migrate bool `url:"migrate" json:"migrate"` +} + +// Restrict sets node status to 'RESTRICTED' and migrates node if migrate=True +func (n Node) Restrict(ctx context.Context, req RestrictRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/restrict" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/serialize.go b/pkg/cloudbroker/node/serialize.go new file mode 100644 index 0000000..901ef53 --- /dev/null +++ b/pkg/cloudbroker/node/serialize.go @@ -0,0 +1,59 @@ +package node + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ln ListNodes) Serialize(params ...string) (serialization.Serialized, error) { + if len(ln.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ln, prefix, indent) + } + + return json.Marshal(ln) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (in ItemNode) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(in, prefix, indent) + } + + return json.Marshal(in) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (rn RecordNode) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(rn, prefix, indent) + } + + return json.Marshal(rn) +} diff --git a/pkg/cloudbroker/node/set_core_isolation.go b/pkg/cloudbroker/node/set_core_isolation.go new file mode 100644 index 0000000..ce799a9 --- /dev/null +++ b/pkg/cloudbroker/node/set_core_isolation.go @@ -0,0 +1,36 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCoreIsolationRequest struct to isolate selected cores on node boot +type SetCoreIsolationRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // List of core number to isolate + // Required: false + CoreList []uint64 `url:"coreList,omitempty" json:"coreList,omitempty"` +} + +// SetCoreIsolation isolates selected cores on node boot +func (n Node) SetCoreIsolation(ctx context.Context, req SetCoreIsolationRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/setCoreIsolation" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/set_huge_pages.go b/pkg/cloudbroker/node/set_huge_pages.go new file mode 100644 index 0000000..7f94fcb --- /dev/null +++ b/pkg/cloudbroker/node/set_huge_pages.go @@ -0,0 +1,40 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetHugePagesRequest struct to set on-boot Huge Pages configuration +type SetHugePagesRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // Number of 2M hugepages to claim + // Required: true + HugePages2M uint64 `url:"hugepages2M" json:"hugepages2M" validate:"required"` + + // Number of 1G hugepages to claim + // Required: true + HugePages1G uint64 `url:"hugepages1G" json:"hugepages1G" validate:"required"` +} + +// SetHugePages sets on-boot Huge Pages configuration +func (n Node) SetHugePages(ctx context.Context, req SetHugePagesRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/setHugePages" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/set_sriov_status.go b/pkg/cloudbroker/node/set_sriov_status.go new file mode 100644 index 0000000..b827684 --- /dev/null +++ b/pkg/cloudbroker/node/set_sriov_status.go @@ -0,0 +1,36 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetSRIOVStatusRequest struct to set Single-root input/output virtualization kernel config on node +type SetSRIOVStatusRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // Enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled" validate:"required"` +} + +// SetSRIOVStatus sets Single-root input/output virtualization kernel config on node +func (n Node) SetSRIOVStatus(ctx context.Context, req SetSRIOVStatusRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/setsriovstatus" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/set_vfs_number.go b/pkg/cloudbroker/node/set_vfs_number.go new file mode 100644 index 0000000..1b6d5d6 --- /dev/null +++ b/pkg/cloudbroker/node/set_vfs_number.go @@ -0,0 +1,44 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetVFsNumberRequest struct to set number of VFs for individual NIC on node +type SetVFsNumberRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // PCI address or NIC name + // Required: true + NicID string `url:"nicId" json:"nicId" validate:"required"` + + // Number of VF to assign + // Required: true + VFNum uint64 `url:"vfNum" json:"vfNum" validate:"required"` + + // Trust + // Required: true + Trust bool `url:"trust" json:"trust" validate:"required"` +} + +// SetVFsNumber sets number of VFs for individual NIC on node +func (n Node) SetVFsNumber(ctx context.Context, req SetVFsNumberRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/setVFsNumber" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/node/update.go b/pkg/cloudbroker/node/update.go new file mode 100644 index 0000000..7e885f5 --- /dev/null +++ b/pkg/cloudbroker/node/update.go @@ -0,0 +1,32 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update node for actual version +type UpdateRequest struct { + // List of Node IDs + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` +} + +// Update updates node for actual version +func (n Node) Update(ctx context.Context, req UpdateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/update" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/pcidevice.go b/pkg/cloudbroker/pcidevice.go new file mode 100644 index 0000000..e35d461 --- /dev/null +++ b/pkg/cloudbroker/pcidevice.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/pcidevice" + +// Accessing the PCI Device method group +func (cb *CloudBroker) PCIDevice() *pcidevice.PCIDevice { + return pcidevice.New(cb.client) +} diff --git a/pkg/cloudbroker/pcidevice/create.go b/pkg/cloudbroker/pcidevice/create.go new file mode 100644 index 0000000..7e2eaf8 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/create.go @@ -0,0 +1,55 @@ +package pcidevice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to creating PCI device +type CreateRequest struct { + // StackID + // Required: true + StackID uint64 `url:"stackId" json:"stackId" validate:"required"` + + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Name of device + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // PCI address of the device + // Must be in format 0000:1f:2b.0 + // Required: true + HWPath string `url:"hwPath" json:"hwPath" validate:"required,hwPath"` + + // Description, just for information + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` +} + +// Create creates PCI Device +func (p PCIDevice) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/pcidevice/create" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/pcidevice/delete.go b/pkg/cloudbroker/pcidevice/delete.go new file mode 100644 index 0000000..5370f26 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/delete.go @@ -0,0 +1,42 @@ +package pcidevice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to deleting PCI device +type DeleteRequest struct { + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` + + // Force delete + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Delete deletes PCI device +func (p PCIDevice) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/pcidevice/delete" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/pcidevice/disable.go b/pkg/cloudbroker/pcidevice/disable.go new file mode 100644 index 0000000..956a13c --- /dev/null +++ b/pkg/cloudbroker/pcidevice/disable.go @@ -0,0 +1,42 @@ +package pcidevice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct for disabling PCI device +type DisableRequest struct { + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` + + // Force delete + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Disable disables PCI device +func (p PCIDevice) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/pcidevice/disable" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/pcidevice/enable.go b/pkg/cloudbroker/pcidevice/enable.go new file mode 100644 index 0000000..952fa56 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/enable.go @@ -0,0 +1,38 @@ +package pcidevice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct for enabling PCI device +type EnableRequest struct { + // PCI device ID + // Required: true + DeviceID uint64 `url:"deviceId" json:"deviceId" validate:"required"` +} + +// Enable enables PCI device +func (p PCIDevice) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/pcidevice/enable" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/pcidevice/ids.go b/pkg/cloudbroker/pcidevice/ids.go new file mode 100644 index 0000000..f558dbb --- /dev/null +++ b/pkg/cloudbroker/pcidevice/ids.go @@ -0,0 +1,10 @@ +package pcidevice + +// IDs gets array of PCIDeviceIDs from ListPCIDevices struct +func (lpd ListPCIDevices) IDs() []uint64 { + res := make([]uint64, 0, len(lpd.Data)) + for _, lb := range lpd.Data { + res = append(res, lb.ID) + } + return res +} diff --git a/pkg/cloudbroker/pcidevice/list.go b/pkg/cloudbroker/pcidevice/list.go new file mode 100644 index 0000000..038def4 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/list.go @@ -0,0 +1,76 @@ +package pcidevice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of pci devices +type ListRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by computeId + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by rgId + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all pci devices as a ListPCIDevices struct +func (p PCIDevice) List(ctx context.Context, req ListRequest) (*ListPCIDevices, error) { + + res, err := p.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListPCIDevices{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all pci devices as an array of bytes +func (p PCIDevice) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/pcidevice/list" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/pcidevice/models.go b/pkg/cloudbroker/pcidevice/models.go new file mode 100644 index 0000000..0a125b0 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/models.go @@ -0,0 +1,50 @@ +package pcidevice + +// Main information about PCI device +type ItemPCIDevice struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Description + Description string `json:"description"` + + // GUID + GUID uint64 `json:"guid"` + + // HwPath + HwPath string `json:"hwPath"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // Status + Status string `json:"status"` + + // System name + SystemName string `json:"systemName"` +} + +// List PCI devices +type ListPCIDevices struct { + // Data + Data []ItemPCIDevice `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + diff --git a/pkg/cloudbroker/pcidevice/pcidevice.go b/pkg/cloudbroker/pcidevice/pcidevice.go new file mode 100644 index 0000000..82b3d3a --- /dev/null +++ b/pkg/cloudbroker/pcidevice/pcidevice.go @@ -0,0 +1,15 @@ +package pcidevice + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to PCI device +type PCIDevice struct { + client interfaces.Caller +} + +// Builder for PCI device endpoints +func New(client interfaces.Caller) *PCIDevice { + return &PCIDevice{ + client: client, + } +} diff --git a/pkg/cloudbroker/pcidevice/serialize.go b/pkg/cloudbroker/pcidevice/serialize.go new file mode 100644 index 0000000..7616b54 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/serialize.go @@ -0,0 +1,43 @@ +package pcidevice + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (l ListPCIDevices) Serialize(params ...string) (serialization.Serialized, error) { + if len(l.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(l, prefix, indent) + } + + return json.Marshal(l) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (i ItemPCIDevice) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(i, prefix, indent) + } + + return json.Marshal(i) +} diff --git a/pkg/cloudbroker/prometheus.go b/pkg/cloudbroker/prometheus.go new file mode 100644 index 0000000..d52fa13 --- /dev/null +++ b/pkg/cloudbroker/prometheus.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/prometheus" + +// Accessing the Prometheus method group +func (cb *CloudBroker) Prometheus() *prometheus.Prometheus { + return prometheus.New(cb.client) +} diff --git a/pkg/cloudbroker/prometheus/compute_cpu_load.go b/pkg/cloudbroker/prometheus/compute_cpu_load.go new file mode 100644 index 0000000..aac3b7d --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_cpu_load.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeCPULoadRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Per-second CPU time consumed by Compute in percent, average over the time step specified +func (p Prometheus) ComputeCPULoad(ctx context.Context, req ComputeCPULoadRequest) (*PrometheusData, error) { + res, err := p.ComputeCPULoadRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeCPULoadRaw(ctx context.Context, req ComputeCPULoadRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeCPUload" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_memory_available.go b/pkg/cloudbroker/prometheus/compute_memory_available.go new file mode 100644 index 0000000..db82f7e --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_memory_available.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeMemoryAvailableRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` +} + +// Available Memory +func (p Prometheus) ComputeMemoryAvailable(ctx context.Context, req ComputeMemoryAvailableRequest) (*PrometheusData, error) { + res, err := p.ComputeMemoryAvailableRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeMemoryAvailableRaw(ctx context.Context, req ComputeMemoryAvailableRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeMemoryAvailable" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_memory_unused.go b/pkg/cloudbroker/prometheus/compute_memory_unused.go new file mode 100644 index 0000000..c64ac5c --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_memory_unused.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeMemoryUnusedRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` +} + +// Unused Memory +func (p Prometheus) ComputeMemoryUnused(ctx context.Context, req ComputeMemoryUnusedRequest) (*PrometheusData, error) { + res, err := p.ComputeMemoryUnusedRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeMemoryUnusedRaw(ctx context.Context, req ComputeMemoryUnusedRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeMemoryUnused" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_memory_usable.go b/pkg/cloudbroker/prometheus/compute_memory_usable.go new file mode 100644 index 0000000..1f1028b --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_memory_usable.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeMemoryUsableRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` +} + +// Usable Memory +func (p Prometheus) ComputeMemoryUsable(ctx context.Context, req ComputeMemoryUsableRequest) (*PrometheusData, error) { + res, err := p.ComputeMemoryUsableRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeMemoryUsableRaw(ctx context.Context, req ComputeMemoryUsableRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeMemoryUsable" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_memory_usage.go b/pkg/cloudbroker/prometheus/compute_memory_usage.go new file mode 100644 index 0000000..b1cad82 --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_memory_usage.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeMemoryUsageRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` +} + +// Memory Usage +func (p Prometheus) ComputeMemoryUsage(ctx context.Context, req ComputeMemoryUsageRequest) (*PrometheusData, error) { + res, err := p.ComputeMemoryUsageRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeMemoryUsageRaw(ctx context.Context, req ComputeMemoryUsageRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeMemoryUsage" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_memory_used.go b/pkg/cloudbroker/prometheus/compute_memory_used.go new file mode 100644 index 0000000..e305bc4 --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_memory_used.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeMemoryUsedRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` +} + +// Used Memory +func (p Prometheus) ComputeMemoryUsed(ctx context.Context, req ComputeMemoryUsedRequest) (*PrometheusData, error) { + res, err := p.ComputeMemoryUsedRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeMemoryUsedRaw(ctx context.Context, req ComputeMemoryUsedRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeMemoryUsed" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_read_bytes.go b/pkg/cloudbroker/prometheus/compute_read_bytes.go new file mode 100644 index 0000000..168ba59 --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_read_bytes.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeReadBytesRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Read Bytes +func (p Prometheus) ComputeReadBytes(ctx context.Context, req ComputeReadBytesRequest) (*PrometheusData, error) { + res, err := p.ComputeReadBytesRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeReadBytesRaw(ctx context.Context, req ComputeReadBytesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeReadBytes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_read_requests.go b/pkg/cloudbroker/prometheus/compute_read_requests.go new file mode 100644 index 0000000..c026a2d --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_read_requests.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeReadRequestsRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Read Requests +func (p Prometheus) ComputeReadRequests(ctx context.Context, req ComputeReadRequestsRequest) (*PrometheusData, error) { + res, err := p.ComputeReadRequestsRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeReadRequestsRaw(ctx context.Context, req ComputeReadRequestsRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeReadRequests" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_receive_bytes.go b/pkg/cloudbroker/prometheus/compute_receive_bytes.go new file mode 100644 index 0000000..53fa8be --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_receive_bytes.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeReceiveBytesRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Receive Bytes +func (p Prometheus) ComputeReceiveBytes(ctx context.Context, req ComputeReceiveBytesRequest) (*PrometheusData, error) { + res, err := p.ComputeReceiveBytesRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeReceiveBytesRaw(ctx context.Context, req ComputeReceiveBytesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeReceiveBytes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_receive_packets.go b/pkg/cloudbroker/prometheus/compute_receive_packets.go new file mode 100644 index 0000000..48dda35 --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_receive_packets.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeReceivePacketsRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Receive Packets +func (p Prometheus) ComputeReceivePackets(ctx context.Context, req ComputeReceivePacketsRequest) (*PrometheusData, error) { + res, err := p.ComputeReceivePacketsRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeReceivePacketsRaw(ctx context.Context, req ComputeReceivePacketsRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeReceivePackets" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_transmit_bytes.go b/pkg/cloudbroker/prometheus/compute_transmit_bytes.go new file mode 100644 index 0000000..b040675 --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_transmit_bytes.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeTransmitBytesRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Transmit Bytes +func (p Prometheus) ComputeTransmitBytes(ctx context.Context, req ComputeTransmitBytesRequest) (*PrometheusData, error) { + res, err := p.ComputeTransmitBytesRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeTransmitBytesRaw(ctx context.Context, req ComputeTransmitBytesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeTransmitBytes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_transmit_packets.go b/pkg/cloudbroker/prometheus/compute_transmit_packets.go new file mode 100644 index 0000000..fce1b1e --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_transmit_packets.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeTransmitPacketsRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Transmit Packets +func (p Prometheus) ComputeTransmitPackets(ctx context.Context, req ComputeTransmitPacketsRequest) (*PrometheusData, error) { + res, err := p.ComputeTransmitPacketsRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeTransmitPacketsRaw(ctx context.Context, req ComputeTransmitPacketsRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeTransmitPackets" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_write_bytes.go b/pkg/cloudbroker/prometheus/compute_write_bytes.go new file mode 100644 index 0000000..1514926 --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_write_bytes.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeWriteBytesRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Write Bytes +func (p Prometheus) ComputeWriteBytes(ctx context.Context, req ComputeWriteBytesRequest) (*PrometheusData, error) { + res, err := p.ComputeWriteBytesRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeWriteBytesRaw(ctx context.Context, req ComputeWriteBytesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeWriteBytes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/compute_write_requests.go b/pkg/cloudbroker/prometheus/compute_write_requests.go new file mode 100644 index 0000000..f14d68b --- /dev/null +++ b/pkg/cloudbroker/prometheus/compute_write_requests.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type ComputeWriteRequestsRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Time to loads of statistic in seconds + // Required: false + ForLast uint64 `url:"forLast,omitempty" json:"forLast,omitempty"` + + // The reading interval in seconds + // Required: true + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: true + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Write Requests +func (p Prometheus) ComputeWriteRequests(ctx context.Context, req ComputeWriteRequestsRequest) (*PrometheusData, error) { + res, err := p.ComputeWriteRequestsRaw(ctx, req) + if err != nil { + return nil, err + } + + info := PrometheusData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (p Prometheus) ComputeWriteRequestsRaw(ctx context.Context, req ComputeWriteRequestsRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computeWriteRequests" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/prometheus/models.go b/pkg/cloudbroker/prometheus/models.go new file mode 100644 index 0000000..6ab647d --- /dev/null +++ b/pkg/cloudbroker/prometheus/models.go @@ -0,0 +1,13 @@ +package prometheus + +// PrometheusData represents an array of data points +type PrometheusData []PrometheusPoint + +// PrometheusPoint represents a single data point +type PrometheusPoint struct { + // Value of the metric at a specific point in time + Value float64 `json:"value"` + + // Timestamp the Unix timestamp. + Timestamp uint64 `json:"timestamp"` +} diff --git a/pkg/cloudbroker/prometheus/prometheus.go b/pkg/cloudbroker/prometheus/prometheus.go new file mode 100644 index 0000000..9a7a70d --- /dev/null +++ b/pkg/cloudbroker/prometheus/prometheus.go @@ -0,0 +1,17 @@ +package prometheus + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to prometheus +type Prometheus struct { + client interfaces.Caller +} + +// Builder for prometheus endpoints +func New(client interfaces.Caller) *Prometheus { + return &Prometheus{ + client: client, + } +} diff --git a/pkg/cloudbroker/resmon.go b/pkg/cloudbroker/resmon.go new file mode 100644 index 0000000..ba2524d --- /dev/null +++ b/pkg/cloudbroker/resmon.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/resmon" +) + +// Accessing the Resmon method group +func (cb *CloudBroker) Resmon() *resmon.Resmon { + return resmon.New(cb.client) +} diff --git a/pkg/cloudbroker/resmon/get_by_compute.go b/pkg/cloudbroker/resmon/get_by_compute.go new file mode 100644 index 0000000..ce2cac4 --- /dev/null +++ b/pkg/cloudbroker/resmon/get_by_compute.go @@ -0,0 +1,53 @@ +package resmon + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type GetByComputeRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Start of time period - unixtime + // Required: false + StartTime uint64 `url:"starttime,omitempty" json:"starttime,omitempty"` + + // End of time period - unixtime + // Required: true + EndTime uint64 `url:"endtime,omitempty" json:"endtime,omitempty"` +} + +// Get resource monitoring for the specified time period for the concrete compute instance +func (r Resmon) GetByCompute(ctx context.Context, req GetByComputeRequest) (*GetByComputeData, error) { + res, err := r.GetByComputeRaw(ctx, req) + if err != nil { + return nil, err + } + + info := GetByComputeData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (r Resmon) GetByComputeRaw(ctx context.Context, req GetByComputeRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/resmon/getByCompute" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/resmon/get_by_computes.go b/pkg/cloudbroker/resmon/get_by_computes.go new file mode 100644 index 0000000..3b510fb --- /dev/null +++ b/pkg/cloudbroker/resmon/get_by_computes.go @@ -0,0 +1,49 @@ +package resmon + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type GetByComputesRequest struct { + // Start of time period - unixtime + // Required: false + StartTime uint64 `url:"starttime,omitempty" json:"starttime,omitempty"` + + // End of time period - unixtime + // Required: true + EndTime uint64 `url:"endtime,omitempty" json:"endtime,omitempty"` +} + +// Get compute instances resource monitoring for the specified time period +func (r Resmon) GetByComputes(ctx context.Context, req GetByComputesRequest) (*GetByComputeData, error) { + res, err := r.GetByComputesRaw(ctx, req) + if err != nil { + return nil, err + } + + info := GetByComputeData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (r Resmon) GetByComputesRaw(ctx context.Context, req GetByComputesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/resmon/getByComputes" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/resmon/get_by_grid.go b/pkg/cloudbroker/resmon/get_by_grid.go new file mode 100644 index 0000000..e17aa93 --- /dev/null +++ b/pkg/cloudbroker/resmon/get_by_grid.go @@ -0,0 +1,49 @@ +package resmon + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type GetByGRIDRequest struct { + // Start of time period - unixtime + // Required: false + StartTime uint64 `url:"starttime,omitempty" json:"starttime,omitempty"` + + // End of time period - unixtime + // Required: true + EndTime uint64 `url:"endtime,omitempty" json:"endtime,omitempty"` +} + +// Get a grid resource monitoring for the specified time period +func (r Resmon) GetByGRID(ctx context.Context, req GetByGRIDRequest) (*GetByGRIDData, error) { + res, err := r.GetByGRIDRaw(ctx, req) + if err != nil { + return nil, err + } + + info := GetByGRIDData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (r Resmon) GetByGRIDRaw(ctx context.Context, req GetByGRIDRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/resmon/getByGrid" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/resmon/get_by_stack.go b/pkg/cloudbroker/resmon/get_by_stack.go new file mode 100644 index 0000000..5d83448 --- /dev/null +++ b/pkg/cloudbroker/resmon/get_by_stack.go @@ -0,0 +1,53 @@ +package resmon + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type GetByStackRequest struct { + // Stack ID + // Required: true + StackID uint64 `url:"stackId" json:"stackId" validate:"required"` + + // Start of time period - unixtime + // Required: false + StartTime uint64 `url:"starttime,omitempty" json:"starttime,omitempty"` + + // End of time period - unixtime + // Required: true + EndTime uint64 `url:"endtime,omitempty" json:"endtime,omitempty"` +} + +// Get a grid resource monitoring for the specified time period +func (r Resmon) GetByStack(ctx context.Context, req GetByStackRequest) (*GetByStackData, error) { + res, err := r.GetByStackRaw(ctx, req) + if err != nil { + return nil, err + } + + info := GetByStackData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (r Resmon) GetByStackRaw(ctx context.Context, req GetByStackRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/resmon/getByStack" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/resmon/get_by_stacks.go b/pkg/cloudbroker/resmon/get_by_stacks.go new file mode 100644 index 0000000..3a4dfae --- /dev/null +++ b/pkg/cloudbroker/resmon/get_by_stacks.go @@ -0,0 +1,49 @@ +package resmon + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type GetByStacksRequest struct { + // Start of time period - unixtime + // Required: false + StartTime uint64 `url:"starttime,omitempty" json:"starttime,omitempty"` + + // End of time period - unixtime + // Required: true + EndTime uint64 `url:"endtime,omitempty" json:"endtime,omitempty"` +} + +// Get a grid resource monitoring for the specified time period +func (r Resmon) GetByStacks(ctx context.Context, req GetByStacksRequest) (*GetByStackData, error) { + res, err := r.GetByStacksRaw(ctx, req) + if err != nil { + return nil, err + } + + info := GetByStackData{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about compute as an array of bytes +func (r Resmon) GetByStacksRaw(ctx context.Context, req GetByStacksRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/resmon/getByStacks" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/resmon/models.go b/pkg/cloudbroker/resmon/models.go new file mode 100644 index 0000000..5206bc8 --- /dev/null +++ b/pkg/cloudbroker/resmon/models.go @@ -0,0 +1,89 @@ +package resmon + +// GetByComputeData represents an array of data points +type GetByComputeData []GetByComputePoint + +type GetByComputePoint struct { + ID uint64 `json:"id"` + Name string `json:"name"` + ComputeID uint64 `json:"computeId"` + AccountID uint64 `json:"accountId"` + RGID uint64 `json:"rgId"` + Usage ComputeUsage `json:"usage"` + Disks []ItemDisk `json:"disks"` + UID string `json:"uid"` + StackID uint64 `json:"stackId"` +} + +type ComputeUsage struct { + VCPUsConsumed uint64 `json:"vcpusConsumed"` + Storage uint64 `json:"storage"` + CPUTime uint64 `json:"cpuTime"` + ExtIPs uint64 `json:"extips"` + RAMConsumed uint64 `json:"ramConsumed"` + VCPUsReserved uint64 `json:"vcpusReserved"` + IsUp uint64 `json:"isUp"` + RAMConsumedReal uint64 `json:"ramConsumedReal"` + RAMReserved uint64 `json:"ramReserved"` +} + +type ItemDisk struct { + Pool string `json:"pool"` + ResID string `json:"resId"` + SizeUsed uint64 `json:"sizeUsed"` + SizeMax uint64 `json:"sizeMax"` +} + +// GetByGRIDData represents an array of data points +type GetByGRIDData []GetByGRIDPoint + +type GetByGRIDPoint struct { + UID string `json:"uid"` + Total ItemTotalByGRID `json:"total"` + Storages map[string]ItemStorage `json:"storages"` +} + +type ItemTotalByGRID struct { + StacksCPU uint64 `json:"stacksCPU"` + StorageCapacity uint64 `json:"storageCapacity"` + CPUPower uint64 `json:"cpuPower"` + CPUUtil uint64 `json:"cpuUtil"` + TotalMem uint64 `json:"totalMem"` + ReservedMem uint64 `json:"reservedMem"` + UsedMem uint64 `json:"usedMem"` + FreeMem uint64 `json:"freeMem"` + VCPUConsumed uint64 `json:"vcpuConsumed"` +} + +type ItemStorage struct { + CapacityLimit uint64 `json:"capacityLimit"` + Consumed uint64 `json:"consumed"` + Type string `json:"type"` + UID string `json:"uid"` +} + +// GetByStackData represents an array of data points +type GetByStackData []GetByStackPoint + +type GetByStackPoint struct { + Usage StackUsage `json:"usage"` + CPUInfo CPUinfoByStack `json:"cpuInfo"` + Name string `json:"name"` + ID uint64 `json:"id"` +} + +type StackUsage struct { + CPUPower uint64 `json:"cpuPower"` + UsedVCPUs uint64 `json:"usedVcpus"` + PCPU uint64 `json:"pcpu"` + UsedMem uint64 `json:"usedMem"` + CPUUtil uint64 `json:"cpuUtil"` + ReservedMem uint64 `json:"reservedMem"` + FreeMem uint64 `json:"freeMem"` +} + +type CPUinfoByStack struct { + ClockSpeed uint64 `json:"clockSpeed"` + CoreCount uint64 `json:"coreCount"` + PhysCount uint64 `json:"physCount"` +} diff --git a/pkg/cloudbroker/resmon/resmon.go b/pkg/cloudbroker/resmon/resmon.go new file mode 100644 index 0000000..4cbea63 --- /dev/null +++ b/pkg/cloudbroker/resmon/resmon.go @@ -0,0 +1,15 @@ +package resmon + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to resource monitoring +type Resmon struct { + client interfaces.Caller +} + +// Builder for resource monitoring endpoints +func New(client interfaces.Caller) *Resmon { + return &Resmon{ + client: client, + } +} diff --git a/pkg/cloudbroker/rg.go b/pkg/cloudbroker/rg.go new file mode 100644 index 0000000..af6cc97 --- /dev/null +++ b/pkg/cloudbroker/rg.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/rg" + +// Accessing the RG method group +func (cb *CloudBroker) RG() *rg.RG { + return rg.New(cb.client) +} diff --git a/pkg/cloudbroker/rg/access_grant.go b/pkg/cloudbroker/rg/access_grant.go new file mode 100644 index 0000000..8ebe17f --- /dev/null +++ b/pkg/cloudbroker/rg/access_grant.go @@ -0,0 +1,50 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessGrantRequest struct to grant access to resource group +type AccessGrantRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // User or group name to grant access + // Required: true + User string `url:"user" json:"user" validate:"required"` + + // Access rights to set, + // Should be one of: + // - "R" + // - "RCX" + // - "ARCXDU" + // Required: true + Right string `url:"right" json:"right" validate:"accessType"` +} + +// AccessGrant grants user or group access to the resource group as specified +func (r RG) AccessGrant(ctx context.Context, req AccessGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/accessGrant" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/access_revoke.go b/pkg/cloudbroker/rg/access_revoke.go new file mode 100644 index 0000000..060a7a7 --- /dev/null +++ b/pkg/cloudbroker/rg/access_revoke.go @@ -0,0 +1,42 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessRevokeRequest struct to revoke access +type AccessRevokeRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // User or group name to revoke access + // Required: true + User string `url:"user" json:"user" validate:"required"` +} + +// AccessRevoke revokes specified user or group access from the resource group +func (r RG) AccessRevoke(ctx context.Context, req AccessRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/accessRevoke" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/affinity_group_computes.go b/pkg/cloudbroker/rg/affinity_group_computes.go new file mode 100644 index 0000000..2f8a0be --- /dev/null +++ b/pkg/cloudbroker/rg/affinity_group_computes.go @@ -0,0 +1,43 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityGroupComputesRequest struct to get list of all computes with their relationships +type AffinityGroupComputesRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Affinity group label + // Required: true + AffinityGroup string `url:"affinityGroup" json:"affinityGroup" validate:"required"` +} + +// AffinityGroupComputes gets list of all computes with their relationships to another computes +func (r RG) AffinityGroupComputes(ctx context.Context, req AffinityGroupComputesRequest) (ListAffinityGroupCompute, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/affinityGroupComputes" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAffinityGroupCompute{} + + if err := json.Unmarshal(res, &list); err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/rg/affinity_groups_get.go b/pkg/cloudbroker/rg/affinity_groups_get.go new file mode 100644 index 0000000..da6282d --- /dev/null +++ b/pkg/cloudbroker/rg/affinity_groups_get.go @@ -0,0 +1,44 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityGroupsGetRequest struct to get list of computes from affinity group +type AffinityGroupsGetRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Label affinity group + // Required: true + AffinityGroup string `url:"affinityGroup" json:"affinityGroup" validate:"required"` +} + +// AffinityGroupsGet gets list of computes in the specified affinity group +func (r RG) AffinityGroupsGet(ctx context.Context, req AffinityGroupsGetRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/affinityGroupsGet" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := make([]uint64, 0) + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/rg/affinity_groups_list.go b/pkg/cloudbroker/rg/affinity_groups_list.go new file mode 100644 index 0000000..fbbb3ad --- /dev/null +++ b/pkg/cloudbroker/rg/affinity_groups_list.go @@ -0,0 +1,48 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AffinityGroupsListRequest struct to get list of affinity groups from resource group +type AffinityGroupsListRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// AffinityGroupsList gets all currently defined affinity groups in this resource group with compute IDs +func (r RG) AffinityGroupsList(ctx context.Context, req AffinityGroupsListRequest) (*ListAffinityGroup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/affinityGroupsList" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAffinityGroup{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/rg/audits.go b/pkg/cloudbroker/rg/audits.go new file mode 100644 index 0000000..217e69a --- /dev/null +++ b/pkg/cloudbroker/rg/audits.go @@ -0,0 +1,40 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuditsRequest struct to get audit +type AuditsRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Audits gets audit records for the specified resource group object +func (r RG) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/audits" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/rg/create.go b/pkg/cloudbroker/rg/create.go new file mode 100644 index 0000000..4133f40 --- /dev/null +++ b/pkg/cloudbroker/rg/create.go @@ -0,0 +1,105 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create resource group +type CreateRequest struct { + // Account, which will own this resource group + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Grid ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // Name of this resource group. Must be unique within the account + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Max size of memory in MB + // Required: false + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + + // Max size of aggregated virtual disks in GB + // Required: false + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + + // Max number of CPU cores + // Required: false + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + + // Max sent/received network transfer peering + // Required: false + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + + // Max number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + + // Username - owner of this resource group. + // Leave blank to set current user as owner + // Required: false + Owner string `url:"owner,omitempty" json:"owner,omitempty"` + + // Type of the default network for this resource group. + // virtual machines created in this resource group will be by default connected to this network. + // Should be one of: + // - PRIVATE + // - PUBLIC + // - NONE + // Required: false + DefNet string `url:"def_net,omitempty" json:"def_net,omitempty" validate:"omitempty,rgDefNet"` + + // Text description of this resource group + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // External network ID + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // External IP address + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Register computes in registration system + // Required: false + RegisterComputes bool `url:"registerComputes,omitempty" json:"registerComputes,omitempty"` + + // List of strings with pools i.e.: ["sep1_poolName1", "sep2_poolName2"] + // Required: false + UniqPools []string `url:"uniqPools,omitempty" json:"uniqPools,omitempty"` + + // Advanced compute features, + // one of: hugepages, numa, cpupin, vfnic + // Required: false + ComputeFeatures []string `url:"computeFeatures,omitempty" json:"computeFeatures,omitempty" validate:"omitempty,computeFeatures"` +} + +// Create creates resource group +func (r RG) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/create" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/delete.go b/pkg/cloudbroker/rg/delete.go new file mode 100644 index 0000000..b0b9dfb --- /dev/null +++ b/pkg/cloudbroker/rg/delete.go @@ -0,0 +1,47 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete resource group +type DeleteRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Set to True if you want force delete non-empty resource group + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` + + // Set to True if you want to destroy resource group and all linked resources, if any, immediately. + // Otherwise, they will be placed into recycle bin and could be restored later within recycle bin's purge period + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes resource group +func (r RG) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/delete" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/disable.go b/pkg/cloudbroker/rg/disable.go new file mode 100644 index 0000000..c544338 --- /dev/null +++ b/pkg/cloudbroker/rg/disable.go @@ -0,0 +1,38 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable resource group +type DisableRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Disable disables resource group by ID +func (r RG) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/disable" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/enable.go b/pkg/cloudbroker/rg/enable.go new file mode 100644 index 0000000..23ec05f --- /dev/null +++ b/pkg/cloudbroker/rg/enable.go @@ -0,0 +1,38 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable resource group +type EnableRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Enable enables resource group by ID +func (r RG) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/enable" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/filter.go b/pkg/cloudbroker/rg/filter.go new file mode 100644 index 0000000..2c1f812 --- /dev/null +++ b/pkg/cloudbroker/rg/filter.go @@ -0,0 +1,89 @@ +package rg + +// FilterByID returns ListRG with specified ID. +func (lrg ListRG) FilterByID(id uint64) ListRG { + predicate := func(irg ItemRG) bool { + return irg.ID == id + } + + return lrg.FilterFunc(predicate) +} + +// FilterByName returns ListRG with specified Name. +func (lrg ListRG) FilterByName(name string) ListRG { + predicate := func(irg ItemRG) bool { + return irg.Name == name + } + + return lrg.FilterFunc(predicate) +} + +// FilterByCreatedBy return ListRG created by specified user. +func (lrg ListRG) FilterByCreatedBy(createdBy string) ListRG { + predicate := func(irg ItemRG) bool { + return irg.CreatedBy == createdBy + } + + return lrg.FilterFunc(predicate) +} + +// FilterByStatus returns ListRG with specified Status. +func (lrg ListRG) FilterByStatus(status string) ListRG { + predicate := func(irg ItemRG) bool { + return irg.Status == status + } + + return lrg.FilterFunc(predicate) +} + +// FilterByLockStatus return ListRG with specified LockStatus. +func (lrg ListRG) FilterByLockStatus(lockStatus string) ListRG { + predicate := func(irg ItemRG) bool { + return irg.LockStatus == lockStatus + } + + return lrg.FilterFunc(predicate) +} + +// FilterByDefNetType returns ListRG with specified DefNetType. +func (lrg ListRG) FilterByDefNetType(defNetType string) ListRG { + predicate := func(irg ItemRG) bool { + return irg.DefNetType == defNetType + } + + return lrg.FilterFunc(predicate) +} + +// FilterByDefNetID returns ListRG with specified DefNetID. +func (lrg ListRG) FilterByDefNetID(defNetID int64) ListRG { + predicate := func(irg ItemRG) bool { + return irg.DefNetID == defNetID + } + + return lrg.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListRG based on a user-specified predicate. +func (lrg ListRG) FilterFunc(predicate func(ItemRG) bool) ListRG { + var result ListRG + + for _, item := range lrg.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemRG. +// If none was found, returns an empty struct. +func (lrg ListRG) FindOne() ItemRG { + if len(lrg.Data) == 0 { + return ItemRG{} + } + + return lrg.Data[0] +} diff --git a/pkg/cloudbroker/rg/filter_test.go b/pkg/cloudbroker/rg/filter_test.go new file mode 100644 index 0000000..f206af6 --- /dev/null +++ b/pkg/cloudbroker/rg/filter_test.go @@ -0,0 +1,247 @@ +package rg + +import "testing" + +var rgs = ListRG{ + Data: []ItemRG{ + { + AccountID: 1, + AccountName: "std", + ACL: []ACL{ + { + Explicit: true, + GUID: "", + Right: "ARCXDU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "sample_user_1@decs3o", + }, + }, + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1676645305, + DefNetID: 1, + DefNetType: "NONE", + DeletedBy: "", + DeletedTime: 0, + Description: "", + GID: 212, + GUID: 7971, + ID: 7971, + LockStatus: "UNLOCKED", + Milestones: 363459, + Name: "rg_1", + RegisterComputes: false, + ResourceLimits: ResourceLimits{ + CUC: -1, + CuD: -1, + CUI: -1, + CUM: -1, + CUNP: -1, + GPUUnits: -1, + }, + Secret: "", + Status: "CREATED", + UpdatedBy: "", + UpdatedTime: 0, + VINS: []uint64{}, + VMs: []uint64{}, + ResTypes: []string{}, + UniqPools: []string{}, + }, + { + AccountID: 2, + AccountName: "std_2", + ACL: []ACL{ + { + Explicit: true, + GUID: "", + Right: "ARCXDU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "sample_user_1@decs3o", + }, + }, + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1676645461, + DefNetID: 2, + DefNetType: "NONE", + DeletedBy: "", + DeletedTime: 0, + Description: "", + GID: 212, + GUID: 7972, + ID: 7972, + LockStatus: "UNLOCKED", + Milestones: 363468, + Name: "rg_2", + RegisterComputes: false, + ResourceLimits: ResourceLimits{ + CUC: -1, + CuD: -1, + CUI: -1, + CUM: -1, + CUNP: -1, + GPUUnits: -1, + }, + Secret: "", + Status: "CREATED", + UpdatedBy: "", + UpdatedTime: 0, + VINS: []uint64{}, + VMs: []uint64{}, + ResTypes: []string{}, + UniqPools: []string{}, + }, + { + AccountID: 3, + AccountName: "std_3", + ACL: []ACL{ + { + Explicit: true, + GUID: "", + Right: "ARCXDU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "sample_user_2@decs3o", + }, + }, + CreatedBy: "sample_user_2@decs3o", + CreatedTime: 1676645548, + DefNetID: 3, + DefNetType: "NONE", + DeletedBy: "", + DeletedTime: 0, + Description: "", + GID: 212, + GUID: 7973, + ID: 7973, + LockStatus: "kjLOCKED", + Milestones: 363471, + Name: "rg_3", + RegisterComputes: false, + ResourceLimits: ResourceLimits{ + CUC: -1, + CuD: -1, + CUI: -1, + CUM: -1, + CUNP: -1, + GPUUnits: -1, + }, + Secret: "", + Status: "DISABLED", + UpdatedBy: "", + UpdatedTime: 0, + VINS: []uint64{}, + VMs: []uint64{ + 48500, + }, + ResTypes: []string{}, + UniqPools: []string{}, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := rgs.FilterByID(7972).FindOne() + + if actual.ID != 7972 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := rgs.FilterByName("rg_1").FindOne() + + if actual.Name != "rg_1" { + t.Fatal("expected Name 'rg_1', found: ", actual.Name) + } +} + +func TestFilterByCreatedBy(t *testing.T) { + actual := rgs.FilterByCreatedBy("sample_user_1@decs3o") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedBy != "sample_user_1@decs3o" { + t.Fatal("expected CreatedBy 'sample_user_1@decs3o', found: ", item.CreatedBy) + } + } +} + +func TestFilterByStatus(t *testing.T) { + actual := rgs.FilterByStatus("CREATED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "CREATED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterByLockStatus(t *testing.T) { + actual := rgs.FilterByLockStatus("UNLOCKED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.LockStatus != "UNLOCKED" { + t.Fatal("expected LockStatus 'UNLOCKED', found: ", item.LockStatus) + } + } +} + +func TestFilterByDefNetType(t *testing.T) { + actual := rgs.FilterByDefNetType("NONE") + + if len(actual.Data) != 3 { + t.Fatal("expected 3 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.DefNetType != "NONE" { + t.Fatal("expected DefNetType 'NONE', found: ", item.DefNetType) + } + } +} + +func TestFilterByDefNetID(t *testing.T) { + actual := rgs.FilterByDefNetID(1).FindOne() + + if actual.DefNetID != 1 { + t.Fatal("expected DefNetID 1, found: ", actual.DefNetID) + } +} + +func TestFilterFunc(t *testing.T) { + actual := rgs.FilterFunc(func(ir ItemRG) bool { + return len(ir.VMs) > 0 + }) + + if len(actual.Data) < 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if len(item.VMs) < 1 { + t.Fatal("expected VMs to contain at least 1 element, found empty") + } + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := rgs.SortByCreatedTime(true) + + if actual.Data[0].CreatedTime != 1676645548 || actual.Data[2].CreatedTime != 1676645305 { + t.Fatal("expected descending order, found ascending") + } +} diff --git a/pkg/cloudbroker/rg/get.go b/pkg/cloudbroker/rg/get.go new file mode 100644 index 0000000..7a8ca96 --- /dev/null +++ b/pkg/cloudbroker/rg/get.go @@ -0,0 +1,46 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about resource group +type GetRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Get gets current configuration of the resource group as a RecordRG struct +func (r RG) Get(ctx context.Context, req GetRequest) (*RecordRG, error) { + res, err := r.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordRG{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets current configuration of the resource group as an array of bytes +func (r RG) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/get" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/rg/get_resource_consumption.go b/pkg/cloudbroker/rg/get_resource_consumption.go new file mode 100644 index 0000000..2295c5e --- /dev/null +++ b/pkg/cloudbroker/rg/get_resource_consumption.go @@ -0,0 +1,40 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetResourceConsumptionRequest struct to get detailed information about resource consumption for ResGroup +type GetResourceConsumptionRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// GetResourceConsumption gets resource consumption of the resource group +func (r RG) GetResourceConsumption(ctx context.Context, req GetResourceConsumptionRequest) (*ItemResourceConsumption, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/getResourceConsumption" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := ItemResourceConsumption{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/rg/ids.go b/pkg/cloudbroker/rg/ids.go new file mode 100644 index 0000000..272570c --- /dev/null +++ b/pkg/cloudbroker/rg/ids.go @@ -0,0 +1,64 @@ +package rg + +// IDs gets array of ResourceGroupIDs from ListRG struct +func (lrg ListRG) IDs() []uint64 { + res := make([]uint64, 0, len(lrg.Data)) + for _, rg := range lrg.Data { + res = append(res, rg.ID) + } + return res +} + +// IDs gets array of ComputeIDs from ListComputes struct +func (lc ListComputes) IDs() []uint64 { + res := make([]uint64, 0, len(lc.Data)) + for _, c := range lc.Data { + res = append(res, c.ID) + } + return res +} + +// IDs gets array of LBIDs from ListLB struct +func (llb ListLB) IDs() []uint64 { + res := make([]uint64, 0, len(llb.Data)) + for _, lb := range llb.Data { + res = append(res, lb.ID) + } + return res +} + +// IDs gets array of VINSIDs from ListVINS struct +func (llb ListVINS) IDs() []uint64 { + res := make([]uint64, 0, len(llb.Data)) + for _, vi := range llb.Data { + res = append(res, vi.ID) + } + return res +} + +// IDs gets array of ResourceGroupIDs from ListResourceConsumption struct +func (lrc ListResourceConsumption) IDs() []uint64 { + res := make([]uint64, 0, len(lrc.Data)) + for _, rg := range lrc.Data { + res = append(res, rg.RGID) + } + return res +} + +// IDs gets array of VINSIDs from ListPFW struct +func (lpfw ListPFW) IDs() []uint64 { + res := make([]uint64, 0, len(lpfw.Data)) + for _, pfw := range lpfw.Data { + res = append(res, pfw.VINSID) + } + return res +} + +// IDs gets array of ComputeIDs from ListAffinityGroupItems struct +func (lag ListAffinityGroupItems) IDs() []uint64 { + res := make([]uint64, 0, len(lag)) + for _, ag := range lag { + res = append(res, ag.ID) + } + return res +} diff --git a/pkg/cloudbroker/rg/list.go b/pkg/cloudbroker/rg/list.go new file mode 100644 index 0000000..3fbddb2 --- /dev/null +++ b/pkg/cloudbroker/rg/list.go @@ -0,0 +1,92 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of resource groups +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by name account + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by created after time (unix timestamp) + // Required: false + CreatedAfter uint64 `url:"createdAfter,omitempty" json:"createdAfter,omitempty"` + + // Find by created before time (unix timestamp) + // Required: false + CreatedBefore uint64 `url:"createdBefore,omitempty" json:"createdBefore,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by status lock + // Required: false + LockStatus string `url:"lockStatus,omitempty" json:"lockStatus,omitempty"` + + // Included deleted resource groups + // Required: false + IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all resource groups the user has access to as a ListRG struct +func (r RG) List(ctx context.Context, req ListRequest) (*ListRG, error) { + + res, err := r.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListRG{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all resource groups the user has access to as an array of bytes +func (r RG) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/list" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/rg/list_computes.go b/pkg/cloudbroker/rg/list_computes.go new file mode 100644 index 0000000..1f17b78 --- /dev/null +++ b/pkg/cloudbroker/rg/list_computes.go @@ -0,0 +1,85 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListComputesRequest struct to get list of computes +type ListComputesRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Find by compute id + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // ID an account + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by ip address + // Required: false + IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"` + + // Find by external network name + // Required: false + ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"` + + // Find by external network id + // Required: false + ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListComputes gets list of all compute instances under specified resource group, accessible by the user +func (r RG) ListComputes(ctx context.Context, req ListComputesRequest) (*ListComputes, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/listComputes" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListComputes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/rg/list_deleted.go b/pkg/cloudbroker/rg/list_deleted.go new file mode 100644 index 0000000..c81a916 --- /dev/null +++ b/pkg/cloudbroker/rg/list_deleted.go @@ -0,0 +1,77 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted resource groups +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by name account + // Required: false + AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` + + // Find by created after time (unix timestamp) + // Required: false + CreatedAfter uint64 `url:"createdAfter,omitempty" json:"createdAfter,omitempty"` + + // Find by created before time (unix timestamp) + // Required: false + CreatedBefore uint64 `url:"createdBefore,omitempty" json:"createdBefore,omitempty"` + + // Find by status lock + // Required: false + LockStatus string `url:"lockStatus,omitempty" json:"lockStatus,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list all deleted resource groups the user has access to +func (r RG) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListRG, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/listDeleted" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListRG{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/rg/list_lb.go b/pkg/cloudbroker/rg/list_lb.go new file mode 100644 index 0000000..83b5559 --- /dev/null +++ b/pkg/cloudbroker/rg/list_lb.go @@ -0,0 +1,77 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListLBRequest struct to get list of load balancers +type ListLBRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by tech status + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by frontend Ip + // Required: false + FrontIP string `url:"frontIp,omitempty" json:"frontIp,omitempty"` + + // Find by backend Ip + // Required: false + BackIP string `url:"backIp,omitempty" json:"backIp,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListLB gets list of all load balancers in the specified resource group, accessible by the user +func (r RG) ListLB(ctx context.Context, req ListLBRequest) (*ListLB, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/listLb" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListLB{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/rg/list_pfw.go b/pkg/cloudbroker/rg/list_pfw.go new file mode 100644 index 0000000..4febb1b --- /dev/null +++ b/pkg/cloudbroker/rg/list_pfw.go @@ -0,0 +1,41 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListPFWRequest struct to get list of port forward rules +type ListPFWRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// ListPFW gets list of port forward rules for the specified resource group +func (r RG) ListPFW(ctx context.Context, req ListPFWRequest) (*ListPFW, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/listPFW" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListPFW{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/rg/list_resource_consumption.go b/pkg/cloudbroker/rg/list_resource_consumption.go new file mode 100644 index 0000000..6ae20df --- /dev/null +++ b/pkg/cloudbroker/rg/list_resource_consumption.go @@ -0,0 +1,26 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" +) + +// ListResourceConsumption gets resource consumptions of the resource groups +func (r RG) ListResourceConsumption(ctx context.Context) (*ListResourceConsumption, error) { + url := "/cloudbroker/rg/listResourceConsumption" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + list := ListResourceConsumption{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/rg/list_vins.go b/pkg/cloudbroker/rg/list_vins.go new file mode 100644 index 0000000..36fd010 --- /dev/null +++ b/pkg/cloudbroker/rg/list_vins.go @@ -0,0 +1,69 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListVINSRequest struct to get list of VINSes +type ListVINSRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // ID an account + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by ip extnet address + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Find by vins id + // Required: false + VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListVINS gets list of all ViNSes under specified resource group, accessible by the user +func (r RG) ListVINS(ctx context.Context, req ListVINSRequest) (*ListVINS, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/listVins" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/rg/mass_delete.go b/pkg/cloudbroker/rg/mass_delete.go new file mode 100644 index 0000000..fe2ea24 --- /dev/null +++ b/pkg/cloudbroker/rg/mass_delete.go @@ -0,0 +1,43 @@ +package rg + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassDeleteRequest struct to delete several resource groups +type MassDeleteRequest struct { + // IDs of the resource groups + // Required: true + RGIDs []uint64 `url:"rgIds" json:"rgIds" validate:"min=1"` + + // Set to true if you want force delete non-empty resource groups + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` + + // Set to true if you want to destroy resource group and all linked + // resources, if any, immediately. + // Otherwise, they will be placed into recycle bin and could be + // restored later within recycle bins purge period + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// MassDelete starts jobs to delete several resource groups +func (r RG) MassDelete(ctx context.Context, req MassDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/massDelete" + + _, err = r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/rg/mass_disable.go b/pkg/cloudbroker/rg/mass_disable.go new file mode 100644 index 0000000..5ee3d74 --- /dev/null +++ b/pkg/cloudbroker/rg/mass_disable.go @@ -0,0 +1,32 @@ +package rg + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassDisableRequest struct to disable several resource groups +type MassDisableRequest struct { + // IDs of the resource groups + // Required: true + RGIDs []uint64 `url:"rgIds" json:"rgIds" validate:"min=1"` +} + +// MassDisable start jobs to disable several resource groups +func (r RG) MassDisable(ctx context.Context, req MassDisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/massDisable" + + _, err = r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/rg/mass_enable.go b/pkg/cloudbroker/rg/mass_enable.go new file mode 100644 index 0000000..3ac582c --- /dev/null +++ b/pkg/cloudbroker/rg/mass_enable.go @@ -0,0 +1,32 @@ +package rg + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassEnableRequest struct to enable several resource groups +type MassEnableRequest struct { + // IDs of the resource groups + // Required: true + RGIDs []uint64 `url:"rgIds" json:"rgIds" validate:"min=1"` +} + +// MassEnable start jobs to enable several resource groups +func (r RG) MassEnable(ctx context.Context, req MassEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/massEnable" + + _, err = r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/rg/models.go b/pkg/cloudbroker/rg/models.go new file mode 100644 index 0000000..d0d541b --- /dev/null +++ b/pkg/cloudbroker/rg/models.go @@ -0,0 +1,720 @@ +package rg + +// Main information about audit +type ItemAudit struct { + // Call + Call string `json:"call"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List Audits +type ListAudits []ItemAudit + +// Reservation information of usage +type Reservation struct { + // Number of CPU + CPU int64 `json:"cpu"` + + // Disk size + DiskSize float64 `json:"disksize"` + + // Max disk size + DiskSizeMax float64 `json:"disksizemax"` + + // External IPs + ExtIPs int64 `json:"extips"` + + // External traffic + ExtTraffic int64 `json:"exttraffic"` + + // Number of GPU + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +// Disk usage +type DiskUsage struct { + // Disk size + DiskSize float64 `json:"disksize"` + + // Disk size max + DiskSizeMax float64 `json:"disksizemax"` +} + +// Resources usage information +type ItemResourceConsumption struct { + // Current information + Consumed Reservation `json:"Consumed"` + + // Reserved information + Reserved Reservation `json:"Reserved"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // Resource group ID + RGID uint64 `json:"rgid"` +} + +type ListResourceConsumption struct { + // Data + Data []ItemResourceConsumption `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Access Control List +type ACL struct { + // Explicit + Explicit bool `json:"explicit"` + + // GUID + GUID string `json:"guid"` + + // Right + Right string `json:"right"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // User group ID + UserGroupID string `json:"userGroupId"` +} + +// List ACL +type ListACL []ACL + +// Resource limits +type ResourceLimits struct { + // CUC + CUC float64 `json:"CU_C"` + + // CUD + CuD float64 `json:"CU_D"` + + // CUDM + CUDM float64 `json:"CU_DM"` + + // CUI + CUI float64 `json:"CU_I"` + + // CUM + CUM float64 `json:"CU_M"` + + // CUNP + CUNP float64 `json:"CU_NP"` + + // GPU units + GPUUnits float64 `json:"gpu_units"` +} + +// Detailed information about resource group +type RecordRG struct { + // Main information about resource group + ItemRG +} + +// Main information about resource group +type ItemRG struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // List ACL + ACL ListACL `json:"acl"` + + // Compute Features + ComputeFeatures []string `json:"computeFeatures"` + + // CPU allocation parameter + CPUAllocationParameter string `json:"cpu_allocation_parameter"` + + // CPU allocation ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // DefNet ID + DefNetID int64 `json:"def_net_id"` + + // DefNet type + DefNetType string `json:"def_net_type"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Dirty + Dirty bool `json:"dirty"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Register computes + RegisterComputes bool `json:"registerComputes"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // Resource types list + ResTypes []string `json:"resourceTypes"` + + // Secret + Secret string `json:"secret"` + + // Status + Status string `json:"status"` + + // Uniq pools + UniqPools []string `json:"uniqPools"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // List VINS IDs + VINS []uint64 `json:"vins"` + + // List virtual machine IDs + VMs []uint64 `json:"vms"` +} + +// List resource groups +type ListRG struct { + // Data + Data []ItemRG `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about affinity group +type ItemAffinityGroupCompute struct { + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Other node + OtherNode []uint64 `json:"otherNode"` + + // Other node indirect + OtherNodeIndirect []uint64 `json:"otherNodeIndirect"` + + // Other node indirect soft + OtherNodeIndirectSoft []uint64 `json:"otherNodeIndirectSoft"` + + // Other node soft + OtherNodeSoft []uint64 `json:"otherNodeSoft"` + + // Same node + SameNode []uint64 `json:"sameNode"` + + // Same node soft + SameNodeSoft []uint64 `json:"sameNodeSoft"` +} + +// List of affinity groups +type ListAffinityGroupCompute []ItemAffinityGroupCompute + +// Main information about affinity rule +type ItemRule struct { + // GUID + GUID string `json:"guid"` + + // Key + Key string `json:"key"` + + // Mode + Mode string `json:"mode"` + + // Policy + Policy string `json:"policy"` + + // Topology + Topology string `json:"topology"` + + // Value + Value string `json:"value"` +} + +// List of rules +type ListRules []ItemRule + +// Main information about compute +type ItemCompute struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Affinity label + AffinityLabel string `json:"affinityLabel"` + + // List affinity rules + AffinityRules ListRules `json:"affinityRules"` + + // Affinity weight + AffinityWeight uint64 `json:"affinityWeight"` + + // Anti affinity rules + AntiAffinityRules ListRules `json:"antiAffinityRules"` + + // Number of CPU + CPUs uint64 `json:"cpus"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Registered + Registered bool `json:"registered"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Total disks size + TotalDisksSize uint64 `json:"totalDisksSize"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed + UserManaged bool `json:"userManaged"` + + // VINS connected + VINSConnected uint64 `json:"vinsConnected"` +} + +// List computes +type ListComputes struct { + // Data + Data []ItemCompute `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about VINS +type ItemVINS struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Computes + Computes uint64 `json:"computes"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // External IP + ExternalIP string `json:"externalIP"` + + // Extnet ID + ExtnetId uint64 `json:"extnetId"` + + // Free IPs + FreeIPs int64 `json:"freeIPs"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // PriVNFDev ID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// List VINSes +type ListVINS struct { + // Data + Data []ItemVINS `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about port forward +type ItemPFW struct { + // Public port end + PublicPortEnd uint64 `json:"Public Port End"` + + // Public port start + PublicPortStart uint64 `json:"Public Port Start"` + + // Virtual machine ID + VMID uint64 `json:"VM ID"` + + // Virtual machine IP + VMIP string `json:"VM IP"` + + // Virtual machine name + VMName string `json:"VM Name"` + + // Virtual machine port + VMPort uint64 `json:"VM Port"` + + // VINS ID + VINSID uint64 `json:"ViNS ID"` + + // VINS name + VINSName string `json:"ViNS Name"` +} + +// List PFWs +type ListPFW struct { + // Data + Data []ItemPFW `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Server settings +type ServerSettings struct { + // Inter + Inter uint64 `json:"inter"` + + // GUID + GUID string `json:"guid"` + + // Down inter + DownInter uint64 `json:"downinter"` + + // Rise + Rise uint64 `json:"rise"` + + // Fall + Fall uint64 `json:"fall"` + + // Slow start + SlowStart uint64 `json:"slowstart"` + + // Max connections + MaxConn uint64 `json:"maxconn"` + + // Max queue + MaxQueue uint64 `json:"maxqueue"` + + // Weight + Weight uint64 `json:"weight"` +} + +// Main information about server +type ItemServer struct { + // Address + Address string `json:"address"` + + // Check + Check string `json:"check"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Port + Port uint64 `json:"port"` + + // Server settings + ServerSettings ServerSettings `json:"serverSettings"` +} + +// List of servers +type ListServers []ItemServer + +// Main information about backend +type ItemBackend struct { + // Algorithm + Algorithm string `json:"algorithm"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Server settings + ServerDefaultSettings ServerSettings `json:"serverDefaultSettings"` + + // List of servers + Servers ListServers `json:"servers"` +} + +// List of backends +type ListBackends []ItemBackend + +// Main information of binding +type ItemBinding struct { + // Address + Address string `json:"address"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` + + // Port + Port uint64 `json:"port"` +} + +// List of bindings +type ListBindings []ItemBinding + +// Main information about frontend +type ItemFrontend struct { + // Backend + Backend string `json:"backend"` + + // List of bindings + Bindings ListBindings `json:"bindings"` + + // GUID + GUID string `json:"guid"` + + // Name + Name string `json:"name"` +} + +// List of frontends +type ListFrontends []ItemFrontend + +// Main information about node +type RecordNode struct { + // Backend IP + BackendIP string `json:"backendIp"` + + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Frontend IP + FrontendIP string `json:"frontendIp"` + + // GUID + GUID string `json:"guid"` + + // MGMT IP + MGMTIP string `json:"mgmtIp"` + + // Network ID + NetworkID uint64 `json:"networkId"` +} + +// Main information about load balancer +type ItemLB struct { + // HAMode + HAMode bool `json:"HAmode"` + + // List ACL + ACL ListACL `json:"acl"` + + // BackendHAIP + BackendHAIP string `json:"backendHAIP"` + + // List backends + Backends ListBackends `json:"backends"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // DPAPI user + DPAPIUser string `json:"dpApiUser"` + + // External network ID + ExtNetID uint64 `json:"extnetId"` + + // FrontendHAIP + FrontendHAIP string `json:"frontendHAIP"` + + // List of frontends + Frontends ListFrontends `json:"frontends"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Primary node + PrimaryNode RecordNode `json:"primaryNode"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // Secondary node + SecondaryNode RecordNode `json:"secondaryNode"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // VINS ID + VINSID uint64 `json:"vinsId"` +} + +// List load balancers +type ListLB struct { + // Data + Data []ItemLB `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type ListAffinityGroup struct { + // Data + Data []map[string]ListAffinityGroupItems `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type ListAffinityGroupItems []ItemAffinityGroup + +type ItemAffinityGroup struct { + ID uint64 `json:"id"` + NodeID uint64 `json:"node_id"` +} diff --git a/pkg/cloudbroker/rg/remove_def_net.go b/pkg/cloudbroker/rg/remove_def_net.go new file mode 100644 index 0000000..2f658aa --- /dev/null +++ b/pkg/cloudbroker/rg/remove_def_net.go @@ -0,0 +1,38 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RemoveDefNetRequest struct to remove default network +type RemoveDefNetRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// RemoveDefNet removes default network from resource group +func (r RG) RemoveDefNet(ctx context.Context, req RemoveDefNetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/removeDefNet" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/restore.go b/pkg/cloudbroker/rg/restore.go new file mode 100644 index 0000000..3118984 --- /dev/null +++ b/pkg/cloudbroker/rg/restore.go @@ -0,0 +1,38 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct to restore resource group +type RestoreRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Restore restores resource group from recycle bin +func (r RG) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/restore" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/rg.go b/pkg/cloudbroker/rg/rg.go new file mode 100644 index 0000000..6fa1233 --- /dev/null +++ b/pkg/cloudbroker/rg/rg.go @@ -0,0 +1,18 @@ +// API Actors for managing resource groups. These actors are the final API for end users to manage resource groups +package rg + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to resource group +type RG struct { + client interfaces.Caller +} + +// Builder for resource group endpoints +func New(client interfaces.Caller) *RG { + return &RG{ + client: client, + } +} diff --git a/pkg/cloudbroker/rg/serialize.go b/pkg/cloudbroker/rg/serialize.go new file mode 100644 index 0000000..374a94d --- /dev/null +++ b/pkg/cloudbroker/rg/serialize.go @@ -0,0 +1,43 @@ +package rg + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lrg ListRG) Serialize(params ...string) (serialization.Serialized, error) { + if len(lrg.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lrg, prefix, indent) + } + + return json.Marshal(lrg) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (irg ItemRG) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(irg, prefix, indent) + } + + return json.Marshal(irg) +} diff --git a/pkg/cloudbroker/rg/set_cpu_allocation_parameter.go b/pkg/cloudbroker/rg/set_cpu_allocation_parameter.go new file mode 100644 index 0000000..7a00afd --- /dev/null +++ b/pkg/cloudbroker/rg/set_cpu_allocation_parameter.go @@ -0,0 +1,44 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCPUAllocationParameterRequest struct for setting CPU allocation parameter +type SetCPUAllocationParameterRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // CPU allocation parameter. + // If "strict" VM can't be run if not enough CPU resources. + // "loose" allow running VM if not enough resources. + // Required: true + StrictLoose string `url:"strict_loose" json:"strict_loose" validate:"required,strict_loose"` +} + +// SetCPUAllocationParameter sets CPU allocation parameter +func (r RG) SetCPUAllocationParameter(ctx context.Context, req SetCPUAllocationParameterRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/setCpuAllocationParameter" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/set_cpu_allocation_ratio.go b/pkg/cloudbroker/rg/set_cpu_allocation_ratio.go new file mode 100644 index 0000000..575d95a --- /dev/null +++ b/pkg/cloudbroker/rg/set_cpu_allocation_ratio.go @@ -0,0 +1,42 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCPUAllocationRatioRequest struct for setting CPU allocation ratio +type SetCPUAllocationRatioRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // CPU allocation ratio, i.e. one pCPU = ratio*vCPU + // Required: true + Ratio float64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetCPUAllocationRatio sets CPU allocation ratio +func (r RG) SetCPUAllocationRatio(ctx context.Context, req SetCPUAllocationRatioRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/setCpuAllocationRatio" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/set_def_net.go b/pkg/cloudbroker/rg/set_def_net.go new file mode 100644 index 0000000..b374337 --- /dev/null +++ b/pkg/cloudbroker/rg/set_def_net.go @@ -0,0 +1,49 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetDefNetRequest struct to set default network +type SetDefNetRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Network type + // Should be one of: + // - "PUBLIC" + // - "PRIVATE" + // Required: true + NetType string `url:"netType" json:"netType" validate:"rgNetType"` + + // Network ID + // Required: false + NetID uint64 `url:"netId,omitempty" json:"netId,omitempty"` +} + +// SetDefNet sets default network for attach associated virtual machines +func (r RG) SetDefNet(ctx context.Context, req SetDefNetRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/setDefNet" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/sorting.go b/pkg/cloudbroker/rg/sorting.go new file mode 100644 index 0000000..37dc53a --- /dev/null +++ b/pkg/cloudbroker/rg/sorting.go @@ -0,0 +1,60 @@ +package rg + +import "sort" + +// SortByCreatedTime sorts ListRG by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lrg ListRG) SortByCreatedTime(inverse bool) ListRG { + if len(lrg.Data) < 2 { + return lrg + } + + sort.Slice(lrg.Data, func(i, j int) bool { + if inverse { + return lrg.Data[i].CreatedTime > lrg.Data[j].CreatedTime + } + + return lrg.Data[i].CreatedTime < lrg.Data[j].CreatedTime + }) + + return lrg +} + +// SortByUpdatedTime sorts ListRG by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lrg ListRG) SortByUpdatedTime(inverse bool) ListRG { + if len(lrg.Data) < 2 { + return lrg + } + + sort.Slice(lrg.Data, func(i, j int) bool { + if inverse { + return lrg.Data[i].UpdatedTime > lrg.Data[j].UpdatedTime + } + + return lrg.Data[i].UpdatedTime < lrg.Data[j].UpdatedTime + }) + + return lrg +} + +// SortByDeletedTime sorts ListRG by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lrg ListRG) SortByDeletedTime(inverse bool) ListRG { + if len(lrg.Data) < 2 { + return lrg + } + + sort.Slice(lrg.Data, func(i, j int) bool { + if inverse { + return lrg.Data[i].DeletedTime > lrg.Data[j].DeletedTime + } + + return lrg.Data[i].DeletedTime < lrg.Data[j].DeletedTime + }) + + return lrg +} diff --git a/pkg/cloudbroker/rg/update.go b/pkg/cloudbroker/rg/update.go new file mode 100644 index 0000000..db9d953 --- /dev/null +++ b/pkg/cloudbroker/rg/update.go @@ -0,0 +1,79 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update resource group +type UpdateRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // New name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Max size of memory in MB + // Required: false + MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` + + // Max size of aggregated virtual disks in GB + // Required: false + MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` + + // Max number of CPU cores + // Required: false + MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` + + // Max sent/received network transfer peering + // Required: false + MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` + + // Max number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` + + // Register computes in registration system + // Required: false + RegisterComputes bool `url:"registerComputes,omitempty" json:"registerComputes,omitempty"` + + // List of strings with pools i.e.: ["sep1_poolName1", "sep2_poolName2", etc] + // Required: false + UniqPools []string `url:"uniqPools,omitempty" json:"uniqPools,omitempty"` + + // if True the field will be cleared + // Default: false + // Required: false + ClearUniqPools bool `url:"clearUniqPools" json:"clearUniqPools"` +} + +// Update updates resource group +func (r RG) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/update" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/update_compute_features.go b/pkg/cloudbroker/rg/update_compute_features.go new file mode 100644 index 0000000..dab210c --- /dev/null +++ b/pkg/cloudbroker/rg/update_compute_features.go @@ -0,0 +1,43 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateComputeFeaturesRequest struct to update advanced compute features +type UpdateComputeFeaturesRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Advanced compute features, + // one of: hugepages, numa, cpupin, vfnic + // Required: false + ComputeFeatures []string `url:"computeFeatures,omitempty" json:"computeFeatures,omitempty" validate:"omitempty,computeFeatures"` +} + +// UpdateComputeFeatures updates advanced compute features +func (r RG) UpdateComputeFeatures(ctx context.Context, req UpdateComputeFeaturesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/updateComputeFeatures" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/update_resource_types.go b/pkg/cloudbroker/rg/update_resource_types.go new file mode 100644 index 0000000..bd08563 --- /dev/null +++ b/pkg/cloudbroker/rg/update_resource_types.go @@ -0,0 +1,49 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateResourceTypesRequest struct to update resource types in account +type UpdateResourceTypesRequest struct { + // ID of resource group + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Resource types available to create in this resource group + // Each element in a resource type slice must be one of: + // - compute + // - vins + // - k8s + // - openshift + // - lb + // - flipgroup + // Required: true + ResTypes []string `url:"resourceTypes" json:"resourceTypes" validate:"min=1,resTypes"` +} + +// UpdateResourceTypes updates resource types in account +func (r RG) UpdateResourceTypes(ctx context.Context, req UpdateResourceTypesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/updateResourceTypes" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/rg/usage.go b/pkg/cloudbroker/rg/usage.go new file mode 100644 index 0000000..e6e1611 --- /dev/null +++ b/pkg/cloudbroker/rg/usage.go @@ -0,0 +1,40 @@ +package rg + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UsageRequest struct to get report of resource usage +type UsageRequest struct { + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` +} + +// Usage gets report resource usage on the resource group +func (r RG) Usage(ctx context.Context, req UsageRequest) (*Reservation, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/usage" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := Reservation{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/sep.go b/pkg/cloudbroker/sep.go new file mode 100644 index 0000000..1e3532d --- /dev/null +++ b/pkg/cloudbroker/sep.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/sep" + +// Accessing the SEP method group +func (cb *CloudBroker) SEP() *sep.SEP { + return sep.New(cb.client) +} diff --git a/pkg/cloudbroker/sep/access_grant.go b/pkg/cloudbroker/sep/access_grant.go new file mode 100644 index 0000000..e662bda --- /dev/null +++ b/pkg/cloudbroker/sep/access_grant.go @@ -0,0 +1,43 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessGrantRequest struct to grant access to SEP +type AccessGrantRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Account ID to grant access to the specified SEP. If 0, + // the SEP will be available for all accounts with no exceptions + // Required: true + AccountID uint64 `url:"account_id" json:"account_id" validate:"required"` +} + +// AccessGrant grants access to SEP +func (s SEP) AccessGrant(ctx context.Context, req AccessGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/accessGrant" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/access_grant_to_pool.go b/pkg/cloudbroker/sep/access_grant_to_pool.go new file mode 100644 index 0000000..2663195 --- /dev/null +++ b/pkg/cloudbroker/sep/access_grant_to_pool.go @@ -0,0 +1,50 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessGrantToPoolRequest struct to grant access to pool SEP +type AccessGrantToPoolRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Pool name + // Required: true + PoolName string `url:"pool_name" json:"pool_name" validate:"required"` + + // Account ID to grant access to the specified pool SEP + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` + + // Resource group to grant access to the specified pool SEP + // Required: false + RGID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"` +} + +// AccessGrantToPool grants access to pool SEP +func (s SEP) AccessGrantToPool(ctx context.Context, req AccessGrantToPoolRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/accessGrantToPool" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/access_revoke.go b/pkg/cloudbroker/sep/access_revoke.go new file mode 100644 index 0000000..ad5ffca --- /dev/null +++ b/pkg/cloudbroker/sep/access_revoke.go @@ -0,0 +1,42 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessRevokeRequest struct to revoke access to SEP +type AccessRevokeRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Account ID to revoke access to the specified SEP + // Required: true + AccountID uint64 `url:"account_id" json:"account_id" validate:"required"` +} + +// AccessRevoke revokes access to SEP +func (s SEP) AccessRevoke(ctx context.Context, req AccessRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/accessRevoke" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/access_revoke_to_pool.go b/pkg/cloudbroker/sep/access_revoke_to_pool.go new file mode 100644 index 0000000..678e6e5 --- /dev/null +++ b/pkg/cloudbroker/sep/access_revoke_to_pool.go @@ -0,0 +1,50 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AccessRevokeToPoolRequest struct to revoke access to pool SEP +type AccessRevokeToPoolRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Pool name + // Required: true + PoolName string `url:"pool_name" json:"pool_name" validate:"required"` + + // Account ID to grant access to the specified pool SEP + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` + + // Resource group ID to grant access to the specified pool SEP + // Required: false + RGID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"` +} + +// AccessRevokeToPool revokes access to pool SEP +func (s SEP) AccessRevokeToPool(ctx context.Context, req AccessRevokeToPoolRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/accessRevokeToPool" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/add_consumer_nodes.go b/pkg/cloudbroker/sep/add_consumer_nodes.go new file mode 100644 index 0000000..b6ad4f1 --- /dev/null +++ b/pkg/cloudbroker/sep/add_consumer_nodes.go @@ -0,0 +1,42 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AddConsumerNodesRequest struct to add consumer nodes +type AddConsumerNodesRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // List of nodes IDs + // Required: true + ConsumerNIDs []uint64 `url:"consumer_nids" json:"consumer_nids" validate:"min=1"` +} + +// AddConsumerNodes adds consumer nodes to SEP parameters +func (s SEP) AddConsumerNodes(ctx context.Context, req AddConsumerNodesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/addConsumerNodes" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/add_pool.go b/pkg/cloudbroker/sep/add_pool.go new file mode 100644 index 0000000..a9eed73 --- /dev/null +++ b/pkg/cloudbroker/sep/add_pool.go @@ -0,0 +1,43 @@ +package sep + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AddPoolRequest struct to add pool to storage endpoint (SEP) +type AddPoolRequest struct { + // ID of SEP to add new pool + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // method Async/Sync + // Default: true + // Required: false + Sync bool `url:"sync" json:"sync"` + + // Pool structure which contains fields such as "name", "types", "accessAccountIds", "accessResGroupIds" + // Required: true + Pool string `url:"pool" json:"pool" validate:"required"` +} + +// AddPool adds pool to SEP +func (s SEP) AddPool(ctx context.Context, req AddPoolRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/addPool" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := string(res) + + return result, nil +} diff --git a/pkg/cloudbroker/sep/add_provider_nodes.go b/pkg/cloudbroker/sep/add_provider_nodes.go new file mode 100644 index 0000000..b94507b --- /dev/null +++ b/pkg/cloudbroker/sep/add_provider_nodes.go @@ -0,0 +1,42 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AddProviderNodesRequest struct to add provider nodes +type AddProviderNodesRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // List of node IDs + // Required: true + ProviderNIDs []uint64 `url:"provider_nids" json:"provider_nids" validate:"min=1"` +} + +// AddProviderNodes adds provider nodes to SEP parameters +func (s SEP) AddProviderNodes(ctx context.Context, req AddProviderNodesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/addProviderNodes" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/config_field_edit.go b/pkg/cloudbroker/sep/config_field_edit.go new file mode 100644 index 0000000..18ef1fa --- /dev/null +++ b/pkg/cloudbroker/sep/config_field_edit.go @@ -0,0 +1,56 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ConfigFieldEditRequest struct to edit config fields +type ConfigFieldEditRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Field name + // Required: true + FieldName string `url:"field_name" json:"field_name" validate:"required"` + + // Field value + // Required: true + FieldValue string `url:"field_value" json:"field_value" validate:"required"` + + // Field type + // Should be one of: + // - int + // - str + // - bool + // - list + // - dict + // Required: true + FieldType string `url:"field_type" json:"field_type" validate:"sepFieldType"` +} + +// ConfigFieldEdit edits SEP config field value +func (s SEP) ConfigFieldEdit(ctx context.Context, req ConfigFieldEditRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/configFieldEdit" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/config_insert.go b/pkg/cloudbroker/sep/config_insert.go new file mode 100644 index 0000000..ae0e0b2 --- /dev/null +++ b/pkg/cloudbroker/sep/config_insert.go @@ -0,0 +1,42 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ConfigInsertRequest struct to insert config +type ConfigInsertRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Storage provider config + // Required: true + Config string `url:"config" json:"config" validate:"required"` +} + +// ConfigInsert inserts config to SEP +func (s SEP) ConfigInsert(ctx context.Context, req ConfigInsertRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/configInsert" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/config_validate.go b/pkg/cloudbroker/sep/config_validate.go new file mode 100644 index 0000000..c6ae1ce --- /dev/null +++ b/pkg/cloudbroker/sep/config_validate.go @@ -0,0 +1,42 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ConfigValidateRequest struct to validate config +type ConfigValidateRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Storage provider config + // Required: true + Config string `url:"config" json:"config" validate:"required"` +} + +// ConfigValidate verifies config for the SEP +func (s SEP) ConfigValidate(ctx context.Context, req ConfigValidateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/configValidate" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/consumption.go b/pkg/cloudbroker/sep/consumption.go new file mode 100644 index 0000000..3f50f26 --- /dev/null +++ b/pkg/cloudbroker/sep/consumption.go @@ -0,0 +1,40 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ConsumptionRequest struct to get consumption info +type ConsumptionRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` +} + +// Consumption gets SEP consumption info +func (s SEP) Consumption(ctx context.Context, req ConsumptionRequest) (*RecordConsumption, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/consumption" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordConsumption{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/sep/create.go b/pkg/cloudbroker/sep/create.go new file mode 100644 index 0000000..a2ff497 --- /dev/null +++ b/pkg/cloudbroker/sep/create.go @@ -0,0 +1,66 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create SEP object +type CreateRequest struct { + // Grid ID + // Required: true + GID uint64 `url:"gid" json:"gid" validate:"required"` + + // SEP name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Type of storage + // Required: true + SEPType string `url:"sep_type" json:"sep_type" validate:"required"` + + // SEP config + // Required: true + Config string `url:"config" json:"config" validate:"required"` + + // Description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // List of provider node IDs + // Required: false + ProviderNIDs []uint64 `url:"provider_nids,omitempty" json:"provider_nids,omitempty"` + + // List of consumer node IDs + // Required: false + ConsumerNIDs []uint64 `url:"consumer_nids,omitempty" json:"consumer_nids,omitempty"` + + // Enable SEP after creation + // Required: false + Enable bool `url:"enable,omitempty" json:"enable,omitempty"` +} + +// Create creates SEP object +func (s SEP) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/create" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/decommission.go b/pkg/cloudbroker/sep/decommission.go new file mode 100644 index 0000000..77a056d --- /dev/null +++ b/pkg/cloudbroker/sep/decommission.go @@ -0,0 +1,42 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DecommissionRequest struct for decommission +type DecommissionRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Clear disks and images physically + // Required: false + ClearPhisically bool `url:"clear_physically,omitempty" json:"clear_physically,omitempty"` +} + +// Decommission unlink everything that exists from SEP +func (s SEP) Decommission(ctx context.Context, req DecommissionRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/decommission" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/del_consumer_nodes.go b/pkg/cloudbroker/sep/del_consumer_nodes.go new file mode 100644 index 0000000..2ba5a54 --- /dev/null +++ b/pkg/cloudbroker/sep/del_consumer_nodes.go @@ -0,0 +1,42 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DelConsumerNodesRequest struct to exclude consumer nodes +type DelConsumerNodesRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // List of consumer node IDs + // Required: true + ConsumerNIDs []uint64 `url:"consumer_nids" json:"consumer_nids" validate:"min=1"` +} + +// DelConsumerNodes excludes consumer nodes from SEP parameters +func (s SEP) DelConsumerNodes(ctx context.Context, req DelConsumerNodesRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/delConsumerNodes" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/del_pool.go b/pkg/cloudbroker/sep/del_pool.go new file mode 100644 index 0000000..da5e155 --- /dev/null +++ b/pkg/cloudbroker/sep/del_pool.go @@ -0,0 +1,42 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DelPoolRequest struct to delete pool from storage endpoint (SEP) +type DelPoolRequest struct { + // ID of SEP to delete pool + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Name of pool to delete + // Required: true + PoolName string `url:"pool_name" json:"pool_name" validate:"required"` +} + +// DelPool deletes pool from SEP +func (s SEP) DelPool(ctx context.Context, req DelPoolRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/delPool" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/delete.go b/pkg/cloudbroker/sep/delete.go new file mode 100644 index 0000000..bfacd51 --- /dev/null +++ b/pkg/cloudbroker/sep/delete.go @@ -0,0 +1,38 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete SEP +type DeleteRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` +} + +// Delete deletes SEP by ID +func (s SEP) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/delete" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/disable.go b/pkg/cloudbroker/sep/disable.go new file mode 100644 index 0000000..712fa76 --- /dev/null +++ b/pkg/cloudbroker/sep/disable.go @@ -0,0 +1,38 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable SEP +type DisableRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` +} + +// Disable disables SEP by ID +func (s SEP) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/disable" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/disk_list.go b/pkg/cloudbroker/sep/disk_list.go new file mode 100644 index 0000000..6d5a7d8 --- /dev/null +++ b/pkg/cloudbroker/sep/disk_list.go @@ -0,0 +1,44 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DiskListRequest struct to get list of disk IDs +type DiskListRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Pool name + // Required: false + PoolName string `url:"pool_name,omitempty" json:"pool_name,omitempty"` +} + +// DiskList gets list of disk IDs, who use this SEP and pool (if provided) +func (s SEP) DiskList(ctx context.Context, req DiskListRequest) ([]uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/diskList" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := make([]uint64, 0) + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/sep/enable.go b/pkg/cloudbroker/sep/enable.go new file mode 100644 index 0000000..b10fcdb --- /dev/null +++ b/pkg/cloudbroker/sep/enable.go @@ -0,0 +1,38 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable SEP +type EnableRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` +} + +// Enable enables SEP by ID +func (s SEP) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/enable" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/sep/filter.go b/pkg/cloudbroker/sep/filter.go new file mode 100644 index 0000000..d3876fc --- /dev/null +++ b/pkg/cloudbroker/sep/filter.go @@ -0,0 +1,71 @@ +package sep + +// FilterByID returns ListSEP with specified ID. +func (lsep ListSEP) FilterByID(id uint64) ListSEP { + predicate := func(rsep RecordSEP) bool { + return rsep.ID == id + } + + return lsep.FilterFunc(predicate) +} + +// FilterByName returns ListSEP with specified Name. +func (lsep ListSEP) FilterByName(name string) ListSEP { + predicate := func(rsep RecordSEP) bool { + return rsep.Name == name + } + + return lsep.FilterFunc(predicate) +} + +// FilterByObjStatus returns ListSEP with specified ObjStatus. +func (lsep ListSEP) FilterByObjStatus(objStatus string) ListSEP { + predicate := func(rsep RecordSEP) bool { + return rsep.ObjStatus == objStatus + } + + return lsep.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListSEP with specified TechStatus. +func (lsep ListSEP) FilterByTechStatus(techStatus string) ListSEP { + predicate := func(rsep RecordSEP) bool { + return rsep.TechStatus == techStatus + } + + return lsep.FilterFunc(predicate) +} + +// FilterByType returns ListSEP with specified Type. +func (lsep ListSEP) FilterByType(sepType string) ListSEP { + predicate := func(rsep RecordSEP) bool { + return rsep.Type == sepType + } + + return lsep.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListSEP based on user-specified predicate. +func (lsep ListSEP) FilterFunc(predicate func(RecordSEP) bool) ListSEP { + var result ListSEP + + for _, item := range lsep.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found RecordSEP +// If none was found, returns an empty struct. +func (lsep ListSEP) FindOne() RecordSEP { + if len(lsep.Data) == 0 { + return RecordSEP{} + } + + return lsep.Data[0] +} diff --git a/pkg/cloudbroker/sep/filter_test.go b/pkg/cloudbroker/sep/filter_test.go new file mode 100644 index 0000000..9ea2bd3 --- /dev/null +++ b/pkg/cloudbroker/sep/filter_test.go @@ -0,0 +1,187 @@ +package sep + +import "testing" + +var seps = ListSEP{ + Data: []RecordSEP{ + { + CKey: "", + Meta: []interface{}{ + "osismodel", + "cloudbroker", + "sep", + 1, + }, + Config: map[string]interface{}{ + "API_IPs": []string{ + "10.212.3.61", + "10.212.3.62", + "10.212.3.63", + }, + }, + ConsumedBy: []uint64{ + 27, + }, + Description: "", + GID: 212, + GUID: 1, + ID: 1, + Milestones: 278329, + Name: "sep_1", + ObjStatus: "CREATED", + ProvidedBy: []uint64{ + 24, + 35, + 29, + }, + SharedWith: []uint64{}, + TechStatus: "ENABLED", + Type: "DES", + }, + { + CKey: "", + Meta: []interface{}{ + "osismodel", + "cloudbroker", + "sep", + 1, + }, + Config: map[string]interface{}{ + "API_IPs": []string{ + "10.212.3.64", + "10.212.3.65", + "10.212.3.66", + }, + }, + ConsumedBy: []uint64{ + 32, + 26, + }, + Description: "", + GID: 212, + GUID: 2, + ID: 2, + Milestones: 278337, + Name: "sep_2", + ObjStatus: "CREATED", + ProvidedBy: []uint64{ + 36, + 42, + 35, + }, + SharedWith: []uint64{}, + TechStatus: "ENABLED", + Type: "DES", + }, + { + CKey: "", + Meta: []interface{}{ + "osismodel", + "cloudbroker", + "sep", + 1, + }, + Config: map[string]interface{}{ + "API_IPs": []string{ + "10.212.3.67", + "10.212.3.68", + "10.212.3.69", + }, + }, + ConsumedBy: []uint64{ + 38, + 28, + }, + Description: "", + GID: 212, + GUID: 3, + ID: 3, + Milestones: 278345, + Name: "sep_3", + ObjStatus: "DESTROYED", + ProvidedBy: []uint64{ + 49, + 48, + 41, + }, + SharedWith: []uint64{}, + TechStatus: "DISABLED", + Type: "DES", + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := seps.FilterByID(1).FindOne() + + if actual.ID != 1 { + t.Fatal("expected ID 1, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := seps.FilterByName("sep_2").FindOne() + + if actual.Name != "sep_2" { + t.Fatal("expected Name 'sep_2', found: ", actual.Name) + } +} + +func TestFilterByObjStatus(t *testing.T) { + actual := seps.FilterByObjStatus("CREATED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.ObjStatus != "CREATED" { + t.Fatal("expected ObjStatus 'CREATED', found: ", item.ObjStatus) + } + } +} + +func TestFilterByTechStatus(t *testing.T) { + actual := seps.FilterByTechStatus("ENABLED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.TechStatus != "ENABLED" { + t.Fatal("expected TechStatus 'ENABLED', found: ", item.TechStatus) + } + } +} + +func TestFilterByType(t *testing.T) { + actual := seps.FilterByType("DES") + + if len(actual.Data) != 3 { + t.Fatal("expected 3 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Type != "DES" { + t.Fatal("expected Type 'DES', found: ", item.Type) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := seps.FilterFunc(func(rs RecordSEP) bool { + return len(rs.ConsumedBy) > 1 + }) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if len(item.ConsumedBy) <= 1 { + t.Fatal("expected ConsumedBy to contain more than 1 element, found: ", len(item.ConsumedBy)) + } + } +} diff --git a/pkg/cloudbroker/sep/get.go b/pkg/cloudbroker/sep/get.go new file mode 100644 index 0000000..7341806 --- /dev/null +++ b/pkg/cloudbroker/sep/get.go @@ -0,0 +1,46 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get SEP parameters +type GetRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` +} + +// Get gets SEP parameters as a RecordSEP struct +func (s SEP) Get(ctx context.Context, req GetRequest) (*RecordSEP, error) { + res, err := s.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordSEP{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets SEP parameters as an array of bytes +func (s SEP) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/get" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/sep/get_config.go b/pkg/cloudbroker/sep/get_config.go new file mode 100644 index 0000000..9142fc9 --- /dev/null +++ b/pkg/cloudbroker/sep/get_config.go @@ -0,0 +1,40 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetConfigRequest struct to get SEP config +type GetConfigRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` +} + +// GetConfig gets SEP config +func (s SEP) GetConfig(ctx context.Context, req GetConfigRequest) (*SEPConfig, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/getConfig" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := SEPConfig{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/sep/get_pool.go b/pkg/cloudbroker/sep/get_pool.go new file mode 100644 index 0000000..637dff1 --- /dev/null +++ b/pkg/cloudbroker/sep/get_pool.go @@ -0,0 +1,44 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetPoolRequest struct to get SEP pool config by name +type GetPoolRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Pool name + // Required: true + PoolName string `url:"pool_name" json:"pool_name" validate:"required"` +} + +// GetPool gets SEP pool config by name +func (s SEP) GetPool(ctx context.Context, req GetPoolRequest) (*RecordPool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/getPool" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordPool{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/sep/ids.go b/pkg/cloudbroker/sep/ids.go new file mode 100644 index 0000000..ff461d6 --- /dev/null +++ b/pkg/cloudbroker/sep/ids.go @@ -0,0 +1,10 @@ +package sep + +// IDs gets array of SEPIDs from ListSEP struct +func (ls ListSEP) IDs() []uint64 { + res := make([]uint64, 0, len(ls.Data)) + for _, s := range ls.Data { + res = append(res, s.ID) + } + return res +} diff --git a/pkg/cloudbroker/sep/list.go b/pkg/cloudbroker/sep/list.go new file mode 100644 index 0000000..45c836c --- /dev/null +++ b/pkg/cloudbroker/sep/list.go @@ -0,0 +1,84 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of SEPs +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by gId + // Required: false + GID uint64 `url:"gid,omitempty" json:"gid,omitempty"` + + // Find by sep type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Find by provided physical node id + // Required: false + ProvidedBy uint64 `url:"providedBy,omitempty" json:"providedBy,omitempty"` + + // Find by techStatus + // Required: false + TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"` + + // Find by consumed physical node id + // Required: false + ConsumedBy uint64 `url:"consumedBy,omitempty" json:"consumedBy,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` +} + +// List gets list of SEPs as a ListSEP struct +func (s SEP) List(ctx context.Context, req ListRequest) (*ListSEP, error) { + + res, err := s.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListSEP{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of SEPs as an array of bytes +func (s SEP) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/list" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/sep/models.go b/pkg/cloudbroker/sep/models.go new file mode 100644 index 0000000..39bfb5d --- /dev/null +++ b/pkg/cloudbroker/sep/models.go @@ -0,0 +1,157 @@ +package sep + +// Total resource information +type Total struct { + // Capacity limit + CapacityLimit uint64 `json:"capacity_limit"` + + // Disk count + DiskCount uint64 `json:"disk_count"` + + // Disk usage + DiskUsage uint64 `json:"disk_usage"` + + // Snapshot count + SnapshotCount uint64 `json:"snapshot_count"` + + // Snapshot usage + SnapshotUsage uint64 `json:"snapshot_usage"` + + // Usage + Usage uint64 `json:"usage"` + + // Usage limit + UsageLimit uint64 `json:"usage_limit"` +} + +type ByPool struct { + + // Disk count + DiskCount uint64 `json:"disk_count"` + + // Disk usage + DiskUsage uint64 `json:"disk_usage"` + + // Snapshot count + SnapshotCount uint64 `json:"snapshot_count"` + + // Snapshot usage + SnapshotUsage uint64 `json:"snapshot_usage"` + + // Usage + Usage uint64 `json:"usage"` + + // Usage limit + UsageLimit uint64 `json:"usage_limit"` +} + +// Main information about consumption +type RecordConsumption struct { + // By pool + ByPool map[string]ByPool `json:"byPool"` + + // Total resource information + Total Total `json:"total"` + + // Type + Type string `json:"type"` +} + +// Main information about URI +type ItemURI struct { + // IP + IP string `json:"ip"` + + // Port + Port uint64 `json:"port"` +} + +// List URIs +type ListURIs []ItemURI + +// Detailed information about SEP pool +type RecordPool struct { + // List access account IDs + AccessAccountIDs []uint64 `json:"accessAccountIds"` + + // List access resource group IDs + AccessResGroupIDs []uint64 `json:"accessResGroupIds"` + + // Name + Name string `json:"name"` + + // Page cache ratio + PageCacheRatio uint64 `json:"pagecache_ratio"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // List types + Types []string `json:"types"` + + // List URIs + URIs ListURIs `json:"uris"` + + // Usage Limit + UsageLimit uint64 `json:"usage_limit"` +} + +// SEP config +type SEPConfig map[string]interface{} + +// Detailed information about SEP +type RecordSEP struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Config + Config SEPConfig `json:"config"` + + // Consumed by + ConsumedBy []uint64 `json:"consumedBy"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Object status + ObjStatus string `json:"objStatus"` + + // Provided by + ProvidedBy []uint64 `json:"providedBy"` + + // Shared with + SharedWith []uint64 `json:"sharedWith"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` +} + +// List SEPs +type ListSEP struct { + // Data + Data []RecordSEP `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/sep/sep.go b/pkg/cloudbroker/sep/sep.go new file mode 100644 index 0000000..683e12c --- /dev/null +++ b/pkg/cloudbroker/sep/sep.go @@ -0,0 +1,18 @@ +// Operator actions for handling interventions on a storage endpoint provider +package sep + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to storage endpoint provider +type SEP struct { + client interfaces.Caller +} + +// Builder for SEP endpoints +func New(client interfaces.Caller) *SEP { + return &SEP{ + client: client, + } +} diff --git a/pkg/cloudbroker/sep/serialize.go b/pkg/cloudbroker/sep/serialize.go new file mode 100644 index 0000000..5813f84 --- /dev/null +++ b/pkg/cloudbroker/sep/serialize.go @@ -0,0 +1,43 @@ +package sep + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lsep ListSEP) Serialize(params ...string) (serialization.Serialized, error) { + if len(lsep.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lsep, prefix, indent) + } + + return json.Marshal(lsep) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (rsep RecordSEP) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(rsep, prefix, indent) + } + + return json.Marshal(rsep) +} diff --git a/pkg/cloudbroker/sep/shared_lock_start.go b/pkg/cloudbroker/sep/shared_lock_start.go new file mode 100644 index 0000000..4d7c737 --- /dev/null +++ b/pkg/cloudbroker/sep/shared_lock_start.go @@ -0,0 +1,87 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SharedLockStartRequest struct to start shared locks +type SharedLockStartRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // List of node IDs for start locks + // Required: false + Nodes []uint64 `url:"nodes,omitempty" json:"nodes,omitempty"` + + // List of pool names for start locks + // Required: false + VGNames []string `url:"vg_names,omitempty" json:"vg_names,omitempty"` + + // If there are LOCKs, ignore them + // Default: false + // Required: false + IgnoreStartedLock bool `url:"ignore_started_lock" json:"ignore_started_lock"` +} + +type wrapperSharedLockStartRequest struct { + SharedLockStartRequest + + AsyncMode bool `url:"sync"` +} + +// SharedLockStart start shared locks without AsyncMode +func (s SEP) SharedLockStart(ctx context.Context, req SharedLockStartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSharedLockStartRequest{ + SharedLockStartRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/sep/sharedLockStart" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// SharedLockStartAsync start shared locks with AsyncMode +func (s SEP) SharedLockStartAsync(ctx context.Context, req SharedLockStartRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSharedLockStartRequest{ + SharedLockStartRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/sep/sharedLockStart" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/sep/shared_lock_stop.go b/pkg/cloudbroker/sep/shared_lock_stop.go new file mode 100644 index 0000000..f7b8a7d --- /dev/null +++ b/pkg/cloudbroker/sep/shared_lock_stop.go @@ -0,0 +1,82 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SharedLockStopRequest struct to stop shared locks +type SharedLockStopRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // List of node IDs for stop locks + // Required: false + Nodes []uint64 `url:"nodes,omitempty" json:"nodes,omitempty"` + + // List of pool names for stop locks + // Required: false + VGNames []string `url:"vg_names,omitempty" json:"vg_names,omitempty"` +} + +type wrapperSharedLockStopRequest struct { + SharedLockStopRequest + + AsyncMode bool `url:"sync"` +} + +// SharedLockStop stop shared locks without AsyncMode +func (s SEP) SharedLockStop(ctx context.Context, req SharedLockStopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSharedLockStopRequest{ + SharedLockStopRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/sep/sharedLockStop" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// SharedLockStopAsync stop shared locks with AsyncMode +func (s SEP) SharedLockStopAsync(ctx context.Context, req SharedLockStopRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSharedLockStopRequest{ + SharedLockStopRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/sep/sharedLockStop" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/sep/update_capacity_limit.go b/pkg/cloudbroker/sep/update_capacity_limit.go new file mode 100644 index 0000000..f21ce38 --- /dev/null +++ b/pkg/cloudbroker/sep/update_capacity_limit.go @@ -0,0 +1,38 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateCapacityLimitRequest struct to update capacity limits +type UpdateCapacityLimitRequest struct { + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` +} + +// UpdateCapacityLimit updates SEP capacity limit +func (s SEP) UpdateCapacityLimit(ctx context.Context, req UpdateCapacityLimitRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/updateCapacityLimit" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/stack.go b/pkg/cloudbroker/stack.go new file mode 100644 index 0000000..08af857 --- /dev/null +++ b/pkg/cloudbroker/stack.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/stack" +) + +// Accessing the Stack method group +func (cb *CloudBroker) Stack() *stack.Stack { + return stack.New(cb.client) +} diff --git a/pkg/cloudbroker/stack/get.go b/pkg/cloudbroker/stack/get.go new file mode 100644 index 0000000..4bf196a --- /dev/null +++ b/pkg/cloudbroker/stack/get.go @@ -0,0 +1,46 @@ +package stack + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get list of stacks +type GetRequest struct { + // Find by ID + // Required: true + StackId uint64 `url:"stackId" json:"stackId" validate:"required"` +} + +// Get gets stack details by ID as an InfoStack struct +func (i Stack) Get(ctx context.Context, req GetRequest) (*InfoStack, error) { + res, err := i.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := InfoStack{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets stack details by ID as an array of bytes +func (i Stack) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/stack/get" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/stack/get_logical_cores_count.go b/pkg/cloudbroker/stack/get_logical_cores_count.go new file mode 100644 index 0000000..8b25eb0 --- /dev/null +++ b/pkg/cloudbroker/stack/get_logical_cores_count.go @@ -0,0 +1,38 @@ +package stack + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetLogicalCoresCountRequest struct to get logical cores count by stack +type GetLogicalCoresCountRequest struct { + // Stack ID + // Required: true + StackId uint64 `url:"stackId" json:"stackId" validate:"required"` +} + +// GetLogicalCoresCount get logical cores count by stack +func (i Stack) GetLogicalCoresCount(ctx context.Context, req GetLogicalCoresCountRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/stack/getLogicalCoresCount" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/stack/ids.go b/pkg/cloudbroker/stack/ids.go new file mode 100644 index 0000000..0a6cc9d --- /dev/null +++ b/pkg/cloudbroker/stack/ids.go @@ -0,0 +1,10 @@ +package stack + +// IDs gets array of StackIDs from ListStacks struct +func (ls ListStacks) IDs() []uint64 { + res := make([]uint64, 0, len(ls.Data)) + for _, s := range ls.Data { + res = append(res, s.ID) + } + return res +} diff --git a/pkg/cloudbroker/stack/list.go b/pkg/cloudbroker/stack/list.go new file mode 100644 index 0000000..004ad4f --- /dev/null +++ b/pkg/cloudbroker/stack/list.go @@ -0,0 +1,72 @@ +package stack + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of stacks +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of stacks as a ListStacks struct +func (i Stack) List(ctx context.Context, req ListRequest) (*ListStacks, error) { + + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListStacks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of stacks as an array of bytes +func (i Stack) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/stack/list" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/stack/models.go b/pkg/cloudbroker/stack/models.go new file mode 100644 index 0000000..3613ea1 --- /dev/null +++ b/pkg/cloudbroker/stack/models.go @@ -0,0 +1,168 @@ +package stack + +// Main information about stack +type InfoStack struct { + // CKey + Ckey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + //API URL + APIURL string `json:"apiUrl"` + + //API key + Apikey string `json:"apikey"` + + // App ID + AppID string `json:"appId"` + + // CPU allocation ratio + CPUAllocationRatio float64 `json:"cpu_allocation_ratio"` + + // Description + Description string `json:"desc"` + + // Descr + Descr string `json:"descr"` + + // Drivers + Drivers []string `json:"drivers"` + + // Eco + Eco interface{} `json:"eco"` + + // Error + Error uint64 `json:"error"` + + // Grid ID + GID uint64 `json:"gid"` + + // GID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // List image IDs + Images []uint64 `json:"images"` + + // Login + Login string `json:"login"` + + // Mem allocation ratio + MemAllocationRatio float64 `json:"mem_allocation_ratio"` + + // Name + Name string `json:"name"` + + // Packegas + Packages Packages `json:"packages"` + + //Password + Password string `json:"passwd"` + + // Reference ID + ReferenceID string `json:"referenceId"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` +} + +// List of stacks +type ListStacks struct { + //List + Data []InfoStack `json:"data"` + + //Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Package +type Packages struct { + // LibvirtBin + LibvirtBin LibvirtBin `json:"libvirt-bin"` + + // LibvirtDaemon + LibvirtDaemon LibvirtDaemon `json:"libvirt-daemon"` + + // Lvm2Lockd + Lvm2Lockd Lvm2Lockd `json:"lvm2-lockd"` + + // OpenvswitchCommon + OpenvswitchCommon OpenvswitchCommon `json:"openvswitch-common"` + + // OpenvswitchSwitch + OpenvswitchSwitch OpenvswitchSwitch `json:"openvswitch-switch"` + + // QemuSystemX86 + QemuSystemX86 QemuSystemX86 `json:"qemu-system-x86"` + + // Sanlock + Sanlock Sanlock `json:"sanlock"` +} + +// LibvirtBin +type LibvirtBin struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +type LibvirtDaemon struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// Lvm2Lockd +type Lvm2Lockd struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// OpenvswitchCommon +type OpenvswitchCommon struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// OpenvswitchSwitch +type OpenvswitchSwitch struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// QemuSystemX86 +type QemuSystemX86 struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} + +// Sanlock +type Sanlock struct { + // InstalledSize + InstalledSize string `json:"installed_size"` + + // Version + Ver string `json:"ver"` +} diff --git a/pkg/cloudbroker/stack/set_cpu_allocation_ratio.go b/pkg/cloudbroker/stack/set_cpu_allocation_ratio.go new file mode 100644 index 0000000..276144d --- /dev/null +++ b/pkg/cloudbroker/stack/set_cpu_allocation_ratio.go @@ -0,0 +1,44 @@ +package stack + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetCpuAllocationRatioRequest struct to set CPU allocation ratio +type SetCpuAllocationRatioRequest struct { + // Stack ID + // Required: true + StackId uint64 `url:"stackId" json:"stackId" validate:"required"` + + // Allocation ratio (zero or positive value) + // Required: true + Ratio float64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetCpuAllocationRatio set CPU allocation ratio +func (i Stack) SetCpuAllocationRatio(ctx context.Context, req SetCpuAllocationRatioRequest) (*InfoStack, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/stack/setCpuAllocationRatio" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := InfoStack{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/stack/set_mem_allocation_ratio.go b/pkg/cloudbroker/stack/set_mem_allocation_ratio.go new file mode 100644 index 0000000..6cf4e50 --- /dev/null +++ b/pkg/cloudbroker/stack/set_mem_allocation_ratio.go @@ -0,0 +1,44 @@ +package stack + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// SetMemAllocationRatioRequest struct to set memory allocation ratio +type SetMemAllocationRatioRequest struct { + // Stack ID + // Required: true + StackId uint64 `url:"stackId" json:"stackId" validate:"required"` + + // Allocation ratio (zero or positive value) + // Required: true + Ratio float64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetMemAllocationRatio set memory allocation ratio +func (i Stack) SetMemAllocationRatio(ctx context.Context, req SetMemAllocationRatioRequest) (*InfoStack, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/stack/setMemAllocationRatio" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := InfoStack{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/stack/stack.go b/pkg/cloudbroker/stack/stack.go new file mode 100644 index 0000000..2426a52 --- /dev/null +++ b/pkg/cloudbroker/stack/stack.go @@ -0,0 +1,15 @@ +package stack + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to stack +type Stack struct { + client interfaces.Caller +} + +// Builder for stack endpoint +func New(client interfaces.Caller) *Stack { + return &Stack{ + client: client, + } +} diff --git a/pkg/cloudbroker/tasks.go b/pkg/cloudbroker/tasks.go new file mode 100644 index 0000000..a73ba78 --- /dev/null +++ b/pkg/cloudbroker/tasks.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/tasks" +) + +// Accessing the tasks method group +func (cb *CloudBroker) Tasks() *tasks.Tasks { + return tasks.New(cb.client) +} diff --git a/pkg/cloudbroker/tasks/get.go b/pkg/cloudbroker/tasks/get.go new file mode 100644 index 0000000..3315d34 --- /dev/null +++ b/pkg/cloudbroker/tasks/get.go @@ -0,0 +1,46 @@ +package tasks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get background API task status and result +type GetRequest struct { + // ID of audit GUID + // Required: true + AuditID string `url:"auditId" json:"auditId" validate:"required"` +} + +// Get gets background API task status and result as a RecordTask struct +func (t Tasks) Get(ctx context.Context, req GetRequest) (*RecordTask, error) { + res, err := t.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + item := RecordTask{} + + err = json.Unmarshal(res, &item) + if err != nil { + return nil, err + } + + return &item, nil +} + +// GetRaw gets background API task status and result as an array of bytes +func (t Tasks) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/tasks/get" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/tasks/list.go b/pkg/cloudbroker/tasks/list.go new file mode 100644 index 0000000..6f4c8d0 --- /dev/null +++ b/pkg/cloudbroker/tasks/list.go @@ -0,0 +1,84 @@ +package tasks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of audits +type ListRequest struct { + // Find by guId + // Required: false + TaskID string `url:"taskId,omitempty" json:"taskId,omitempty"` + + // Find by auditId + // Required: false + AuditID string `url:"auditId,omitempty" json:"auditId,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by completed True or False + // Required: false + Completed interface{} `url:"completed,omitempty" json:"completed,omitempty" validate:"omitempty,isBool"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Find all tasks after point in time (unixtime) + // Required: false + UpdateTimeAt uint64 `url:"updateTimeAt,omitempty" json:"updateTimeAt,omitempty"` + + // Find all tasks before point in time (unixtime) + // Required: false + UpdateTimeTo uint64 `url:"updateTimeTo,omitempty" json:"updateTimeTo,omitempty"` + + // Page number + // Default: 0 + // Default: 0 + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Default: 0 + // Default: 0 + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of user API task with status PROCESSING as a ListTasks struct +func (t Tasks) List(ctx context.Context, req ListRequest) (*ListTasks, error) { + + res, err := t.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + item := ListTasks{} + + err = json.Unmarshal(res, &item) + if err != nil { + return nil, err + } + + return &item, nil +} + +// ListRaw gets list of user API task with status PROCESSING as an array of bytes +func (t Tasks) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/tasks/list" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/tasks/models.go b/pkg/cloudbroker/tasks/models.go new file mode 100644 index 0000000..f2cbd13 --- /dev/null +++ b/pkg/cloudbroker/tasks/models.go @@ -0,0 +1,150 @@ +package tasks + +import ( + "errors" + "fmt" +) + +// Result structure of the task to provide methods +type Result struct { + Result interface{} `json:"result"` +} + +// Detailed information about task +type RecordTask struct { + // Updated by + UpdatedBy string `json:"updatedBy"` + + ItemTask +} + +type ItemTask struct { + // Audit ID + AuditID string `json:"auditId"` + + // Completed + Completed bool `json:"completed"` + + // Error + Error string `json:"error"` + + // GUID + GUID string `json:"guid"` + + // List of logs + Log []string `json:"log"` + + // Final result + Result + + // Stage + Stage string `json:"stage"` + + // Status + Status string `json:"status"` + + // Update time + UpdateTime uint64 `json:"updateTime"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` +} + +// List of tasks +type ListTasks struct { + // Data + Data []ItemTask `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// ID returns ID of cluster or WG or any other resource successfully created as a Result of the task. +// It returns error if Result does not contain any resource ID. +func (r Result) ID() (int, error) { + // check id from cluster - it comes as slice, like [1234, "cluster-name"] + slice, ok := r.Result.([]interface{}) + if ok { + if len(slice) == 0 { + return 0, fmt.Errorf("could not get ID from empty slice") + } + + idFloat64, ok := slice[0].(float64) + if !ok { + return 0, fmt.Errorf("could not get ID from first slice element (%v)", slice[0]) + } + + return int(idFloat64), nil + } + + // check id from other resources - it comes as id + idFloat64, ok := r.Result.(float64) + if ok { + return int(idFloat64), nil + } + + return 0, errors.New("could not get ID because result is neither slice nor number (%v)") +} + +// Name returns name of cluster or wg successfully created as a result of the task. +// It returns error if Result does not contain k8s name. +func (r Result) Name() (string, error) { + slice, ok := r.Result.([]interface{}) + if !ok { + return "", fmt.Errorf("could not convert Result (%v) to slice", r.Result) + } + + if len(slice) < 2 { + return "", fmt.Errorf("could not get name from second slice element") + } + + var name string + name, ok = slice[1].(string) + if !ok { + return "", fmt.Errorf("could not get name from second slice element (%v)", slice[1]) + } + + return name, nil +} + +// ToMaps converts Result to a slice of maps containing back-up information as a result of the task. +// It returns error if Result does not contain back-up information. +func (r Result) ToMaps() ([]map[string]interface{}, error) { + slice, ok := r.Result.([]interface{}) + if !ok { + return nil, fmt.Errorf("could not convert Result (%v) to slice", r.Result) + } + + if len(slice) == 0 { + return nil, fmt.Errorf("could not get maps from empty slice") + } + + result := make([]map[string]interface{}, 0, len(slice)) + for _, s := range slice { + elem, ok := s.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("could not get map[string]interface{} from slice element (%v)", s) + } + result = append(result, elem) + } + + return result, nil +} + +// ToString converts Result to non-empty string. +// It returns error if Result is not a string or is an empty string. +func (r Result) ToString() (string, error) { + status, ok := r.Result.(string) + if !ok { + return "", fmt.Errorf("could not convert Result (%v) to string", r.Result) + } + + if status == "" { + return "", fmt.Errorf("info contains empty string") + } + + return status, nil +} diff --git a/pkg/cloudbroker/tasks/tasks.go b/pkg/cloudbroker/tasks/tasks.go new file mode 100644 index 0000000..c584595 --- /dev/null +++ b/pkg/cloudbroker/tasks/tasks.go @@ -0,0 +1,16 @@ +// User API tasks interface +package tasks + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to tasks +type Tasks struct { + client interfaces.Caller +} + +// Builder for tasks endpoints +func New(client interfaces.Caller) *Tasks { + return &Tasks{ + client: client, + } +} diff --git a/pkg/cloudbroker/user.go b/pkg/cloudbroker/user.go new file mode 100644 index 0000000..bffc075 --- /dev/null +++ b/pkg/cloudbroker/user.go @@ -0,0 +1,7 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/user" + +func (cb *CloudBroker) User() *user.User { + return user.New(cb.client) +} diff --git a/pkg/cloudbroker/user/api_list.go b/pkg/cloudbroker/user/api_list.go new file mode 100644 index 0000000..5e90beb --- /dev/null +++ b/pkg/cloudbroker/user/api_list.go @@ -0,0 +1,41 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// APIListRequest struct for getting API list. +type APIListRequest struct { + // ID of the user. + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` +} + +// APIList gets a list of all API functions that a given user has +// access to according to their apiaccess group membership. +func (u User) APIList(ctx context.Context, req APIListRequest) (*APIsEndpoints, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/apiList" + + info := APIsEndpoints{} + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/user/apiaccess_join.go b/pkg/cloudbroker/user/apiaccess_join.go new file mode 100644 index 0000000..96e27d4 --- /dev/null +++ b/pkg/cloudbroker/user/apiaccess_join.go @@ -0,0 +1,42 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// APIAccessJoinRequest struct for joining user into apiaccess group. +type APIAccessJoinRequest struct { + // ID of the user whose membership will be updated. + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` + + // ID of the API access group to join + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` +} + +// APIAccessJoin joins user into apiaccess group. +func (u User) APIAccessJoin(ctx context.Context, req APIAccessJoinRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/apiaccessJoin" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/user/apiaccess_leave.go b/pkg/cloudbroker/user/apiaccess_leave.go new file mode 100644 index 0000000..8056a96 --- /dev/null +++ b/pkg/cloudbroker/user/apiaccess_leave.go @@ -0,0 +1,42 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// APIAccessLeaveRequest struct for leaving user from apiaccess group. +type APIAccessLeaveRequest struct { + // ID of the user whose membership will be updated. + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` + + // ID of the API access group to leave. + // Required: true + APIAccessID uint64 `url:"apiaccessId" json:"apiaccessId" validate:"required"` +} + +// APIAccessLeave leaves user from apiaccess group. +func (u User) APIAccessLeave(ctx context.Context, req APIAccessLeaveRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/apiaccessLeave" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/user/apiaccess_list.go b/pkg/cloudbroker/user/apiaccess_list.go new file mode 100644 index 0000000..e5591cf --- /dev/null +++ b/pkg/cloudbroker/user/apiaccess_list.go @@ -0,0 +1,42 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// APIAccessListRequest struct for showing list of dicts with information about +// apiaccess groups contains to the user. +type APIAccessListRequest struct { + // ID of the user to list API access groups for. + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` +} + +// APIAccessList shows list of dicts with information about apiaccess groups contains to the user. +func (u User) APIAccessList(ctx context.Context, req APIAccessListRequest) (ListAPIAccess, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/apiaccessList" + + list := ListAPIAccess{} + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &list) + + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/user/create.go b/pkg/cloudbroker/user/create.go new file mode 100644 index 0000000..b718fcf --- /dev/null +++ b/pkg/cloudbroker/user/create.go @@ -0,0 +1,54 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct for creating a user. +type CreateRequest struct { + // ID of user. + // Required: true + Username string `url:"username" json:"username" validate:"required"` + + // Email addresses of the user. + // Required: true + EmailAddress []string `url:"emailaddress" json:"emailaddress" validate:"required"` + + // Password of user + // Required: false + Password string `url:"password,omitempty" json:"password,omitempty"` + + // List of groups this user belongs to. + // Required: false + Groups []string `url:"groups,omitempty" json:"groups,omitempty"` + + // List of apiaccess groups this user belongs to. + // Required: false + APIAccess []uint64 `url:"apiaccess,omitempty" json:"apiaccess,omitempty"` +} + +// Create creates a user. +func (u User) Create(ctx context.Context, req CreateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/create" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/user/delete.go b/pkg/cloudbroker/user/delete.go new file mode 100644 index 0000000..593f9d0 --- /dev/null +++ b/pkg/cloudbroker/user/delete.go @@ -0,0 +1,38 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct for deleting a user. +type DeleteRequest struct { + // ID of user. + // Required: true + Username string `url:"username" json:"username" validate:"required"` +} + +// Delete deletes a user. +func (u User) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/delete" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/user/delete_by_guid.go b/pkg/cloudbroker/user/delete_by_guid.go new file mode 100644 index 0000000..af50ae8 --- /dev/null +++ b/pkg/cloudbroker/user/delete_by_guid.go @@ -0,0 +1,40 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteByGUIDRequest struct for deleting a user using user's GUID. +type DeleteByGUIDRequest struct { + // GUID of user. + // Required: true + GUID string `url:"userguid" json:"userguid" validate:"required"` +} + +// DeleteByGUID deletes a user using user's GUID. +// Note: This actor can also be called using username instead of guid to workaround CBGrid +// allowing userguid or username. +func (u User) DeleteByGUID(ctx context.Context, req DeleteByGUIDRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/deleteByGuid" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/user/delete_users.go b/pkg/cloudbroker/user/delete_users.go new file mode 100644 index 0000000..bf5ff31 --- /dev/null +++ b/pkg/cloudbroker/user/delete_users.go @@ -0,0 +1,38 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteUsersRequest struct for bulk delete a list of users. +type DeleteUsersRequest struct { + // List of user ids + // Required: true + UserIDs string `url:"userIds" json:"userIds" validate:"required"` +} + +// DeleteUsers bulk delete a list of users. +func (u User) DeleteUsers(ctx context.Context, req DeleteUsersRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/deleteUsers" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/user/get.go b/pkg/cloudbroker/user/get.go new file mode 100644 index 0000000..27caeea --- /dev/null +++ b/pkg/cloudbroker/user/get.go @@ -0,0 +1,46 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get user details. +type GetRequest struct { + // ID of the user. + // Required: true + UserID string `url:"userId" json:"userId" validate:"required"` +} + +// Get gets user details as an ItemUser struct. +func (u User) Get(ctx context.Context, req GetRequest) (*ItemUser, error) { + res, err := u.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + item := ItemUser{} + + err = json.Unmarshal(res, &item) + if err != nil { + return nil, err + } + + return &item, nil +} + +// GetRaw gets user details as an array of bytes +func (u User) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/get" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/user/get_audit.go b/pkg/cloudbroker/user/get_audit.go new file mode 100644 index 0000000..d3f3bc9 --- /dev/null +++ b/pkg/cloudbroker/user/get_audit.go @@ -0,0 +1,57 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" +) + +// GetAuditRequest struct for getting user's audits. +type GetAuditRequest struct { + // Name of user (get audits for current user if set to empty). + // Required: false + Username string `url:"username,omitempty" json:"username,omitempty"` + + // Find by api call. + // Required: false + Call string `url:"call,omitempty" json:"call,omitempty"` + + // Find by HTTP status code + // Required: false + StatusCode uint64 `url:"statuscode,omitempty" json:"statuscode,omitempty"` + + // Find all audits after point in time (unixtime) + // Required: false + TimestampAt uint64 `url:"timestampAt,omitempty" json:"timestampAt,omitempty"` + + // Find all audits before point in time (unixtime) + // Required: false + TimestampTo uint64 `url:"timestampTo,omitempty" json:"timestampTo,omitempty"` + + // Page number. + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size, maximum - 100. + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// GetAudit gets user's audits. +func (u User) GetAudit(ctx context.Context, req GetAuditRequest) (ListAudits, error) { + url := "/cloudbroker/user/getAudit" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return ListAudits{}, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return ListAudits{}, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/user/get_matching_usernames.go b/pkg/cloudbroker/user/get_matching_usernames.go new file mode 100644 index 0000000..d482e19 --- /dev/null +++ b/pkg/cloudbroker/user/get_matching_usernames.go @@ -0,0 +1,45 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetMatchingUsernamesRequest struct for getting a list of the matching usernames for a given string. +type GetMatchingUsernamesRequest struct { + // Regex of the usernames to searched for. + // Required: true + UsernameRegex string `url:"usernameregex" json:"usernameregex" validate:"required"` + + // The number of usernames to return. + // Required: true + Limit uint64 `url:"limit" json:"limit" validate:"required"` +} + +// GetMatchingUsernames gets a list of the matching usernames for a given string. +func (u User) GetMatchingUsernames(ctx context.Context, req GetMatchingUsernamesRequest) (ListMatchingUsernames, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/getMatchingUsernames" + + list := ListMatchingUsernames{} + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(res, &list) + + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/user/ids.go b/pkg/cloudbroker/user/ids.go new file mode 100644 index 0000000..35b9cd8 --- /dev/null +++ b/pkg/cloudbroker/user/ids.go @@ -0,0 +1,10 @@ +package user + +// IDs gets array of UserIDs from ListAPIAccess struct +func (us ListAPIAccess) IDs() []uint64 { + res := make([]uint64, 0, len(us)) + for _, us := range us { + res = append(res, us.ID) + } + return res +} diff --git a/pkg/cloudbroker/user/list.go b/pkg/cloudbroker/user/list.go new file mode 100644 index 0000000..55b0960 --- /dev/null +++ b/pkg/cloudbroker/user/list.go @@ -0,0 +1,68 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get all non deleted user instances. +type ListRequest struct { + // Find by ID. + // Required: false + ByID string `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by active. True or False. + // Required: false + Active interface{} `url:"active,omitempty" json:"active,omitempty" validate:"omitempty,isBool"` + + // Find by serviceaccount. True or False. + // Required: false + ServiceAccount interface{} `url:"serviceaccount,omitempty" json:"serviceaccount,omitempty" validate:"omitempty,isBool"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number. + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size, maximum - 100. + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets all non deleted user instances as a ListUsers struct +func (u User) List(ctx context.Context, req ListRequest) (*ListUsers, error) { + + res, err := u.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListUsers{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets all non deleted user instances as an array of bytes +func (u User) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/list" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/user/models.go b/pkg/cloudbroker/user/models.go new file mode 100644 index 0000000..a928088 --- /dev/null +++ b/pkg/cloudbroker/user/models.go @@ -0,0 +1,283 @@ +package user + +import "strconv" + +type ItemUser struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Is active + Active bool `json:"active"` + + // APIAccess + APIAccess map[string]string `json:"apiaccess"` + + // AuthKey + AuthKey string `json:"authkey"` + + // AuthKeys + AuthKeys []interface{} + + // Data + Data string `json:"data"` + + // Description + Description string `json:"description"` + + // Domain + Domain string `json:"domain"` + + // Emails + Emails []string `json:"emails"` + + // GID + GID uint64 `json:"gid"` + + // Groups + Groups []string `json:"groups"` + + // GUID + GUID string `json:"guid"` + + // ID + ID string `json:"id"` + + // LastCheck + LastCheck uint64 `json:"lastcheck"` + + // Mobile + Mobile []interface{} `json:"mobile"` + + // Password + Password string `json:"passwd"` + + // Protected + Protected bool `json:"protected"` + + // Roles + Roles []interface{} `json:"roles"` + + // ServiceAccount + ServiceAccount bool `json:"serviceaccount"` + + // XMPP + XMPP []interface{} `json:"xmpp"` +} + +type ListUsers struct { + Data []ItemUser `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +type ItemAPIAccess struct { + // Description + Description string `json:"desc"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` +} + +type ListAPIAccess []ItemAPIAccess + +type ItemMatchingUsername struct { + // Gravatar URL + GravatarURL string `json:"gravatarurl"` + + // Username + Username string `json:"username"` +} + +type ListMatchingUsernames []ItemMatchingUsername + +type ItemAudit struct { + // Call + Call string `json:"Call"` + + // Response time + ResponseTime ResponseTime `json:"Response Time"` + + // StatusCode + StatusCode StatusCode `json:"Status Code"` + + // Guid + GUID string `json:"Guid"` + + // Time + Time float64 `json:"Time"` +} + +type ListAudits struct { + // Data + Data []ItemAudit `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type ResponseTime float64 + +func (r *ResponseTime) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + *r = ResponseTime(-1) + + return nil + } + + res, err := strconv.ParseFloat(string(b), 64) + if err != nil { + return err + } + + *r = ResponseTime(res) + + return nil +} + +type StatusCode int64 + +func (s *StatusCode) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + *s = StatusCode(-1) + + return nil + } + + res, err := strconv.ParseInt(string(b), 10, 64) + if err != nil { + return err + } + + *s = StatusCode(res) + + return nil +} + +type APIsEndpoints struct { + // CloudAPI endpoints + CloudAPI CloudAPIEndpoints `json:"cloudapi,omitempty"` + + // CloudBroker endpoints + CloudBroker CloudBrokerEndpoints `json:"cloudbroker,omitempty"` + + // LibCloud endpoints + LibCloud LibCloudEndpoints `json:"libcloud,omitempty"` + + // System endpoints + System SystemEndpoints `json:"system,omitempty"` +} + +type CloudAPIEndpoints struct { + Account []string `json:"account,omitempty"` + BService []string `json:"bservice,omitempty"` + CloudSpace []string `json:"cloudspace,omitempty"` + Compute []string `json:"compute,omitempty"` + ComputeCI []string `json:"computeci,omitempty"` + Disks []string `json:"disks,omitempty"` + ExtNet []string `json:"extnet,omitempty"` + FLIPGroup []string `json:"flipgroup,omitempty"` + GPU []string `json:"gpu,omitempty"` + Image []string `json:"image,omitempty"` + K8CI []string `json:"k8ci,omitempty"` + K8S []string `json:"k8s,omitempty"` + KVMX86 []string `json:"kvmx86,omitempty"` + LB []string `json:"lb,omitempty"` + Loactions []string `json:"locations,omitempty"` + Machine []string `json:"machine,omitempty"` + Openshift []string `json:"openshift,omitempty"` + OpenshiftCI []string `json:"openshiftci,omitempty"` + PCIDevice []string `json:"pcidevice,omitempty"` + PortForwarding []string `json:"portforwarding,omitempty"` + Prometheus []string `json:"prometheus,omitempty"` + RG []string `json:"rg,omitempty"` + Sizes []string `json:"sizes,omitempty"` + Tasks []string `json:"tasks,omitempty"` + User []string `json:"user,omitempty"` + VGPU []string `json:"vgpu,omitempty"` + VINS []string `json:"vins,omitempty"` + All bool `json:"ALL,omitempty"` +} + +type CloudBrokerEndpoints struct { + Account []string `json:"account,omitempty"` + APIAccess []string `json:"apiaccess,omitempty"` + Audit []string `json:"audit,omitempty"` + AuditBeat []string `json:"auditbeat,omitempty"` + AuditCollector []string `json:"auditcollector,omitempty"` + BackupCreator []string `json:"backupcreator,omitempty"` + BService []string `json:"bservice,omitempty"` + CloudSpace []string `json:"cloudspace,omitempty"` + Compute []string `json:"compute,omitempty"` + ComputeCI []string `json:"computeci,omitempty"` + Desnode []string `json:"desnode,omitempty"` + Diagnostics []string `json:"diagnostics,omitempty"` + Disks []string `json:"disks,omitempty"` + Eco []string `json:"eco,omitempty"` + ExtNet []string `json:"extnet,omitempty"` + FlIPgroup []string `json:"flipgroup,omitempty"` + Grid []string `json:"grid,omitempty"` + Group []string `json:"group,omitempty"` + Health []string `json:"health,omitempty"` + IaaS []string `json:"iaas,omitempty"` + Image []string `json:"image,omitempty"` + Job []string `json:"job,omitempty"` + K8CI []string `json:"k8ci,omitempty"` + K8S []string `json:"k8s,omitempty"` + KVMX86 []string `json:"kvmx86,omitempty"` + LB []string `json:"lb,omitempty"` + Machine []string `json:"machine,omitempty"` + Metering []string `json:"metering,omitempty"` + Milestones []string `json:"milestones,omitempty"` + Node []string `json:"node,omitempty"` + Openshift []string `json:"openshift,omitempty"` + OpenshiftCI []string `json:"openshiftci,omitempty"` + Ovsnode []string `json:"ovsnode,omitempty"` + PCIDevice []string `json:"pcidevice,omitempty"` + PGPU []string `json:"pgpu,omitempty"` + Prometheus []string `json:"prometheus,omitempty"` + QOS []string `json:"qos,omitempty"` + Resmon []string `json:"resmon,omitempty"` + RG []string `json:"rg,omitempty"` + Sep []string `json:"sep,omitempty"` + Stack []string `json:"stack,omitempty"` + Tasks []string `json:"tasks,omitempty"` + TLock []string `json:"tlock,omitempty"` + User []string `json:"user,omitempty"` + VGPU []string `json:"vgpu,omitempty"` + VINS []string `json:"vins,omitempty"` + VNFDev []string `json:"vnfdev,omitempty"` + ZeroAccess []string `json:"zeroaccess,omitempty"` + All bool `json:"ALL,omitempty"` +} + +type LibCloudEndpoints struct { + Libvirt []string `json:"libvirt,omitempty"` + All bool `json:"ALL,omitempty"` +} + +type SystemEndpoints struct { + AgentController []string `json:"agentcontroller,omitempty"` + Alerts []string `json:"alerts,omitempty"` + Audits []string `json:"audits,omitempty"` + ContentManager []string `json:"contentmanager,omitempty"` + DocGenerator []string `json:"docgenerator,omitempty"` + EmailSender []string `json:"emailsender,omitempty"` + ErrorConditionHandler []string `json:"errorconditionhandler,omitempty"` + GridManager []string `json:"gridmanager,omitempty"` + Health []string `json:"health,omitempty"` + Info []string `json:"info,omitempty"` + InfoMGR []string `json:"infomgr,omitempty"` + Job []string `json:"job,omitempty"` + Log []string `json:"log,omitempty"` + Logo []string `json:"logo,omitempty"` + Oauth []string `json:"oauth,omitempty"` + Task []string `json:"task,omitempty"` + UserManager []string `json:"usermanager,omitempty"` + All bool `json:"ALL,omitempty"` +} diff --git a/pkg/cloudbroker/user/user.go b/pkg/cloudbroker/user/user.go new file mode 100644 index 0000000..9a1ff2c --- /dev/null +++ b/pkg/cloudbroker/user/user.go @@ -0,0 +1,15 @@ +package user + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to User +type User struct { + client interfaces.Caller +} + +// Builder for User endpoints +func New(client interfaces.Caller) *User { + return &User{ + client: client, + } +} diff --git a/pkg/cloudbroker/vfpool.go b/pkg/cloudbroker/vfpool.go new file mode 100644 index 0000000..2697aff --- /dev/null +++ b/pkg/cloudbroker/vfpool.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/vfpool" + +// Accessing the VFPool method group +func (cb *CloudBroker) VFPool() *vfpool.VFPool { + return vfpool.New(cb.client) +} diff --git a/pkg/cloudbroker/vfpool/create.go b/pkg/cloudbroker/vfpool/create.go new file mode 100644 index 0000000..a979166 --- /dev/null +++ b/pkg/cloudbroker/vfpool/create.go @@ -0,0 +1,97 @@ +package vfpool + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct to create vfpool device +type CreateRequest struct { + // Name of device + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Name of device + // Required: false + Config []Config `url:"-" json:"config,omitempty" validate:"omitempty,dive"` + + // List of Account IDs + // Required: false + AccountAccess []uint64 `url:"accountAccess,omitempty" json:"accountAccess,omitempty"` + + // List of RG IDs + // Required: false + RGAccess []uint64 `url:"rgAccess,omitempty" json:"rgAccess,omitempty"` +} + +// Config struct for CreateRequest +type Config struct { + // Node ID + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validation:"required"` + + // NicName + // Required: true + NicName string `url:"nicName" json:"nicName" validation:"required"` + + // VF IDs + // Required: true + VFIDs []uint64 `url:"vfIds" json:"vfIds" validation:"required"` +} + +type wrapperCreateRequest struct { + CreateRequest + Config []string `url:"config,omitempty"` +} + +// Create creates vfpool device +func (v VFPool) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var config []string + + if len(req.Config) != 0 { + config = make([]string, 0, len(req.Config)) + + for c := range req.Config { + b, err := json.Marshal(req.Config[c]) + if err != nil { + return 0, err + } + + config = append(config, string(b)) + } + } else { + config = []string{} + } + + reqWrapped := wrapperCreateRequest{ + CreateRequest: req, + Config: config, + } + + url := "/cloudbroker/vfpool/create" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vfpool/delete.go b/pkg/cloudbroker/vfpool/delete.go new file mode 100644 index 0000000..2c6e7d9 --- /dev/null +++ b/pkg/cloudbroker/vfpool/delete.go @@ -0,0 +1,38 @@ +package vfpool + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete vfpool device +type DeleteRequest struct { + // VFPool device ID + // Required: true + VFPoolID uint64 `url:"id" json:"id" validate:"required"` +} + +// Delete deletes vfpool device +func (v VFPool) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vfpool/delete" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vfpool/disable.go b/pkg/cloudbroker/vfpool/disable.go new file mode 100644 index 0000000..7a59fe1 --- /dev/null +++ b/pkg/cloudbroker/vfpool/disable.go @@ -0,0 +1,38 @@ +package vfpool + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable vfpool device +type DisableRequest struct { + // VFPool device ID + // Required: true + VFPoolID uint64 `url:"id" json:"id" validate:"required"` +} + +// Disable disables vfpool device +func (v VFPool) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vfpool/disable" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vfpool/enable.go b/pkg/cloudbroker/vfpool/enable.go new file mode 100644 index 0000000..25467d1 --- /dev/null +++ b/pkg/cloudbroker/vfpool/enable.go @@ -0,0 +1,38 @@ +package vfpool + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable vfpool device +type EnableRequest struct { + // VFPool device ID + // Required: true + VFPoolID uint64 `url:"id" json:"id" validate:"required"` +} + +// Enable enables vfpool device +func (v VFPool) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vfpool/enable" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vfpool/filter.go b/pkg/cloudbroker/vfpool/filter.go new file mode 100644 index 0000000..78cbbf3 --- /dev/null +++ b/pkg/cloudbroker/vfpool/filter.go @@ -0,0 +1,99 @@ +package vfpool + +// FilterByID returns ListVFPool with specified ID. +func (lvfp ListVFPool) FilterByID(id uint64) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.ID == id + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByGID returns ListVFPool with specified GID. +func (lvfp ListVFPool) FilterByGID(gid uint64) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.GID == gid + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByName returns ListVFPool with specified Name. +func (lvfp ListVFPool) FilterByName(name string) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.Name == name + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByDescription returns ListVFPool with specified Description. +func (lvfp ListVFPool) FilterByDescription(description string) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.Description == description + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByStatus returns ListVFPool with specified Status. +func (lvfp ListVFPool) FilterByStatus(status string) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + return ivfp.Status == status + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByAccountAccess returns ListVFPool with specified AccountAccess. +func (lvfp ListVFPool) FilterByAccountAccess(accountAccess uint64) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + for _, i := range ivfp.AccountAccess { + if i == accountAccess { + return true + } + } + return false + } + + return lvfp.FilterFunc(predicate) +} + +// FilterByRGAccess returns ListVFPool with specified RGAccess. +func (lvfp ListVFPool) FilterByRGAccess(rgAccess uint64) ListVFPool { + predicate := func(ivfp ItemVFPool) bool { + for _, i := range ivfp.RGAccess { + if i == rgAccess { + return true + } + } + return false + } + + return lvfp.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListVFPool based on a user-specified predicate. +func (lvfp ListVFPool) FilterFunc(predicate func(ItemVFPool) bool) ListVFPool { + var result ListVFPool + + for _, item := range lvfp.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemVFPool +// If none was found, returns an empty struct. +func (lvfp ListVFPool) FindOne() ItemVFPool { + if lvfp.EntryCount == 0 { + return ItemVFPool{} + } + + return lvfp.Data[0] +} diff --git a/pkg/cloudbroker/vfpool/filter_test.go b/pkg/cloudbroker/vfpool/filter_test.go new file mode 100644 index 0000000..66d78a3 --- /dev/null +++ b/pkg/cloudbroker/vfpool/filter_test.go @@ -0,0 +1,138 @@ +package vfpool + +import "testing" + +var vfpools = ListVFPool{ + Data: []ItemVFPool{ + { + AccountAccess: []uint64{1, 2}, + Description: "descr", + GID: 1, + ID: 1, + Name: "name", + RGAccess: []uint64{3, 4}, + Status: "ENABLED", + }, + { + AccountAccess: []uint64{}, + Description: "", + GID: 2, + ID: 2, + Name: "name2", + RGAccess: []uint64{}, + Status: "DISABLED", + }, + { + AccountAccess: []uint64{7, 8}, + Description: "", + GID: 215, + ID: 3, + Name: "name3", + RGAccess: []uint64{5, 6}, + Status: "DISABLED", + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := vfpools.FilterByID(1).FindOne() + + if actual.ID != 1 { + t.Fatal("expected ID 1, found: ", actual.ID) + } +} + +func TestFilterByGID(t *testing.T) { + var gid uint64 = 1 + actual := vfpools.FilterByGID(gid).FindOne() + + if actual.GID != gid { + t.Fatal("expected ", gid, " found: ", actual.GID) + } +} + +func TestFilterByName(t *testing.T) { + name := "name" + actual := vfpools.FilterByName(name).FindOne() + + if actual.Name != name { + t.Fatal("expected ", name, " found: ", actual.Name) + } +} + +func TestFilterByDescription(t *testing.T) { + description := "descr" + actual := vfpools.FilterByDescription(description).FindOne() + + if actual.Description != description { + t.Fatal("expected ", description, " found: ", actual.Description) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := vfpools.FilterByStatus("ENABLED") + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected Status 'ENABLED', found: ", item.Status) + } + } +} + +func TestFilterByAccountAccess(t *testing.T) { + var account uint64 = 1 + actual := vfpools.FilterByAccountAccess(account) + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + var found bool + for _, a := range item.AccountAccess { + if a == account { + found = true + } + } + + if !found { + t.Fatalf("expected account access %d, found: %v", account, item.AccountAccess) + } + } +} + +func TestFilterByRGAccess(t *testing.T) { + var rg uint64 = 3 + actual := vfpools.FilterByRGAccess(rg) + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + var found bool + for _, r := range item.RGAccess { + if r == rg { + found = true + } + } + + if !found { + t.Fatalf("expected account access %d, found: %v", rg, item.RGAccess) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := vfpools.FilterFunc(func(ivfpool ItemVFPool) bool { + return ivfpool.GID == ivfpool.ID + }) + + if len(actual.Data) != 2 { + t.Fatal("expected 2 elements, found: ", len(actual.Data)) + } +} diff --git a/pkg/cloudbroker/vfpool/get.go b/pkg/cloudbroker/vfpool/get.go new file mode 100644 index 0000000..99f13e3 --- /dev/null +++ b/pkg/cloudbroker/vfpool/get.go @@ -0,0 +1,46 @@ +package vfpool + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get detailed information about vfpool device +type GetRequest struct { + // ID of vfpool device + // Required: true + VFPoolID uint64 `url:"id" json:"id" validate:"required"` +} + +// Get gets detailed information about vfpool device as a RecordVFPool struct +func (v VFPool) Get(ctx context.Context, req GetRequest) (*RecordVFPool, error) { + res, err := v.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordVFPool{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed information about vfpool device as an array of bytes +func (v VFPool) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vfpool/get" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/vfpool/ids.go b/pkg/cloudbroker/vfpool/ids.go new file mode 100644 index 0000000..1a58061 --- /dev/null +++ b/pkg/cloudbroker/vfpool/ids.go @@ -0,0 +1,19 @@ +package vfpool + +// IDs gets array of VFPool IDs from ListVFPool struct +func (lv ListVFPool) IDs() []uint64 { + res := make([]uint64, 0, len(lv.Data)) + for _, e := range lv.Data { + res = append(res, e.ID) + } + return res +} + +// IDs gets array of VF IDs from VFSInfoList struct +func (lv VFSInfoList) IDs() []uint64 { + res := make([]uint64, 0, len(lv)) + for _, e := range lv { + res = append(res, e.ID) + } + return res +} diff --git a/pkg/cloudbroker/vfpool/list.go b/pkg/cloudbroker/vfpool/list.go new file mode 100644 index 0000000..5b17adc --- /dev/null +++ b/pkg/cloudbroker/vfpool/list.go @@ -0,0 +1,83 @@ +package vfpool + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of vfpool devices +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by Grid ID + // Required: false + GID uint64 `url:"gid,omitempty" json:"gid,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Find by status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by account Access + // Required: false + AccountAccess uint64 `url:"accountAccess,omitempty" json:"accountAccess,omitempty"` + + // Find by resource group Access + // Required: false + RGAccess uint64 `url:"rgAccess,omitempty" json:"rgAccess,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all available vfpool devices as a ListVFPool struct +func (v VFPool) List(ctx context.Context, req ListRequest) (*ListVFPool, error) { + + res, err := v.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListVFPool{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all available vfpool devices as an array of bytes +func (v VFPool) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vfpool/list" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/vfpool/models.go b/pkg/cloudbroker/vfpool/models.go new file mode 100644 index 0000000..3ecd53b --- /dev/null +++ b/pkg/cloudbroker/vfpool/models.go @@ -0,0 +1,116 @@ +package vfpool + +// Main information about vfpool device +type ItemVFPool struct { + // AccountAccess + AccountAccess []uint64 `json:"accountAccess"` + + // CreatedTime + CreatedTime uint64 `json:"createdTime"` + + // Description + Description string `json:"description"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // RGAccess + RGAccess []uint64 `json:"rgAccess"` + + // Status + Status string `json:"status"` + + // UpdatedTime + UpdatedTime uint64 `json:"updatedTime"` + + // VFS + VFS []VFS `json:"vfs"` +} + +// List of information about vfpool devices +type ListVFPool struct { + Data []ItemVFPool `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// Detailed information about vfpool device +type RecordVFPool struct { + // AccountAccess + AccountAccess []uint64 `json:"accountAccess"` + + // CreatedTime + CreatedTime uint64 `json:"createdTime"` + + // Description + Description string `json:"description"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // RGAccess + RGAccess []uint64 `json:"rgAccess"` + + // Status + Status string `json:"status"` + + // UpdatedTime + UpdatedTime uint64 `json:"updatedTime"` + + // VFS + VFS []VFS `json:"vfs"` +} + +// VFS struct +type VFS struct { + // NodeID + NodeID uint64 `json:"nodeId"` + + // UpdatedTime + VFList VFList `json:"vfList"` +} + +// VFList struct +type VFList []VFItem + +// VFItem struct +type VFItem struct { + // NicName + NicName string `json:"nicName"` + + // VFSInfo list + VFSInfo VFSInfoList `json:"vfsInfo"` +} + +// VFSInfoList struct +type VFSInfoList []VFSInfoItem + +// VFSInfoItem struct +type VFSInfoItem struct { + // ID + ID uint64 `json:"id"` + + // Claimed + Claimed bool `json:"claimed"` + + // VM ID + VMID uint64 `json:"vmId"` +} diff --git a/pkg/cloudbroker/vfpool/serialize.go b/pkg/cloudbroker/vfpool/serialize.go new file mode 100644 index 0000000..d840502 --- /dev/null +++ b/pkg/cloudbroker/vfpool/serialize.go @@ -0,0 +1,59 @@ +package vfpool + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lvfpool ListVFPool) Serialize(params ...string) (serialization.Serialized, error) { + if lvfpool.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lvfpool, prefix, indent) + } + + return json.Marshal(lvfpool) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (rvfpool RecordVFPool) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(rvfpool, prefix, indent) + } + + return json.Marshal(rvfpool) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (ivfpool ItemVFPool) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(ivfpool, prefix, indent) + } + + return json.Marshal(ivfpool) +} diff --git a/pkg/cloudbroker/vfpool/update.go b/pkg/cloudbroker/vfpool/update.go new file mode 100644 index 0000000..7415f21 --- /dev/null +++ b/pkg/cloudbroker/vfpool/update.go @@ -0,0 +1,86 @@ +package vfpool + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// UpdateRequest struct to update vfpool device +type UpdateRequest struct { + // VFPool device ID + // Required: true + VFPoolID uint64 `url:"id" json:"id" validate:"required"` + + // Name of vfpool device + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Name of device + // Required: false + Config []Config `url:"-" json:"config,omitempty" validation:"omitempty,dive"` + + // List of Account IDs + // Required: false + AccountAccess []uint64 `url:"accountAccess,omitempty" json:"accountAccess,omitempty"` + + // List of RG IDs + // Required: false + RGAccess []uint64 `url:"rgAccess,omitempty" json:"rgAccess,omitempty"` +} + +type wrapperUpdateRequest struct { + UpdateRequest + Config []string `url:"config,omitempty"` +} + +// Update updates vfpool device +func (v VFPool) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + var config []string + + if len(req.Config) != 0 { + config = make([]string, 0, len(req.Config)) + + for c := range req.Config { + b, err := json.Marshal(req.Config[c]) + if err != nil { + return false, err + } + + config = append(config, string(b)) + } + } else { + config = []string{} + } + + reqWrapped := wrapperUpdateRequest{ + UpdateRequest: req, + Config: config, + } + + url := "/cloudbroker/vfpool/update" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vfpool/vfpool.go b/pkg/cloudbroker/vfpool/vfpool.go new file mode 100644 index 0000000..15f7996 --- /dev/null +++ b/pkg/cloudbroker/vfpool/vfpool.go @@ -0,0 +1,18 @@ +// API Actor for managing vfpool device +package vfpool + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to vfpool +type VFPool struct { + client interfaces.Caller +} + +// Builder for vfpool endpoints +func New(client interfaces.Caller) *VFPool { + return &VFPool{ + client, + } +} diff --git a/pkg/cloudbroker/vgpu.go b/pkg/cloudbroker/vgpu.go new file mode 100644 index 0000000..b7f3424 --- /dev/null +++ b/pkg/cloudbroker/vgpu.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/vgpu" + +// Accessing the VGPU method group +func (cb *CloudBroker) VGPU() *vgpu.VGPU { + return vgpu.New(cb.client) +} diff --git a/pkg/cloudbroker/vgpu/allocate.go b/pkg/cloudbroker/vgpu/allocate.go new file mode 100644 index 0000000..f91a01a --- /dev/null +++ b/pkg/cloudbroker/vgpu/allocate.go @@ -0,0 +1,38 @@ +package vgpu + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AllocateRequest struct for allocating VGPU +type AllocateRequest struct { + // Virtual GPU ID + // Required: true + VGPUID uint64 `url:"vgpuId" json:"vgpuId" validate:"required"` +} + +// Allocate allocates GPU +func (v VGPU) Allocate(ctx context.Context, req AllocateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vgpu/allocate" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vgpu/create.go b/pkg/cloudbroker/vgpu/create.go new file mode 100644 index 0000000..58b554c --- /dev/null +++ b/pkg/cloudbroker/vgpu/create.go @@ -0,0 +1,50 @@ +package vgpu + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateRequest struct for creating VGPU +type CreateRequest struct { + // ID of pGPU + // Required: true + PGPUID uint64 `url:"pgpuId" json:"pgpuId" validate:"required"` + + // ID of the target resource group. + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Virtual profile id + // Required: false + ProfileID uint64 `url:"profileId,omitempty" json:"profileId,omitempty"` + + // Allocate vgpu after creation + // Required: false + Allocate bool `url:"allocate,omitempty" json:"allocate,omitempty"` +} + +// Create creates VGPU +func (v VGPU) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vgpu/create" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vgpu/deallocate.go b/pkg/cloudbroker/vgpu/deallocate.go new file mode 100644 index 0000000..4a8fe20 --- /dev/null +++ b/pkg/cloudbroker/vgpu/deallocate.go @@ -0,0 +1,42 @@ +package vgpu + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeallocateRequest struct for deallocating VGPU +type DeallocateRequest struct { + // Virtual GPU ID + // Required: true + VGPUID uint64 `url:"vgpuId" json:"vgpuId" validate:"required"` + + // Force delete (detach from compute) + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Deallocate releases GPU resources +func (v VGPU) Deallocate(ctx context.Context, req DeallocateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vgpu/deallocate" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vgpu/destroy.go b/pkg/cloudbroker/vgpu/destroy.go new file mode 100644 index 0000000..72ee68e --- /dev/null +++ b/pkg/cloudbroker/vgpu/destroy.go @@ -0,0 +1,42 @@ +package vgpu + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DestroyRequest struct for destroying VGPU +type DestroyRequest struct { + // Virtual GPU ID + // Required: true + VGPUID uint64 `url:"vgpuId" json:"vgpuId" validate:"required"` + + // Force delete (deallocate and detach from compute) + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Destroy destroys VGPU +func (v VGPU) Destroy(ctx context.Context, req DestroyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vgpu/destroy" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vgpu/ids.go b/pkg/cloudbroker/vgpu/ids.go new file mode 100644 index 0000000..810cde6 --- /dev/null +++ b/pkg/cloudbroker/vgpu/ids.go @@ -0,0 +1,10 @@ +package vgpu + +// IDs gets array of VGPUIDs from ListVGPU struct +func (lvg ListVGPU) IDs() []uint64 { + res := make([]uint64, 0, len(lvg.Data)) + for _, s := range lvg.Data { + res = append(res, s.ID) + } + return res +} diff --git a/pkg/cloudbroker/vgpu/list.go b/pkg/cloudbroker/vgpu/list.go new file mode 100644 index 0000000..c4cbca2 --- /dev/null +++ b/pkg/cloudbroker/vgpu/list.go @@ -0,0 +1,88 @@ +package vgpu + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of VGPU +type ListRequest struct { + // Find by id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by vgpu status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Find by vgpu type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` + + // Find by vgpu mode + // Required: false + Mode string `url:"mode,omitempty" json:"mode,omitempty"` + + // Find by id resgroup + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by account id + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by compute id + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` + + // Find by pgpu id + // Required: false + PGPUID uint64 `url:"pgpuId,omitempty" json:"pgpuId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of all VGPU as a ListVGPU struct +func (v VGPU) List(ctx context.Context, req ListRequest) (*ListVGPU, error) { + + res, err := v.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListVGPU{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all VGPU as an array of bytes +func (v VGPU) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vgpu/list" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/vgpu/models.go b/pkg/cloudbroker/vgpu/models.go new file mode 100644 index 0000000..c7fa695 --- /dev/null +++ b/pkg/cloudbroker/vgpu/models.go @@ -0,0 +1,72 @@ +package vgpu + +type ItemVGPU struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + //Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // VGPU ID + ID uint64 `json:"id"` + + // Last claimed by + LastClaimedBy uint64 `json:"lastClaimedBy"` + + // Last update time + LastUpdateTime uint64 `json:"lastUpdateTime"` + + // Mode + Mode string `json:"mode"` + + // PCI Slot + PCISlot interface{} `json:"pciSlot"` + + // PGPUID + PGPUID uint64 `json:"pgpuid"` + + // Profile ID + ProfileID interface{} `json:"profileId"` + + // RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID interface{} `json:"referenceId"` + + // RGID + RGID uint64 `json:"rgId"` + + // Status + Status string `json:"status"` + + // Type + Type string `json:"type"` + + // VMID + VMID uint64 `json:"vmid"` +} + +// List of VGPU +type ListVGPU struct { + // Data + Data []ItemVGPU `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/vgpu/serialize.go b/pkg/cloudbroker/vgpu/serialize.go new file mode 100644 index 0000000..ccb101f --- /dev/null +++ b/pkg/cloudbroker/vgpu/serialize.go @@ -0,0 +1,43 @@ +package vgpu + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (l ListVGPU) Serialize(params ...string) (serialization.Serialized, error) { + if len(l.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(l, prefix, indent) + } + + return json.Marshal(l) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (i ItemVGPU) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(i, prefix, indent) + } + + return json.Marshal(i) +} diff --git a/pkg/cloudbroker/vgpu/vgpu.go b/pkg/cloudbroker/vgpu/vgpu.go new file mode 100644 index 0000000..db27c12 --- /dev/null +++ b/pkg/cloudbroker/vgpu/vgpu.go @@ -0,0 +1,15 @@ +package vgpu + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" + +// Structure for creating request to VGPU +type VGPU struct { + client interfaces.Caller +} + +// Builder for VGPU endpoints +func New(client interfaces.Caller) *VGPU { + return &VGPU{ + client: client, + } +} diff --git a/pkg/cloudbroker/vins.go b/pkg/cloudbroker/vins.go new file mode 100644 index 0000000..719533d --- /dev/null +++ b/pkg/cloudbroker/vins.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker/vins" +) + +// Accessing the VINS method group +func (cb *CloudBroker) VINS() *vins.VINS { + return vins.New(cb.client) +} diff --git a/pkg/cloudbroker/vins/audits.go b/pkg/cloudbroker/vins/audits.go new file mode 100644 index 0000000..7638fe2 --- /dev/null +++ b/pkg/cloudbroker/vins/audits.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// AuditsRequest struct to get audits +type AuditsRequest struct { + // ID of the VINS + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Audits gets audit records for the specified VINS object +func (v VINS) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/audits" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/vins/create_in_account.go b/pkg/cloudbroker/vins/create_in_account.go new file mode 100644 index 0000000..8c241e5 --- /dev/null +++ b/pkg/cloudbroker/vins/create_in_account.go @@ -0,0 +1,105 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +type Route struct { + // Destination network + Destination string `url:"destination" json:"destination" validate:"required"` + + //Destination network mask in 255.255.255.255 format + Netmask string `url:"netmask" json:"netmask" validate:"required"` + + //Next hop host, IP address from ViNS ID free IP pool + Gateway string `url:"gateway" json:"gateway" validate:"required"` +} + +// CreateInAccountRequest struct to create VINS in account +type CreateInAccountRequest struct { + // VINS name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Grid ID + // Required: false + GID uint64 `url:"gid,omitempty" json:"gid,omitempty"` + + // Private network IP CIDR + // Required: false + IPCIDR string `url:"ipcidr,omitempty" json:"ipcidr,omitempty"` + + // Description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Number of pre created reservations + // Required: false + PreReservationsNum uint64 `url:"preReservationsNum,omitempty" json:"preReservationsNum,omitempty"` + + // List of DNS ip address + // Required: false + DNSList []string `url:"dnsList" json:"dnsList,omitempty"` + + // List of static routes, each item must have destination, netmask, and gateway fields + // Required: false + Routes []Route `url:"-" json:"routes,omitempty" validate:"omitempty,dive"` +} + +type wrapperCreateRequestInAcc struct { + CreateInAccountRequest + Routes []string `url:"routes,omitempty"` +} + +// CreateInAccount creates VINS in account level +func (v VINS) CreateInAccount(ctx context.Context, req CreateInAccountRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + var routes []string + + if len(req.Routes) != 0 { + routes = make([]string, 0, len(req.Routes)) + + for r := range req.Routes { + b, err := json.Marshal(req.Routes[r]) + if err != nil { + return 0, err + } + + routes = append(routes, string(b)) + } + } else { + routes = []string{"[]"} + } + + reqWrapped := wrapperCreateRequestInAcc{ + CreateInAccountRequest: req, + Routes: routes, + } + + url := "/cloudbroker/vins/createInAccount" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/create_in_rg.go b/pkg/cloudbroker/vins/create_in_rg.go new file mode 100644 index 0000000..2eab2fe --- /dev/null +++ b/pkg/cloudbroker/vins/create_in_rg.go @@ -0,0 +1,98 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// CreateInRGRequest struct to create VINS in resource group +type CreateInRGRequest struct { + // VINS name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Resource group ID + // Required: true + RGID uint64 `url:"rgId" json:"rgId" validate:"required"` + + // Private network IP CIDR + // Required: false + IPCIDR string `url:"ipcidr,omitempty" json:"ipcidr,omitempty"` + + // External network ID + // Required: false + // -1 - not connect to extnet, 0 - auto select, 1+ - extnet ID + ExtNetID int64 `url:"extNetId" json:"extNetId"` + + // External IP, related only for extNetId >= 0 + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Number of pre created reservations + // Required: false + PreReservationsNum uint64 `url:"preReservationsNum,omitempty" json:"preReservationsNum,omitempty"` + + // List of DNS ip address + // Required: false + DNSList []string `url:"dnsList" json:"dnsList,omitempty"` + + // List of static routes, each item must have destination, netmask, and gateway fields + // Required: false + Routes []Route `url:"-" json:"routes,omitempty" validate:"omitempty,dive"` +} + +type wrapperCreateRequestInRG struct { + CreateInRGRequest + Routes []string `url:"routes,omitempty"` +} + +// CreateInRG creates VINS in resource group level +func (v VINS) CreateInRG(ctx context.Context, req CreateInRGRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + var routes []string + + if len(req.Routes) != 0 { + routes = make([]string, 0, len(req.Routes)) + + for r := range req.Routes { + b, err := json.Marshal(req.Routes[r]) + if err != nil { + return 0, err + } + + routes = append(routes, string(b)) + } + } else { + routes = []string{"[]"} + } + + reqWrapped := wrapperCreateRequestInRG{ + CreateInRGRequest: req, + Routes: routes, + } + + url := "/cloudbroker/vins/createInRG" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/default_qos_update.go b/pkg/cloudbroker/vins/default_qos_update.go new file mode 100644 index 0000000..5dfe7ea --- /dev/null +++ b/pkg/cloudbroker/vins/default_qos_update.go @@ -0,0 +1,50 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DefaultQOSUpdateRequest struct to update QOS +type DefaultQOSUpdateRequest struct { + // ID of VINS + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Internal traffic, kbit + // Required: false + IngressRate uint64 `url:"ingress_rate,omitempty" json:"ingress_rate,omitempty"` + + // Internal traffic burst, kbit + // Required: false + IngressBirst uint64 `url:"ingress_burst,omitempty" json:"ingress_burst,omitempty"` + + // External traffic rate, kbit + // Required: false + EgressRate uint64 `url:"egress_rate,omitempty" json:"egress_rate,omitempty"` +} + +// DefaultQOSUpdate updates default QOS values +func (v VINS) DefaultQOSUpdate(ctx context.Context, req DefaultQOSUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/defaultQosUpdate" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/delete.go b/pkg/cloudbroker/vins/delete.go new file mode 100644 index 0000000..01a3369 --- /dev/null +++ b/pkg/cloudbroker/vins/delete.go @@ -0,0 +1,50 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DeleteRequest struct to delete VINS +type DeleteRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Set to True if you want force delete non-empty VINS. + // Primarily, VINS is considered non-empty if it has virtual machines connected to it, + // and force flag will detach them from the VINS being deleted. + // Otherwise method will return an error + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` + + // Set to True if you want to destroy VINS and all linked resources, if any, immediately. + // Otherwise, they will be placed into recycle bin and could be restored later within the recycle bin's purge period + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// Delete deletes VINS +func (v VINS) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/delete" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/disable.go b/pkg/cloudbroker/vins/disable.go new file mode 100644 index 0000000..aa7a9d6 --- /dev/null +++ b/pkg/cloudbroker/vins/disable.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DisableRequest struct to disable VINS +type DisableRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Disable disables VINS by ID +func (v VINS) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/disable" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/dns_apply.go b/pkg/cloudbroker/vins/dns_apply.go new file mode 100644 index 0000000..eb422fd --- /dev/null +++ b/pkg/cloudbroker/vins/dns_apply.go @@ -0,0 +1,43 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// DNSApplyRequest struct to apply new DNS list in VINS +type DNSApplyRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // List of DNS ip address + // Required: false + DNSList []string `url:"dnsList" json:"dnsList"` +} + +// DNSApply applies new DNS list in VINS +func (v VINS) DNSApply(ctx context.Context, req DNSApplyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/dnsApply" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil + +} diff --git a/pkg/cloudbroker/vins/enable.go b/pkg/cloudbroker/vins/enable.go new file mode 100644 index 0000000..2f63453 --- /dev/null +++ b/pkg/cloudbroker/vins/enable.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// EnableRequest struct to enable VINS +type EnableRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Enable enables VINS by ID +func (v VINS) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/enable" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/extnet_connect.go b/pkg/cloudbroker/vins/extnet_connect.go new file mode 100644 index 0000000..9914dd5 --- /dev/null +++ b/pkg/cloudbroker/vins/extnet_connect.go @@ -0,0 +1,46 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ExtNetConnectRequest struct to connect external network +type ExtNetConnectRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // External network ID + // Required: false + NetID uint64 `url:"netId,omitempty" json:"netId,omitempty"` + + // Directly set IP address + // Required: false + IP string `url:"ip,omitempty" json:"ip,omitempty"` +} + +// ExtNetConnect connects VINS to external network +func (v VINS) ExtNetConnect(ctx context.Context, req ExtNetConnectRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/extNetConnect" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/extnet_disconnect.go b/pkg/cloudbroker/vins/extnet_disconnect.go new file mode 100644 index 0000000..2f84834 --- /dev/null +++ b/pkg/cloudbroker/vins/extnet_disconnect.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ExtNetDisconnectRequest struct to disconnect VINS from external network +type ExtNetDisconnectRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// ExtNetDisconnect disconnects VINS from external network +func (v VINS) ExtNetDisconnect(ctx context.Context, req ExtNetDisconnectRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/extNetDisconnect" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/extnet_list.go b/pkg/cloudbroker/vins/extnet_list.go new file mode 100644 index 0000000..f84857b --- /dev/null +++ b/pkg/cloudbroker/vins/extnet_list.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ExtNetListRequest struct to get list of VINS external network connections +type ExtNetListRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// ExtNetList shows list of VINS external network connections +func (v VINS) ExtNetList(ctx context.Context, req ExtNetListRequest) (*ListExtNets, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/extNetList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListExtNets{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/vins/filter.go b/pkg/cloudbroker/vins/filter.go new file mode 100644 index 0000000..d635887 --- /dev/null +++ b/pkg/cloudbroker/vins/filter.go @@ -0,0 +1,80 @@ +package vins + +// FilterByID returns ListVINS with specified ID. +func (lv ListVINS) FilterByID(id uint64) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.ID == id + } + + return lv.FilterFunc(predicate) +} + +// FilterByName returns ListVINS with specified Name. +func (lv ListVINS) FilterByName(name string) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.Name == name + } + + return lv.FilterFunc(predicate) +} + +// FilterByAccountID returns ListVINS with specified AccountID. +func (lv ListVINS) FilterByAccountID(accountID uint64) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.AccountID == accountID + } + + return lv.FilterFunc(predicate) +} + +// FilterByCreatedBy returns ListVINS created by specified user. +func (lv ListVINS) FilterByCreatedBy(createdBy string) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.CreatedBy == createdBy + } + + return lv.FilterFunc(predicate) +} + +// FilterByUpdatedBy returns ListVINS updated by specified user. +func (lv ListVINS) FilterByUpdatedBy(updatedBy string) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.UpdatedBy == updatedBy + } + + return lv.FilterFunc(predicate) +} + +// FilterByDeletedBy returns ListVINS deleted by specified user. +func (lv ListVINS) FilterByDeletedBy(deletedBy string) ListVINS { + predicate := func(iv ItemVINS) bool { + return iv.DeletedBy == deletedBy + } + + return lv.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListVINS based on a user-specified predicate. +func (lv ListVINS) FilterFunc(predicate func(ItemVINS) bool) ListVINS { + var result ListVINS + + for _, item := range lv.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemVINS +// If none was found, returns an empty struct. +func (lv ListVINS) FindOne() ItemVINS { + if len(lv.Data) == 0 { + return ItemVINS{} + } + + return lv.Data[0] +} diff --git a/pkg/cloudbroker/vins/filter_test.go b/pkg/cloudbroker/vins/filter_test.go new file mode 100644 index 0000000..65443e1 --- /dev/null +++ b/pkg/cloudbroker/vins/filter_test.go @@ -0,0 +1,204 @@ +package vins + +import "testing" + +var vinsItems = ListVINS{ + Data: []ItemVINS{ + { + AccountID: 1, + AccountName: "std", + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1676898844, + DefaultGW: "", + DefaultQOS: QOS{ + ERate: 0, + GUID: "", + InBurst: 0, + InRate: 0, + }, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExternalIP: "", + GID: 212, + GUID: 1, + ID: 1, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + Milestones: 363485, + Name: "vins01", + NetMask: 24, + Network: "192.168.1.0/24", + PreReservationsNum: 32, + PriVNFDevID: 29557, + Redundant: false, + RGID: 7971, + RGName: "rg_01", + SecVNFDevID: 0, + Status: "ENABLED", + UpdatedBy: "", + UpdatedTime: 0, + UserManaged: true, + VNFs: ItemVNFs{ + DHCP: 51997, + DNS: 0, + FW: 0, + GW: 0, + NAT: 0, + VPN: 0, + }, + VXLANID: 3544, + }, + { + AccountID: 2, + AccountName: "std2", + CreatedBy: "sample_user_1@decs3o", + CreatedTime: 1676898948, + DefaultGW: "", + DefaultQOS: QOS{ + ERate: 0, + GUID: "", + InBurst: 0, + InRate: 0, + }, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExternalIP: "", + GID: 212, + GUID: 2, + ID: 2, + LockStatus: "LOCKED", + ManagerID: 0, + ManagerType: "", + Milestones: 363508, + Name: "vins02", + NetMask: 24, + Network: "192.168.2.0/24", + PreReservationsNum: 32, + PriVNFDevID: 29558, + Redundant: false, + RGID: 7972, + RGName: "rg_02", + SecVNFDevID: 0, + Status: "ENABLED", + UpdatedBy: "", + UpdatedTime: 0, + UserManaged: true, + VNFs: ItemVNFs{ + DHCP: 51998, + DNS: 0, + FW: 0, + GW: 0, + NAT: 0, + VPN: 0, + }, + VXLANID: 3545, + }, + { + AccountID: 3, + AccountName: "std3", + CreatedBy: "sample_user_2@decs3o", + CreatedTime: 1676899026, + DefaultGW: "", + DefaultQOS: QOS{ + ERate: 0, + GUID: "", + InBurst: 0, + InRate: 0, + }, + DeletedBy: "", + DeletedTime: 0, + Description: "", + ExternalIP: "", + GID: 212, + GUID: 3, + ID: 3, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + Milestones: 363549, + Name: "vins03", + NetMask: 24, + Network: "192.168.3.0/24", + PreReservationsNum: 32, + PriVNFDevID: 29559, + Redundant: false, + RGID: 7973, + RGName: "rg_03", + SecVNFDevID: 0, + Status: "DISABLED", + UpdatedBy: "", + UpdatedTime: 0, + UserManaged: true, + VNFs: ItemVNFs{ + DHCP: 51999, + DNS: 0, + FW: 0, + GW: 0, + NAT: 0, + VPN: 0, + }, + VXLANID: 3546, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := vinsItems.FilterByID(2).FindOne() + + if actual.ID != 2 { + t.Fatal("expected ID 2, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := vinsItems.FilterByName("vins01").FindOne() + + if actual.Name != "vins01" { + t.Fatal("expected Name 'vins01', found: ", actual.Name) + } +} + +func TestFilterByAccountID(t *testing.T) { + actual := vinsItems.FilterByAccountID(3).FindOne() + + if actual.AccountID != 3 { + t.Fatal("expected AccountID 3, found: ", actual.AccountID) + } +} + +func TestFilterByCreatedBy(t *testing.T) { + actual := vinsItems.FilterByCreatedBy("sample_user_1@decs3o") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedBy != "sample_user_1@decs3o" { + t.Fatal("expected CreatedBy 'sample_user_1@decs3o', found: ", item.CreatedBy) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := vinsItems.FilterFunc(func(iv ItemVINS) bool { + return iv.RGID == 7971 + }). + FindOne() + + if actual.RGID != 7971 { + t.Fatal("expected RGID 7971, found: ", actual.RGID) + } +} + +func TestSortByCreatedTime(t *testing.T) { + actual := vinsItems.SortByCreatedTime(false) + + if actual.Data[0].CreatedTime != 1676898844 || actual.Data[2].CreatedTime != 1676899026 { + t.Fatal("expected ascending order, found descending") + } +} diff --git a/pkg/cloudbroker/vins/get.go b/pkg/cloudbroker/vins/get.go new file mode 100644 index 0000000..fbc6579 --- /dev/null +++ b/pkg/cloudbroker/vins/get.go @@ -0,0 +1,46 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// GetRequest struct to get information about VINS +type GetRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Get gets information about VINS by ID as a RecordVINS struct +func (v VINS) Get(ctx context.Context, req GetRequest) (*RecordVINS, error) { + res, err := v.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordVINS{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about VINS by ID as an array of bytes +func (v VINS) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/get" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/vins/ids.go b/pkg/cloudbroker/vins/ids.go new file mode 100644 index 0000000..8e516b4 --- /dev/null +++ b/pkg/cloudbroker/vins/ids.go @@ -0,0 +1,55 @@ +package vins + +// IDs gets array of VINSIDs from ListVINS struct +func (lv ListVINS) IDs() []uint64 { + res := make([]uint64, 0, len(lv.Data)) + for _, v := range lv.Data { + res = append(res, v.ID) + } + return res +} + +// IDs gets array of ExtNetIDs from ListExtNets struct +func (le ListExtNets) IDs() []uint64 { + res := make([]uint64, 0, len(le.Data)) + for _, e := range le.Data { + res = append(res, e.ExtNetID) + } + return res +} + +// IDs gets array of NATRuleIDs from ListNATRules struct +func (lnr ListNATRules) IDs() []uint64 { + res := make([]uint64, 0, len(lnr.Data)) + for _, nrc := range lnr.Data { + res = append(res, nrc.ID) + } + return res +} + +// IDs gets array of StaticRouteIDs from ListStaticRoutes struct +func (lsr ListStaticRoutes) IDs() []uint64 { + res := make([]uint64, 0, len(lsr.Data)) + for _, sr := range lsr.Data { + res = append(res, sr.ID) + } + return res +} + +// IDs gets array of RouteIDs from ListRoutes struct +func (lr ListRoutes) IDs() []uint64 { + res := make([]uint64, 0, len(lr)) + for _, r := range lr { + res = append(res, r.ID) + } + return res +} + +// IDs gets array of NATRuleConfigIDs from ListNATRule struct +func (lnrc ListNATRule) IDs() []uint64 { + res := make([]uint64, 0, len(lnrc)) + for _, nrc := range lnrc { + res = append(res, nrc.ID) + } + return res +} \ No newline at end of file diff --git a/pkg/cloudbroker/vins/ip_list.go b/pkg/cloudbroker/vins/ip_list.go new file mode 100644 index 0000000..4123ce2 --- /dev/null +++ b/pkg/cloudbroker/vins/ip_list.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPListRequest struct for DHCP IP +type IPListRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// IPList shows DHCP IP reservations on VINS +func (v VINS) IPList(ctx context.Context, req IPListRequest) (*ListIPs, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/ipList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListIPs{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/vins/ip_release.go b/pkg/cloudbroker/vins/ip_release.go new file mode 100644 index 0000000..b7c11bf --- /dev/null +++ b/pkg/cloudbroker/vins/ip_release.go @@ -0,0 +1,47 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPReleaseRequest struct for IP release +type IPReleaseRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // IP address + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // MAC address + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` +} + +// IPRelease delete IP reservation matched by specified IP & MAC address combination. +// If both IP and MAC address are empty strings, all IP reservations will be deleted. +func (v VINS) IPRelease(ctx context.Context, req IPReleaseRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/ipRelease" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/ip_reserve.go b/pkg/cloudbroker/vins/ip_reserve.go new file mode 100644 index 0000000..595ed9c --- /dev/null +++ b/pkg/cloudbroker/vins/ip_reserve.go @@ -0,0 +1,60 @@ +package vins + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// IPReserveRequest struct for IP reserve +type IPReserveRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Type of the reservation + // Should be one of: + // - DHCP + // - VIP + // - EXCLUDE + // Required: true + Type string `url:"type" json:"type" validate:"vinsType"` + + // IP address to use. Non-empty string is required for type "EXCLUDE". + // Ignored for types "DHCP" and "VIP". + // Required: false + IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` + + // MAC address to associate with IP reservation. + // Ignored for type "EXCLUDE", + // non-empty string is required for "DHCP" and "VIP" + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` + + // ID of the compute, associated with this reservation of type "DHCP". + // Ignored for other types + // Required: false + ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` +} + +// IPReserve creates reservation on ViNS DHCP +func (v VINS) IPReserve(ctx context.Context, req IPReserveRequest) (string, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/ipReserve" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} diff --git a/pkg/cloudbroker/vins/list.go b/pkg/cloudbroker/vins/list.go new file mode 100644 index 0000000..8531776 --- /dev/null +++ b/pkg/cloudbroker/vins/list.go @@ -0,0 +1,84 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListRequest struct to get list of VINSes +type ListRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by external network IP + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Find by VNF Device id + // Required: false + VNFDevID uint64 `url:"vnfdevId,omitempty" json:"vnfdevId,omitempty"` + + // Include deleted + // Required: false + IncludeDeleted bool `url:"includeDeleted,omitempty" json:"includeDeleted,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// List gets list of VINSes as a ListVINS struct +func (v VINS) List(ctx context.Context, req ListRequest) (*ListVINS, error) { + + res, err := v.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of VINSes as an array of bytes +func (v VINS) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/list" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/vins/list_deleted.go b/pkg/cloudbroker/vins/list_deleted.go new file mode 100644 index 0000000..9729b28 --- /dev/null +++ b/pkg/cloudbroker/vins/list_deleted.go @@ -0,0 +1,73 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// ListDeletedRequest struct to get list of deleted VINSes +type ListDeletedRequest struct { + // Find by ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Find by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Find by account ID + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Find by external network IP + // Required: false + ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` + + // Find by VNF Device id + // Required: false + VNFDevId uint64 `url:"vnfdevId,omitempty" json:"vnfdevId,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// ListDeleted gets list of deleted VINSes +func (v VINS) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListVINS, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/listDeleted" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/vins/mass_delete.go b/pkg/cloudbroker/vins/mass_delete.go new file mode 100644 index 0000000..3c024f7 --- /dev/null +++ b/pkg/cloudbroker/vins/mass_delete.go @@ -0,0 +1,45 @@ +package vins + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassDeleteRequest struct to delete several VINSes +type MassDeleteRequest struct { + // VINS IDs + // Required: true + VINSIDs []uint64 `url:"vinsIds" json:"vinsIds" validate:"min=1"` + + // Set to true if you want force delete non-empty VINS. Primarily, + // VINS is considered non-empty if it has VMs connected to it, + // and force flag will detach them from the VINS being deleted. + // Otherwise method will return an error + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` + + // Set to true if you want to destroy VINS and all linked resources, if any, immediately. + // Otherwise, they will be placed into recycle bin and could be restored later + // within the recycle bins purge period + // Required: false + Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` +} + +// MassDelete start jobs to delete several VINSes +func (v VINS) MassDelete(ctx context.Context, req MassDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/massDelete" + + _, err = v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/vins/mass_disable.go b/pkg/cloudbroker/vins/mass_disable.go new file mode 100644 index 0000000..31be4c7 --- /dev/null +++ b/pkg/cloudbroker/vins/mass_disable.go @@ -0,0 +1,32 @@ +package vins + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassDisableRequest struct to disable several VINSes +type MassDisableRequest struct { + // VINS IDs + // Required: true + VINSIDs []uint64 `url:"vinsIds" json:"vinsIds" validate:"min=1"` +} + +// MassDisable start jobs to disable several VINSes +func (v VINS) MassDisable(ctx context.Context, req MassDisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/massDisable" + + _, err = v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/vins/mass_enable.go b/pkg/cloudbroker/vins/mass_enable.go new file mode 100644 index 0000000..27aaf5a --- /dev/null +++ b/pkg/cloudbroker/vins/mass_enable.go @@ -0,0 +1,32 @@ +package vins + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// MassEnableRequest struct to enable several VINSes +type MassEnableRequest struct { + // VINS IDs + // Required: true + VINSIDs []uint64 `url:"vinsIds" json:"vinsIds" validate:"min=1"` +} + +// MassEnable start jobs to enable several VINSes +func (v VINS) MassEnable(ctx context.Context, req MassEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/massEnable" + + _, err = v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/cloudbroker/vins/models.go b/pkg/cloudbroker/vins/models.go new file mode 100644 index 0000000..3654170 --- /dev/null +++ b/pkg/cloudbroker/vins/models.go @@ -0,0 +1,806 @@ +package vins + +// Main information about audit +type ItemAudit struct { + // Call + Call string `json:"call"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // User + User string `json:"user"` +} + +// List of audits +type ListAudits []ItemAudit + +// Main information about external network +type ItemExtNet struct { + // Default GW + DefaultGW string `json:"default_gw"` + + // External network ID + ExtNetID uint64 `json:"ext_net_id"` + + // IP + IP string `json:"ip"` + + // Prefix len + PrefixLen uint64 `json:"prefixlen"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` +} + +// List of external networks +type ListExtNets struct { + // Data + Data []ItemExtNet `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// MGMT +type MGMT struct { + // IP address + IPAddress string `json:"ipaddr"` + + // Password + Password string `json:"password"` + + // SSH key + SSHKey string `json:"sshkey"` + + // User + User string `json:"user"` +} + +// Main information about resource +type Resources struct { + // Number of CPU + CPU uint64 `json:"cpu"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Stack ID + StackID uint64 `json:"stackId"` + + // UUID + UUID string `json:"uuid"` +} + +// VNF config +type Config struct { + // MGMT + MGMT MGMT `json:"mgmt"` + + // Resources + Resources Resources `json:"resources"` +} + +// Main information about QOS +type QOS struct { + // ERate + ERate uint64 `json:"eRate"` + + // GUID + GUID string `json:"guid"` + + // InBurst + InBurst uint64 `json:"inBurst"` + + // InRate + InRate uint64 `json:"inRate"` +} + +// Main information about interface +type ItemInterface struct { + // Bus number + BusNumber uint64 `json:"bus_number"` + + // Connection ID + ConnID uint64 `json:"connId"` + + // Connection type + ConnType string `json:"connType"` + + // Default GW + DefGW string `json:"defGw"` + + // Enabled + Enabled bool `json:"enabled"` + + // FLIPGroup ID + FLIPGroupID uint64 `json:"flipgroupId"` + + // GUID + GUID string `json:"guid"` + + // IP address + IPAddress string `json:"ipAddress"` + + // Listen SSH + ListenSSH bool `json:"listenSsh"` + + // MAC + MAC string `json:"mac"` + + // Maximum transmission unit + MTU uint64 `json:"mtu"` + + // Libvirt Settings + LibvirtSettings LibvirtSettings `json:"libvirtSettings"` + + // Name + Name string `json:"name"` + + // Network type + NetID uint64 `json:"netId"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network type + NetType string `json:"netType"` + + // NodeID + NodeID int64 `json:"nodeId"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // QOS + QOS QOS `json:"qos"` + + // Target + Target string `json:"target"` + + // Type + Type string `json:"type"` + + // List of VNF IDs + VNFs []uint64 `json:"vnfs"` +} + +// List of interfaces +type ListInterfaces []ItemInterface + +// Main information about VNF device +type VNFDev struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Capabilities + Capabilities []string `json:"capabilities"` + + // Config + Config Config `json:"config"` + + // Config saved + ConfigSaved bool `json:"configSaved"` + + // CustomPreConfig + CustomPreConfig bool `json:"customPrecfg"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // List of interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // TechStatus + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` + + //List of VINS IDs + VINS []uint64 `json:"vins"` +} + +// Main information about reservation +type ItemReservation struct { + // Client type + ClientType string `json:"clientType"` + + // Description + Description string `json:"desc"` + + // Domain name + DomainName string `json:"domainname"` + + // Hostname + Hostname string `json:"hostname"` + + // IP + IP string `json:"ip"` + + // MAC + MAC string `json:"mac"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmId"` +} + +// List of reservations +type ListReservations []ItemReservation + +// VNFs sonfig +type VNFsConfig struct { + // Default GW + DefaultGW string `json:"default_gw"` + + // List of DNS + DNS []string `json:"dns"` + + // IP end + IPEnd string `json:"ip_end"` + + // IP start + IPStart string `json:"ip_start"` + + // Lease + Lease uint64 `json:"lease"` + + // Network mask + NetMask uint64 `json:"netmask"` + + // Network + Network string `json:"network"` + + // List of reservations + Reservations ListReservations `json:"reservations"` +} + +// Primary +type Primary struct { + // Device ID + DevID uint64 `json:"devId"` + + // IFace01 + IFace01 string `json:"iface01"` + + // IFace02 + IFace02 string `json:"iface02"` +} + +// Devices +type Devices struct { + // Primary + Primary Primary `json:"primary"` +} + +// Main information about DHCP +type RecordDHCP struct { + // Config + Config VNFsConfig `json:"config"` + + // DHCP details + InfoVNF +} + +// GW config +type GWConfig struct { + // Default GW + DefaultGW string `json:"default_gw"` + + // External network ID + ExtNetID uint64 `json:"ext_net_id"` + + // External network IP + ExtNetIP string `json:"ext_net_ip"` + + // External network mask + ExtNetMask uint64 `json:"ext_netmask"` + + // QOS + QOS QOS `json:"qos"` +} + +// Main information about GW +type RecordGW struct { + // Config + Config GWConfig `json:"config"` + + // GW details + InfoVNF +} + +// List NATRules +type ListNATRule []ItemNATRule + +// NAT config +type NATConfig struct { + // Network mask + NetMask uint64 `json:"netmask"` + + // Network + Network string `json:"network"` + + // Rules + Rules ListNATRule `json:"rules"` +} + +// Main information about NAT +type RecordNAT struct { + // Config + Config NATConfig `json:"config"` + + // NAT details + InfoVNF +} + +// DHCP/GW/NAT details +type InfoVNF struct { + // CKey + CKey string `json:"_ckey"` + + // Meta + Meta []interface{} `json:"_meta"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // CreatedTime + CreatedTime uint64 `json:"createdTime"` + + // Devices + Devices Devices `json:"devices"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Owner ID + OwnerID uint64 `json:"ownerId"` + + // Owner type + OwnerType string `json:"ownerType"` + + // Pure virtual + PureVirtual bool `json:"pureVirtual"` + + // Routes + Routes ListRoutes `json:"routes"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Type + Type string `json:"type"` +} + +// List of static routes +type ListStaticRoutes struct { + // Data + Data []ItemRoutes `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// List of Routes +type ListRoutes []ItemRoutes + +// Detailed information about Routes +type ItemRoutes struct { + //Compute Id + ComputeIds []uint64 `json:"computeIds"` + + // Destination network + Destination string `json:"destination"` + + //Next hop host, IP address from ViNS ID free IP pool + Gateway string `json:"gateway"` + + // GUID + GUID string `json:"guid"` + + // ID + ID uint64 `json:"id"` + + //Destination network mask in 255.255.255.255 format + Netmask string `json:"netmask"` +} + +// main information about VNF +type RecordVNFs struct { + // DHCP + DHCP RecordDHCP `json:"DHCP"` + + // GW + GW RecordGW `json:"GW"` + + // NAT + NAT RecordNAT `json:"NAT"` +} + +type Computes struct { + ID uint64 `json:"id"` + Name string `json:"name"` +} + +// Detailed information about VINS +type RecordVINS struct { + // VNF device + VNFDev VNFDev `json:"VNFDev"` + + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Computes + Computes []Computes `json:"computes"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Default QOS + DefaultQOS QOS `json:"defaultQos"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Manager ID + ManagerID uint64 `json:"managerId"` + + // Manager type + ManagerType string `json:"managerType"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network + Network string `json:"network"` + + // PreReservationsNum + PreReservationsNum uint64 `json:"preReservationsNum"` + + // Redundant + Redundant bool `json:"redundant"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // SecVNFDevID + SecVNFDevID uint64 `json:"secVnfDevId"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed + UserManaged bool `json:"userManaged"` + + // VNFs information + VNFs RecordVNFs `json:"vnfs"` + + // VXLAN ID + VXLANID uint64 `json:"vxlanId"` +} + +// Information about libvirt settings +type LibvirtSettings struct { + // TX mode + TXMode string `json:"txmode"` + + // IO event + IOEventFD string `json:"ioeventfd"` + + // Event ID + EventIDx string `json:"event_idx"` + + // Number of queues + Queues uint64 `json:"queues"` + + // RX queue size + RXQueueSize uint64 `json:"rx_queue_size"` + + // TX queue size + TXQueueSize uint64 `json:"tx_queue_size"` + + // GUID + GUID string `json:"guid"` +} + +// Main information about IP +type ItemIP struct { + // IP + IP string `json:"ip"` + + // MAC + MAC string `json:"mac"` + + // Type + Type string `json:"type"` + + // Virtual machine ID + VMID uint64 `json:"vmId"` + + // Client type + ClientType string `json:"clientType"` + + // Domain name + DomainName string `json:"domainname"` + + // Hostname + Hostname string `json:"hostname"` +} + +// List of information about IPs +type ListIPs struct { + // Data + Data []ItemIP `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about NAT rule +type ItemNATRule struct { + // ID + ID uint64 `json:"id"` + + // Local IP + LocalIP string `json:"localIp"` + + // Local port + LocalPort uint64 `json:"localPort"` + + // Protocol + Protocol string `json:"protocol"` + + // Public port end + PublicPortEnd uint64 `json:"publicPortEnd"` + + // Public port start + PublicPortStart uint64 `json:"publicPortStart"` + + // Virtual machine ID + VMID uint64 `json:"vmId"` + + // Virtual machine name + VMName string `json:"vmName"` +} + +// List NAT rules +type ListNATRules struct { + // Data + Data []ItemNATRule `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Shorted information about VNF +type ItemVNFs struct { + // DHCP + DHCP uint64 `json:"dhcp"` + + // DNS + DNS uint64 `json:"dns"` + + // FW + FW uint64 `json:"fw"` + + // GW + GW uint64 `json:"gw"` + + // NAT + NAT uint64 `json:"nat"` + + // VPN + VPN uint64 `json:"vpn"` +} + +// Main information about VINS +type ItemVINS struct { + // Account ID + AccountID uint64 `json:"accountId"` + + // Account name + AccountName string `json:"accountName"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Default GW + DefaultGW string `json:"defaultGW"` + + // Default QOS + DefaultQOS QOS `json:"defaultQos"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Description + Description string `json:"desc"` + + // External IP + ExternalIP string `json:"externalIP"` + + // Extnet ID + ExtnetId uint64 `json:"extnetId"` + + // Free IPs + FreeIPs int64 `json:"freeIPs"` + + // Grid ID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Lock status + LockStatus string `json:"lockStatus"` + + // Manager ID + ManagerID uint64 `json:"managerId"` + + // Manager type + ManagerType string `json:"managerType"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // Name + Name string `json:"name"` + + // Network mask + NetMask uint64 `json:"netMask"` + + // Network + Network string `json:"network"` + + // PreReservationsNum + PreReservationsNum uint64 `json:"preReservationsNum"` + + // PriVNFDevID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // Redundant + Redundant bool `json:"redundant"` + + // Resource group ID + RGID uint64 `json:"rgId"` + + // Resource group name + RGName string `json:"rgName"` + + // SecVNFDevID + SecVNFDevID uint64 `json:"secVnfDevId"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // User managed + UserManaged bool `json:"userManaged"` + + // VNFs + VNFs ItemVNFs `json:"vnfs"` + + // VXLAN ID + VXLANID uint64 `json:"vxlanId"` +} + +// List of VINS +type ListVINS struct { + // Data + Data []ItemVINS `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type SearchVINS []ItemVINS diff --git a/pkg/cloudbroker/vins/nat_rule_add.go b/pkg/cloudbroker/vins/nat_rule_add.go new file mode 100644 index 0000000..795ceb4 --- /dev/null +++ b/pkg/cloudbroker/vins/nat_rule_add.go @@ -0,0 +1,61 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NATRuleAddRequest struct to create NAT rules +type NATRuleAddRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Internal IP address to apply this rule to + // Required: true + IntIP string `url:"intIp" json:"intIp" validate:"required"` + + // External IP start port to use for this rule + // Required: true + ExtPortStart uint64 `url:"extPortStart" json:"extPortStart" validate:"required"` + + // Internal IP port number to use for this rule + // Required: false + IntPort uint64 `url:"intPort,omitempty" json:"intPort,omitempty"` + + // External IP end port to use for this rule + // Required: false + ExtPortEnd uint64 `url:"extPortEnd,omitempty" json:"extPortEnd,omitempty"` + + // IP protocol type + // Should be one of: + // - "tcp" + // - "udp" + // Required: false + Proto string `url:"proto,omitempty" json:"proto,omitempty" validate:"omitempty,proto"` +} + +// NATRuleAdd creates NAT (port forwarding) rule on VINS +func (v VINS) NATRuleAdd(ctx context.Context, req NATRuleAddRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/natRuleAdd" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/nat_rule_del.go b/pkg/cloudbroker/vins/nat_rule_del.go new file mode 100644 index 0000000..537c13b --- /dev/null +++ b/pkg/cloudbroker/vins/nat_rule_del.go @@ -0,0 +1,43 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NATRuleDelRequest struct to delete NAT rule +type NATRuleDelRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // ID of the rule to delete. + // Pass -1 to clear all rules at once + // Required: true + RuleID int64 `url:"ruleId" json:"ruleId" validate:"required"` +} + +// NATRuleDel deletes NAT (port forwarding) rule on VINS +func (v VINS) NATRuleDel(ctx context.Context, req NATRuleDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/natRuleDel" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/nat_rule_list.go b/pkg/cloudbroker/vins/nat_rule_list.go new file mode 100644 index 0000000..f10dfdd --- /dev/null +++ b/pkg/cloudbroker/vins/nat_rule_list.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NATRuleListRequest struct to get list of NAT rules +type NATRuleListRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// NATRuleList gets list of NAT (port forwarding) rules +func (v VINS) NATRuleList(ctx context.Context, req NATRuleListRequest) (*ListNATRules, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/natRuleList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListNATRules{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/vins/net_qos.go b/pkg/cloudbroker/vins/net_qos.go new file mode 100644 index 0000000..9ccb0e1 --- /dev/null +++ b/pkg/cloudbroker/vins/net_qos.go @@ -0,0 +1,50 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// NetQOSRequest struct to update all VINS interfaces QOS +type NetQOSRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Internal traffic, kbit + // Required: false + IngressRate uint64 `url:"ingress_rate,omitempty" json:"ingress_rate,omitempty"` + + // Internal traffic burst, kbit + // Required: false + IngressBirst uint64 `url:"ingress_burst,omitempty" json:"ingress_burst,omitempty"` + + // External traffic rate, kbit + // Required: false + EgressRate uint64 `url:"egress_rate,omitempty" json:"egress_rate,omitempty"` +} + +// NetQOS update all VINS interfaces QOS +func (v VINS) NetQOS(ctx context.Context, req NetQOSRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/netQos" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/raise_down.go b/pkg/cloudbroker/vins/raise_down.go new file mode 100644 index 0000000..416c627 --- /dev/null +++ b/pkg/cloudbroker/vins/raise_down.go @@ -0,0 +1,24 @@ +package vins + +import ( + "context" + "net/http" + "strconv" +) + +// RaiseDown starting all VINSes VNFDevs in "DOWN" tech status +func (v VINS) RaiseDown(ctx context.Context) (bool, error) { + url := "/cloudbroker/vins/raiseDown" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, nil) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/restore.go b/pkg/cloudbroker/vins/restore.go new file mode 100644 index 0000000..bc313af --- /dev/null +++ b/pkg/cloudbroker/vins/restore.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// RestoreRequest struct for restore +type RestoreRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// Restore restores VINS from recycle bin +func (v VINS) Restore(ctx context.Context, req RestoreRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/restore" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/search.go b/pkg/cloudbroker/vins/search.go new file mode 100644 index 0000000..0bd43bb --- /dev/null +++ b/pkg/cloudbroker/vins/search.go @@ -0,0 +1,45 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" +) + +// SearchRequest struct to search VINSes +type SearchRequest struct { + // ID of the account to search for the ViNSes + // Required: false + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` + + // ID of the resource group to limit search to the specified RG level only + // Required: false + RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` + + // Name of the ViNS to search for + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // If False, then VINSes having one of the statuses are not listed for + // Required: false + ShowAll bool `url:"show_all,omitempty" json:"show_all,omitempty"` +} + +// Search searches VINSes +func (v VINS) Search(ctx context.Context, req SearchRequest) (SearchVINS, error) { + url := "/cloudbroker/vins/search" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := SearchVINS{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/cloudbroker/vins/serialize.go b/pkg/cloudbroker/vins/serialize.go new file mode 100644 index 0000000..418f301 --- /dev/null +++ b/pkg/cloudbroker/vins/serialize.go @@ -0,0 +1,43 @@ +package vins + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/serialization" +) + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (lv ListVINS) Serialize(params ...string) (serialization.Serialized, error) { + if len(lv.Data) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(lv, prefix, indent) + } + + return json.Marshal(lv) +} + +// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions. +// +// In order to serialize with indent make sure to follow these guidelines: +// - First argument -> prefix +// - Second argument -> indent +func (iv ItemVINS) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(iv, prefix, indent) + } + + return json.Marshal(iv) +} diff --git a/pkg/cloudbroker/vins/sorting.go b/pkg/cloudbroker/vins/sorting.go new file mode 100644 index 0000000..80d898c --- /dev/null +++ b/pkg/cloudbroker/vins/sorting.go @@ -0,0 +1,60 @@ +package vins + +import "sort" + +// SortByCreatedTime sorts ListVINS by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lv ListVINS) SortByCreatedTime(inverse bool) ListVINS { + if len(lv.Data) < 2 { + return lv + } + + sort.Slice(lv.Data, func(i, j int) bool { + if inverse { + return lv.Data[i].CreatedTime > lv.Data[j].CreatedTime + } + + return lv.Data[i].CreatedTime < lv.Data[j].CreatedTime + }) + + return lv +} + +// SortByUpdatedTime sorts ListVINS by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lv ListVINS) SortByUpdatedTime(inverse bool) ListVINS { + if len(lv.Data) < 2 { + return lv + } + + sort.Slice(lv.Data, func(i, j int) bool { + if inverse { + return lv.Data[i].UpdatedTime > lv.Data[j].UpdatedTime + } + + return lv.Data[i].UpdatedTime < lv.Data[j].UpdatedTime + }) + + return lv +} + +// SortByDeletedTime sorts ListVINS by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lv ListVINS) SortByDeletedTime(inverse bool) ListVINS { + if len(lv.Data) < 2 { + return lv + } + + sort.Slice(lv.Data, func(i, j int) bool { + if inverse { + return lv.Data[i].DeletedTime > lv.Data[j].DeletedTime + } + + return lv.Data[i].DeletedTime < lv.Data[j].DeletedTime + }) + + return lv +} diff --git a/pkg/cloudbroker/vins/static_route_access_grant.go b/pkg/cloudbroker/vins/static_route_access_grant.go new file mode 100644 index 0000000..c5bd5db --- /dev/null +++ b/pkg/cloudbroker/vins/static_route_access_grant.go @@ -0,0 +1,46 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAccessGrantRequest struct to grant access to static route to Compute/ViNS +type StaticRouteAccessGrantRequest struct { + // ViNS ID to grant access + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Route ID to grant access, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` + + // List of Compute IDs to grant access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAccessGrant grants access to static route to Compute/ViNS +func (v VINS) StaticRouteAccessGrant(ctx context.Context, req StaticRouteAccessGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/staticRouteAccessGrant" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/static_route_access_revoke.go b/pkg/cloudbroker/vins/static_route_access_revoke.go new file mode 100644 index 0000000..46dcf80 --- /dev/null +++ b/pkg/cloudbroker/vins/static_route_access_revoke.go @@ -0,0 +1,46 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAccessRevokeRequest struct to revoke access to static route to Compute/ViNS +type StaticRouteAccessRevokeRequest struct { + // ViNS ID to revoke access + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Route ID to revoke access, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` + + // List of Compute IDs to revoke access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAccessRevoke revokes access to static route to Compute/ViNS +func (v VINS) StaticRouteAccessRevoke(ctx context.Context, req StaticRouteAccessRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/staticRouteAccessRevoke" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/static_route_add.go b/pkg/cloudbroker/vins/static_route_add.go new file mode 100644 index 0000000..acd13c9 --- /dev/null +++ b/pkg/cloudbroker/vins/static_route_add.go @@ -0,0 +1,54 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteAddRequest struct to add static route +type StaticRouteAddRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Destination network + // Required: true + Destination string `url:"destination" json:"destination" validate:"required"` + + // Destination network mask in 255.255.255.255 format + // Required: true + Netmask string `url:"netmask" json:"netmask" validate:"required"` + + // Next hop host, IP address from ViNS ID free IP pool + // Required: true + Gateway string `url:"gateway" json:"gateway" validate:"required"` + + // List of Compute IDs which have access to this route + // Required: false + ComputeIds []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"` +} + +// StaticRouteAdd adds new static route to ViNS +func (v VINS) StaticRouteAdd(ctx context.Context, req StaticRouteAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/staticRouteAdd" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/static_route_del.go b/pkg/cloudbroker/vins/static_route_del.go new file mode 100644 index 0000000..1827dab --- /dev/null +++ b/pkg/cloudbroker/vins/static_route_del.go @@ -0,0 +1,42 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteDelRequest struct to remove static route from ViNS +type StaticRouteDelRequest struct { + // ViNS ID to remove static route from + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` + + // Route ID to remove, can be found in staticRouteList + // Required: true + RouteId uint64 `url:"routeId" json:"routeId" validate:"required"` +} + +// StaticRouteDel removes static route from ViNS +func (v VINS) StaticRouteDel(ctx context.Context, req StaticRouteDelRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/staticRouteDel" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/static_route_list.go b/pkg/cloudbroker/vins/static_route_list.go new file mode 100644 index 0000000..57e71e8 --- /dev/null +++ b/pkg/cloudbroker/vins/static_route_list.go @@ -0,0 +1,40 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// StaticRouteListRequest struct for static route list +type StaticRouteListRequest struct { + // ViNS ID to show list of static routes + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// StaticRouteList shows list of static routes for ViNS +func (v VINS) StaticRouteList(ctx context.Context, req StaticRouteListRequest) (*ListStaticRoutes, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/staticRouteList" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + list := ListStaticRoutes{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/vins/vins.go b/pkg/cloudbroker/vins/vins.go new file mode 100644 index 0000000..53ade85 --- /dev/null +++ b/pkg/cloudbroker/vins/vins.go @@ -0,0 +1,18 @@ +// API Actor for managing VINS. This actor is a final API for endusers to manage VINS +package vins + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/interfaces" +) + +// Structure for creating request to VINS +type VINS struct { + client interfaces.Caller +} + +// Builder for VINS endpoints +func New(client interfaces.Caller) *VINS { + return &VINS{ + client: client, + } +} diff --git a/pkg/cloudbroker/vins/vnfdev_redeploy.go b/pkg/cloudbroker/vins/vnfdev_redeploy.go new file mode 100644 index 0000000..d593f1c --- /dev/null +++ b/pkg/cloudbroker/vins/vnfdev_redeploy.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// VNFDevRedeployRequest struct to redeploy VNF devices +type VNFDevRedeployRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// VNFDevRedeploy redeploys VINS VNFDevs +func (v VINS) VNFDevRedeploy(ctx context.Context, req VNFDevRedeployRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/vnfdevRedeploy" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/vnfdev_reset.go b/pkg/cloudbroker/vins/vnfdev_reset.go new file mode 100644 index 0000000..0bdae99 --- /dev/null +++ b/pkg/cloudbroker/vins/vnfdev_reset.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// VNFDevResetRequest struct to reset VNF device +type VNFDevResetRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// VNFDevReset resets VINSes primary VNF device +func (v VINS) VNFDevReset(ctx context.Context, req VNFDevResetRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/vnfdevReset" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/vnfdev_restart.go b/pkg/cloudbroker/vins/vnfdev_restart.go new file mode 100644 index 0000000..471398f --- /dev/null +++ b/pkg/cloudbroker/vins/vnfdev_restart.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// VNFDevRestartRequest struct to reboot VINSes primary VNF device +type VNFDevRestartRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// VNFDevRestart reboots VINSes primary VNF device +func (v VINS) VNFDevRestart(ctx context.Context, req VNFDevRestartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/vnfdevRestart" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/vnfdev_start.go b/pkg/cloudbroker/vins/vnfdev_start.go new file mode 100644 index 0000000..ad4db3e --- /dev/null +++ b/pkg/cloudbroker/vins/vnfdev_start.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// VNFDevStartRequest struct to start VNF devices +type VNFDevStartRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// VNFDevStart starts VINSes primary VNF device +func (v VINS) VNFDevStart(ctx context.Context, req VNFDevStartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/vnfdevStart" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/vins/vnfdev_stop.go b/pkg/cloudbroker/vins/vnfdev_stop.go new file mode 100644 index 0000000..2affa00 --- /dev/null +++ b/pkg/cloudbroker/vins/vnfdev_stop.go @@ -0,0 +1,38 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/internal/validators" +) + +// VNFDevStopRequest struct to stop VNF devices +type VNFDevStopRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"` +} + +// VNFDevStop stops VINSes primary VNF device +func (v VINS) VNFDevStop(ctx context.Context, req VNFDevStopRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/vnfdevStop" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/samples/config/bvs-config.json b/samples/config/bvs-config.json new file mode 100644 index 0000000..7df369b --- /dev/null +++ b/samples/config/bvs-config.json @@ -0,0 +1,21 @@ +{ + "username": "", + "password": "", + "appId": "", + "appSecret": "", + "ssoUrl": "https://bvs-delta.qa.loc:8443", + "decortUrl": "https://delta.qa.loc", + "domain": "dynamix", + "token": { + "access_token": "string_token", + "token_type": "bearer", + "refresh_token": "string_refresh_token", + "expiry": "2023-11-24T12:40:27.954150524+03:00" + }, + "retries": 5, + "sslSkipVerify": true, + "timeout": "5m", + "path_cfg": "config", + "path_token": "token", + "timeToRefresh": 5 +} diff --git a/samples/config/bvs-config.yml b/samples/config/bvs-config.yml new file mode 100644 index 0000000..a49b8d5 --- /dev/null +++ b/samples/config/bvs-config.yml @@ -0,0 +1,18 @@ +username: +password: +appId: +appSecret: +ssoUrl: https://bvs-delta.qa.loc:8443 +decortUrl: https://delta.qa.loc +domain: dynamix +token": +access_token: string_token +token_type: bearer +refresh_token: string_refresh_token +expiry: 2023-11-24T12:40:27.954150524+03:00 +retries: 5 +sslSkipVerify: true +timeout: 5m +path_cfg: config +path_token: token +timeToRefresh: 5 diff --git a/samples/config/config.json b/samples/config/config.json new file mode 100644 index 0000000..c8fca0b --- /dev/null +++ b/samples/config/config.json @@ -0,0 +1,9 @@ +{ + "appId": "", + "appSecret": "", + "ssoUrl": "https://sso.digitalenergy.online", + "decortUrl": "https://mr4.digitalenergy.online", + "retries": 5, + "timeout": "5m", + "sslSkipVerify": false +} diff --git a/samples/config/config.yml b/samples/config/config.yml new file mode 100644 index 0000000..fea256d --- /dev/null +++ b/samples/config/config.yml @@ -0,0 +1,7 @@ +appId: +appSecret: +ssoUrl: https://sso.digitalenergy.online +decortUrl: https://mr4.digitalenergy.online +retries: 5 +timeout: 5m +sslSkipVerify: false diff --git a/samples/config/legacy-config.json b/samples/config/legacy-config.json new file mode 100644 index 0000000..9be5381 --- /dev/null +++ b/samples/config/legacy-config.json @@ -0,0 +1,8 @@ +{ + "username": "", + "password": "", + "decortUrl": "https://mr4.digitalenergy.online", + "retries": 5, + "timeout": "5m", + "sslSkipVerify": true +} diff --git a/samples/config/legacy-config.yml b/samples/config/legacy-config.yml new file mode 100644 index 0000000..811b80a --- /dev/null +++ b/samples/config/legacy-config.yml @@ -0,0 +1,6 @@ +username: +password: +decortUrl: https://mr4.digitalenergy.online +retries: 5 +timeout: 5m +sslSkipVerify: true diff --git a/universal-client.go b/universal-client.go new file mode 100644 index 0000000..1c93092 --- /dev/null +++ b/universal-client.go @@ -0,0 +1,37 @@ +package decortsdk + +import ( + "fmt" + "reflect" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v9/pkg/cloudbroker" +) + +type ClientInterface interface { + CloudAPI() *cloudapi.CloudAPI + CloudBroker() *cloudbroker.CloudBroker +} + +func NewUniversal(cfg config.UniversalConfig) (ClientInterface, error) { + countConfigs := 0 + var client ClientInterface + + switch { + case cfg.Decs3oConfig != nil && reflect.TypeOf(*cfg.Decs3oConfig) == reflect.TypeOf(config.Config{}): + client = New(*cfg.Decs3oConfig) + countConfigs++ + case cfg.BVSConfig != nil && reflect.TypeOf(*cfg.BVSConfig) == reflect.TypeOf(config.BVSConfig{}): + client = NewBVS(*cfg.BVSConfig) + countConfigs++ + case cfg.LegacyConfig != nil && reflect.TypeOf(*cfg.LegacyConfig) == reflect.TypeOf(config.LegacyConfig{}): + client = NewLegacy(*cfg.LegacyConfig) + countConfigs++ + } + if countConfigs != 1 { + return nil, fmt.Errorf("only 1 config can be used at a time") + } + + return client, nil +}