diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..56f0af7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,41 @@ +## Version 15.2.0 + +Методы `Audits` в cloudapi/compute, cloudbroker/compute, cloudapi/account, cloudbroker/account, cloudapi/vins, cloudbroker/vins, cloudapi/rg и cloudbroker/rg стали deprecated и в следующих версиях будут удалены, вместо них необходимо использовать метод `List` в cloudapi/audit и cloudbroker/audit с соответствующими фильтрами +Методы `AccessGrant`, `AccessGrantToPool`, `AccessRevoke`, `AccessRevokeToPool` в cloudbroker/sep стали deprecated и в следующих версиях будут удалены +Методы `ComputeCISet`, `ComputeCIUnset`, `GetAudits` в cloudbroker/compute и `GetAudits` в cloudapi/compute стали deprecated и будут удалены в следующих версиях +Методы `ComputeCISet`, `ComputeCIUnset` в cloudbroker/image стали deprecated и будут удалены в следующих версиях + +Все методы группы `.SDN()` находятся в альфа-версии. + + +### Добавлено + +#### image +| Идентификатор задачи | Описание | +| --- | --- | +| BGOS-916 | Вычисляемые поля `Compute` и `MemoryDumpImage` в структуру ответа `ItemSnapSet` в cloudapi/image | + +#### node +| Идентификатор задачи | Описание | +| --- | --- | +| BGOS-919 | Вычисляемое поле `MaxSupportedGeneration` в структуру ответа `CpuInfo` в cloudbroker/node | + +#### zone +| Идентификатор задачи | Описание | +| --- | --- | +| BGOS-914 | Вычисляемые поля `ID`, `Name` и `ConsumedBy` в структуру ответа `RecordConsumption` в cloudbroker/sep | +| BGOS-914 | Структура ответа `ListConsumption` в cloudbroker/sep | + + +### Изменено + +#### account +| Идентификатор задачи | Описание | +| --- | --- | +| BGOS-918 | Тип обязательного поля `Ratio` с `float64` на `uint64` в структуре запроса `SetCPUAllocationRatioRequest` в cloudbroker/account | + +#### SEP +| Идентификатор задачи | Описание | +| --- | --- | +| BGOS-914 | Тип поля `SEPID` с обязательного на опциональный в структуре запроса `ConsumptionRequest` в cloudbroker/sep | +| BGOS-914 | Тип ответа метода `Consumption` с `RecordConsumption` на `ListConsumption` в cloudbroker/sep | 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 new file mode 100644 index 0000000..907d5f6 --- /dev/null +++ b/README.md @@ -0,0 +1,1732 @@ +# Dynamix SDK + +Dynamix SDK - это библиотека, написанная на языке GO, позволяющая взаимодействовать с API облачной платформы **Dynamix**. Библиотека содержит в себе структуры и методы, необходимые для отправки запросов. Dynamix SDK имеет встроенный http-клиент и поддерживает разные способы авторизации на платформе. Библиотека так же содержит в себе модели ответов от платформы. + +## Версии + + - Версия 9.0.х Dynamix-SDK соответствует 4.1.0 версии платформы + - Версия 10.0.х Dynamix-SDK соответствует 4.2.0 версии платформы + - Версия 11.0.х Dynamix-SDK соответствует 4.3.0 версии платформы + - Версия 12.x.х Dynamix-SDK соответствует 4.4.0 версии платформы + - Версия 13.x.х Dynamix-SDK соответствует 4.5.0 версии платформы + - Версия 14.x.х Dynamix-SDK соответствует 4.6.0 версии платформы + - Версия 15.x.х Dynamix-SDK соответствует 4.7.0 версии платформы + +## Оглавление + +- [Dynamix SDK](#decort-sdk) + - [Версии](#версии) + - [Оглавление](#оглавление) + - [Установка](#установка) + - [Список API](#список-api) + - [Cloudapi](#cloudapi) + - [Cloudbroker](#cloudbroker) + - [SDN](#sdn) + - [Работа с библиотекой](#работа-с-библиотекой) + - [Настройка конфигурации клиента](#настройка-конфигурации-клиента) + - [Пример конфигурации клиента](#пример-конфигурации-клиента) + - [Парсинг конфигурации из файла](#парсинг-конфигурации-из-файла) + - [Пример 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) + - [Проверка соответствия версии платформы и версии dynamix](#проверка-соответствия-версии-платформы-и-версии-dynamix) + - [Пример выполнения запроса](#пример-выполнения-запроса-5) + - [Создание mock клиента](#создание-mock-клиента) + - [Пример создания mock клиента](#пример-создания-mock-клиента) + +## Установка + +Выполните команду в терминале: + +```bash +go get -u repository.basistech.ru/BASIS/dynamix-golang-sdk/v15 +``` + +## Список API + +Библиотека поддерживает две группы API платформы: + +- `cloudapi` - пользовательская группа, которая позволяет воспользоваться всем стардартным функционалом платформы; +- `cloudbroker` - административная группа, которая позволяет воспользоваться всем стандартным функционалом платформы и расширенными возможностями, включающими в себя управление пользователями, ресурсами, платформами размещения ресурсов и т.д. +- `sdn` - группа для работы с SDN; + +### Cloudapi + +`Cloudapi` позволяет выполнять запросы к группе пользовательских конечных точек +Данная группа ручек позволяет выполнять следующие операции в платформе: + +- `Account` - управление аккаунтами - внутренними учетными записями платформы, которые являются владельцами вычислительных ресурсов; +- `Audit` - получение информации о событиях системы; +- `BService` - управление группами виртуальных машин (computes); +- `Compute` - управление виртуальными машинами (индивидуально); +- `Disks` - управление виртуальными дисками; +- `DPDK` - управление виртуальными сетями DPDK; +- `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ; +- `FLIPgroup` - управление группами "плавающими" ip - адресами; +- `Image` - управление образами операционных систем; +- `K8CI` - получение информации о конвейере для создания кластера; +- `K8S` - управление кластерами kubernetes; +- `KVMx86` - создание виртуальной машины x86; +- `LB` - управление балансировщиками нагрузки; +- `Locations` - получение информации о grid площадки; +- `Prometheus` - получение статистики prometheus; +- `RG` - управление ресурсными группами аккаунта; +- `Security group` – управление группами безопасности; +- `SEP` - управление storage endpoint (sep); +- `Storage policy` – получение информации о политиках хранения; +- `Tasks` - получение информации о ходе выполнения асинхронных задач (например, создание кластера); +- `Trunk` - получение информации о транковых портах; +- `VFPool` - управление пулом виртуальных сетевых функций; +- `VINS` - управление виртуальными изолированными сетями; +- `VGPU` - управление виртуальными графическими процессорами; +- `Zone` - управление зонами. + +### Cloudbroker + +`Cloudbroker` позволяет выполнять запросы к группе пользовательских конечных точек +Данная группа ручек позволяет выполнять следующие операции в платформе: + +- `Account` - управление аккаунтами - внутренними учетными записями платформы, которые являются владельцами вычислительных ресурсов; +- `Audit` - получение информации о событиях системы; +- `APIAccess` - управление доступом к API и его объектам; +- `Backup` - управление резервным копированием; +- `BService` - управление группами виртуальных машин (computes); +- `Compute` - управление виртуальными машинами (индивидуально); +- `Disks` - управление виртуальными дисками; +- `DPDK` - управление виртуальными сетями DPDK; +- `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ; +- `FLIPGroup` - управление группами с «плавающими» ip адресами; +- `Grid` - управление площадками; +- `Group` - управление группами пользователей; +- `Image` - управление образами операционных систем; +- `K8CI` - управление конвейром для создания кластера; +- `K8S` - управление кластерами kubernetes; +- `KVMx86` - создание виртуальной машины x86; +- `LB` - управление балансировщиками нагрузки; +- `Node` - управление нодами платформы; +- `PCIDevice` - управление устройствами; +- `Prometheus` - получение статистики prometheus; +- `Resmon` - получение статистики resource monitoring; +- `RG` - управление ресурсными группами аккаунта; +- `Security group` – управление группами безопасности; +- `SEP` - управление storage endpoint (sep); +- `Storage policy` – управление политиками хранения; +- `Tasks` - получение информации о ходе выполнения асинхронных задач (например, создание кластера); +- `Trunk` - управление транковыми портами; +- `User` - управление пользователями (индивидуально); +- `VGPU` - управление виртуальными графическими процессорами; +- `VFPool` - управление пулом виртуальных сетевых функций; +- `VINS` - управление виртуальными изолированными сетями. +- `Zone` - управление зонами. + +### SDN + +`SDN` позволяет выполнять запросы к группе пользовательских конечных точек +Данная группа ручек позволяет выполнять следующие операции в платформе: + +- `Access group` - управление группами доступа; +- `Address pool` - управление пулами адресов; +- `DefaultSecurityPolicies` - управление политиками хранения по умолчанию; +- `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ; +- `Hypervisors` - управление гипервизорами; +- `FloatingIPs` - управление плавающими IP-адресами; +- `Logical ports` - управление логическими портами; +- `NetworkObjectGroups` - управление группами объектов сети; +- `Routers` - управление роутерами; +- `SecurityPolicies` - управление политиками хранения; +- `Segments` - управление сегментами; +- `Version` - получение информации о версии SDN; + +## Работа с библиотекой + +Алгоритм работы с библиотекой выглядит следующим образом: + +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/v15/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/v15/config" +) + +func main() { + // Парсинг конфигурации из JSON-файла + cfg, _ := config.ParseConfigJSON("") +} +``` + +#### Пример JSON конфигурации + +```json +{ + "appId": "", + "appSecret": "", + "ssoUrl": "https://sso.digitalenergy.online", + "decortUrl": "https://mr4.digitalenergy.online", + "retries": 5, + "timeout": "5m", + "sslSkipVerify": false +} +``` + +#### Пример YAML конфигурации + +```yaml +appId: +appSecret: +ssoUrl: https://sso.digitalenergy.online +decortUrl: https://mr4.digitalenergy.online +retries: 5 +timeout: 5m +sslSkipVerify: false +``` + +### Создание клиента + +Создание клиента происходит с помощью функции-строителя `New` из основного пакета `decort-sdk`, для избежания проблем с именами, пакету можно присвоить алиас `decort`. Функция принимает конфигурацию, возвращает структуру `DecortClient`, с помощью которой можно взаимодействовать с платформой. + +### Пример + +```go +package main + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +func main() { + // Настройка конфигурации + cfg := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + } + + cfg.SetTimeout(5 * time.Minute) + + // Создание клиента + client := decort.New(cfg) +} +``` + +### Создание структуры запроса + +Структуры запросов определены в пакетах: + +- `pkg/cloudapi` - для `cloudapi` +- `pkg/cloudbroker` - для `cloudbroker` +- `pkg/sdn` - для `sdn` + +В каждом пакете находятся пакеты групп 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/prometheus` - для `Prometheus` + - `pkg/cloudapi/rg` - для `RG` + - `pkg/cloudapi/secgroup` - для `Security group` + - `pkg/cloudapi/sep` - для `SEP` + - `pkg/cloudapi/stpolicy` - для `Storage policy` + - `pkg/cloudapi/tasks` - для `Tasks` + - `pkg/cloudapi/trunk` - для `Trunk` + - `pkg/cloudapi/vfpool` - для `VFPool` + - `pkg/cloudapi/vins` - для `VINS` + - `pkg/cloudapi/vgpu` - для `VGPU` + - `pkg/cloudapi/zone` - для `Zone` +- **cloudbroker**: + - `pkg/cloudbroker/account` - для `Account` + - `pkg/cloudbroker/audit` - для `Audit` + - `pkg/cloudbroker/apiaccess` - для `APIAccess` + - `pkg/cloudbroker/backup` - для `Backup` + - `pkg/cloudbroker/bservice` - для `Basic Service` + - `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/secgroup` - для `Security group` + - `pkg/cloudbroker/sep` - для `SEP` + - `pkg/cloudbroker/stpolicy` - для `Storage policy` + - `pkg/cloudbroker/tasks` - для `Tasks` + - `pkg/cloudbroker/trunk` - для `Trunk` + - `pkg/cloudbroker/user` - для `User` + - `pkg/cloudbroker/vgpu` - для `VGPU` + - `pkg/cloudbroker/vfpool` - для `VFPool` + - `pkg/cloudbroker/vins` - для `VINS` + - `pkg/cloudbroker/zone` - для `Zone` +- **sdn**: + - `pkg/sdn/acsgroups` - для `Access groups` + - `pkg/sdn/adrspools` - для `Address pool` + - `pkg/sdn/defsecpolicies` - для `DefaultSecurityPolicies` + - `pkg/sdn/external_networks` - для `ExtNet` + - `pkg/sdn/hypervisors` - для `Hypervisors` + - `pkg/sdn/flips` - для `FloatingIPs` + - `pkg/sdn/logicalports` - для `Logical ports` + - `pkg/sdn/netobjgroups` - для `NetworkObjectGroups` + - `pkg/sdn/routers` - для `Routers` + - `pkg/sdn/secpolicies` - для `SecurityPolicies` + - `pkg/sdn/segments` - для `Segments` + - `pkg/sdn/version` - для `Version` + +Все поля структуры имеют описание, в которых содержится: + +- Их назначение; +- Обязательный или нет - поле 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"` + + // Node ID + // Required: false + NodeID uint64 `url:"nodeId,omitempty" json:"nodeId,omitempty"` +} +``` + +#### Пример создания запроса для развертывания виртуальной машины: + +```go +package main + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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, + } +} +``` + +### Выполнение запроса + +Чтобы выполнить запрос, необходимо: + +1. Вызвать у клиента метод, отвечающий за определение группы API для взаимодействия, это может быть `.CloudAPI()`, `.CloudBroker()` или `.SDN()`. Данные методы возвращают соответствующие структуры, с помощью которых можно совершать запросы. +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` + - `.Prometheus()` - для работы с `Prometheus` + - `.RG()` - для работы с `RG` + - `.SecurityGroup()` - для работы с `Security Group` + - `.SEP()` - для работы с `SEP` + - `.StPolicy()` - для работы с `Storage Policy` + - `.Tasks()` - для работы с `Tasks` + - `.Trunk()` - для работы с `Trunk` + - `.VFPool()` - для работы с `VFPool` + - `.VINS()` - для работы с `VINS` + - `.VGPU()` - для работы с `VGPU` + - `.Zone()` - для работы с `Zone` + + Доступные методы для `.CloudBroker()`: + + - `.Account()` - для работы с `Account` + - `.Audit()` - для работы с `Audit` + - `.APIAccess()` - для работы с `APIAccess` + - `.Backup()` - для работы с `Backup` + - `.BService()` - для работы с `BService` + - `.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` + - `.SecurityGroup()` - для работы с `Security Group` + - `.SEP()` - для работы с `SEP` + - `.StPolicy()` - для работы с `Storage Policy` + - `.Tasks()` - для работы с `Tasks` + - `.Trunk()` - для работы с `Trunk` + - `.User()` - для работы с `User` + - `.VGPU()` - для работы с `VGPU` + - `.VFPool()` - для работы с `VFPool` + - `.VINS()` - для работы с `VINS` + - `.Zone()` - для работы с `Zone` + + Доступные методы для `.SDN()`: + + - `.AccessGroup()` - для работы с `Access group` + - `.AddressPool()` - для работы с `Addres pool` + - `.DefaultSecurityPolicies()` - для работы с `DefaultSecurityPolicies` + - `.ExtNet()` - для работы с `ExtNet` + - `.Hypervisors()` - для работы с `Hypervisors` + - `.FloatingIPs()` - для работы с `FloatingIPs` + - `.LogicalPorts()` - для работы с `Logical ports` + - `.NetworkObjectGroups()` - для работы с `NetworkObjectGroups` + - `.Routers()` - для работы с `Routers` + - `.SecurityPolicies()` - для работы с `SecurityPolicies` + - `.Segments()` - для работы с `Segments` + - `.Version()` - для работы с `Version` + +3. Вызвать метод, отвечающий за выполнение запроса и передать в него: + +- контекст; +- структуру запроса. + У каждой группы ручек API имеются свои доступные методы, которые определяются платформой. + +4. Обработать результат и ошибки. + +Т.к. все вызовы методов идут последовательно, можно их объеденить в конвейер: +Общий вид конвейера будет выглядеть так: + +```go + client..<группа>.<метод> +``` + +#### Пример выполнения запроса + +```go +package main + +import ( + "log" + "fmt" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/tasks" + tasks_cb "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/v15/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/v15/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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +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/v15/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/v15/config" +) + +func main() { + // Парсинг конфигурации из YAML-файла + BVSCfg, _ := config.ParseConfigBVSYAML("") +} +``` + +#### Парсинг BVS токена из файла + +Также возможно создать переменную токена из JSON или YAML файла, используя функцию `ParseTokenBVSJSON` (или `ParseTokenBVSYAML`) из пакета config. +
+*См. пример файлов конфигурации ниже и в директории `samples/`.* + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +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/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/v15/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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +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/v15/config" + decort "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +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) +} +``` + + +## Проверка соответствия версии платформы и версии dynamix + +С версии v11.0.0 для каждого клиента добавлен метод `Check` для проверки соответствия decort-sdk и платформы dynamix. +В случае соответсвия decort-sdk и платформы dynamix возвращается структура, содержащая информацию о версии и актуальном билде платформы. + +#### Пример выполнения запроса + +```go +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/config" +) + +func main(){ + // Настройка конфигурации + config := config.Config{ + AppID: "", + AppSecret: "", + SSOURL: "https://sso.digitalenergy.online", + DecortURL: "https://mr4.digitalenergy.online", + Retries: 5, + SSLSkipVerify: true, + } + + // Создание клиента + client := decort.New(config) + + // Проверка соответствия версии + checkInfo, err := client.Check() +} +``` + +# Создание mock клиента + +Создание клиента происходит с помощью функции-строителя `NewMockDecortClient` из основного пакета `decort-sdk`. Функция принимает mock реализацию интерфейса interfaces.Calller, возвращает структуру `MockDecortClient`, с помощью которой можно производить unit тестирование API Decort SDK без подключения к серверу + +#### Пример создания mock клиента +```go +package unit_test + +import ( + "testing" + "go.uber.org/mock/gomock" + decortsdk "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" +) + +// Пример юнит тестирования на моках +func TestClient(t *testing.T) { + ctrl := gomock.NewController(t) + // Создаем mock интерфейса Caller + mockCaller := decortsdk.NewMockCaller(ctrl) + // Создаем mock интерфейса DecortClient + mockClient := decortsdk.NewMockDecortClient(mockCaller) + // .... +} +``` +Пример юнит теста можно посмотреть в файле [samples/client/client_test.go](samples/client/client_test.go) + +При редактировании интерфеса interface.Caller необходимо перегенерировать Mock : +``` shell +make gen-mock +``` \ No newline at end of file diff --git a/check.go b/check.go new file mode 100644 index 0000000..fd469a3 --- /dev/null +++ b/check.go @@ -0,0 +1,106 @@ +package decortsdk + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" +) + +type CheckInfo struct { + Version string `json:"version"` + Build uint64 `json:"build"` +} + +const versionURL = "/system/info/version" + +func (de DecortClient) Check() (*CheckInfo, error) { + res, err := de.DecortApiCall(context.Background(), http.MethodGet, versionURL, nil) + if err != nil { + return nil, err + } + + info := CheckInfo{} + + err = json.Unmarshal([]byte(strings.Replace(strings.Trim(string(res), `"`), "\\", "", -1)), &info) + if err != nil { + var v string + json.Unmarshal([]byte(res), &v) + if _, exists := constants.VersionMap[v]; exists { + info.Version = v + } else { + return nil, fmt.Errorf("platform version isn't supported") + } + } + + if v, ok := constants.VersionMap[info.Version]; ok { + if v == "-" { + return &info, nil + } + return nil, errors.New(fmt.Sprintf("SDK don't support platform version %s, please use %s SDK version", info.Version, v)) + } + + return nil, errors.New(fmt.Sprintf("platform version %s isn't supported", info.Version)) +} + +func (bvs BVSDecortClient) Check() (*CheckInfo, error) { + res, err := bvs.DecortApiCall(context.Background(), http.MethodGet, versionURL, nil) + if err != nil { + return nil, err + } + + info := CheckInfo{} + + err = json.Unmarshal([]byte(strings.Replace(strings.Trim(string(res), `"`), "\\", "", -1)), &info) + if err != nil { + var v string + json.Unmarshal([]byte(res), &v) + if _, exists := constants.VersionMap[v]; exists { + info.Version = v + } else { + return nil, fmt.Errorf("platform version isn't supported") + } + } + + if v, ok := constants.VersionMap[info.Version]; ok { + if v == "-" { + return &info, nil + } + return nil, errors.New(fmt.Sprintf("SDK don't support platform version %s, please use %s SDK version", info.Version, v)) + } + + return nil, errors.New(fmt.Sprintf("platform version %s isn't supported", info.Version)) +} + +func (ldc LegacyDecortClient) Check() (*CheckInfo, error) { + res, err := ldc.DecortApiCall(context.Background(), http.MethodGet, versionURL, nil) + if err != nil { + return nil, err + } + + info := CheckInfo{} + + err = json.Unmarshal([]byte(strings.Replace(strings.Trim(string(res), `"`), "\\", "", -1)), &info) + if err != nil { + var v string + json.Unmarshal([]byte(res), &v) + if _, exists := constants.VersionMap[v]; exists { + info.Version = v + } else { + return nil, fmt.Errorf("platform version isn't supported") + } + } + + if v, ok := constants.VersionMap[info.Version]; ok { + if v == "-" { + return &info, nil + } + return nil, errors.New(fmt.Sprintf("SDK don't support platform version %s, please use %s SDK version", info.Version, v)) + } + + return nil, errors.New(fmt.Sprintf("platform version %s isn't supported", info.Version)) +} diff --git a/client.go b/client.go new file mode 100644 index 0000000..42bacca --- /dev/null +++ b/client.go @@ -0,0 +1,453 @@ +package decortsdk + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/base64" + "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/v15/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn" +) + +// 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 err := validators.ValidateConfig(cfg); err != nil { + panic(validators.ValidationErrors(validators.GetErrors(err))) + } + + if cfg.Retries == 0 { + cfg.Retries = 5 + } + + return &DecortClient{ + decortURL: cfg.DecortURL, + client: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + //nolint:gosec + InsecureSkipVerify: cfg.SSLSkipVerify, + }, + }, + }, + cfg: trimConfig(&cfg), + 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) +} + +// SDN builder +func (dc *DecortClient) SDN() *sdn.SDN { + return sdn.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 +} + +// DecortApiCallCtype method for sending requests to the platform with content type +func (dc *DecortClient) DecortApiCallCtype(ctx context.Context, method, url, ctype string, params interface{}) ([]byte, error) { + + var body *bytes.Buffer + + switch ctype { + case constants.MIMESTREAM: + body = bytes.NewBuffer(params.([]byte)) + case constants.MIMEJSON: + jsonBody, err := json.Marshal(params) + if err != nil { + return nil, err + } + body = bytes.NewBuffer(jsonBody) + default: + ctype = constants.MIMEPOSTForm + 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) + expiryTime, err := getTokenExp(token) + if err != nil { + return fmt.Errorf("cannot get expiry time: %w", err) + } + + dc.cfg.Token = token + dc.expiryTime = expiryTime + + 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 || resp.StatusCode == 204 { + 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 +} + +func getTokenExp(token string) (time.Time, error) { + parts := strings.Split(token, ".") + if len(parts) != 3 { + return time.Time{}, fmt.Errorf("invalid token format") + } + + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return time.Time{}, fmt.Errorf("error decode payload from token: %w", err) + } + + var claims map[string]interface{} + if err := json.Unmarshal(payload, &claims); err != nil { + return time.Time{}, err + } + + exp, ok := claims["exp"] + if !ok { + return time.Time{}, fmt.Errorf("exp time bot found") + } + + expTime := time.Unix(int64(exp.(float64)), 0) + + return expTime, nil +} diff --git a/client_bvs.go b/client_bvs.go new file mode 100644 index 0000000..7e3d627 --- /dev/null +++ b/client_bvs.go @@ -0,0 +1,472 @@ +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/v15/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn" +) + +// 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 err := validators.ValidateConfig(cfg); err != nil { + panic(validators.ValidationErrors(validators.GetErrors(err))) + } + + 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) +} + +// SDN builder +func (bdc *BVSDecortClient) SDN() *sdn.SDN { + return sdn.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 +} + +// DecortApiCallCtype method for sending requests to the platform with content type +func (bdc *BVSDecortClient) DecortApiCallCtype(ctx context.Context, method, url, ctype string, params interface{}) ([]byte, error) { + var body *bytes.Buffer + + switch ctype { + case constants.MIMESTREAM: + body = bytes.NewBuffer(params.([]byte)) + case constants.MIMEJSON: + jsonBody, err := json.Marshal(params) + if err != nil { + return nil, err + } + body = bytes.NewBuffer(jsonBody) + default: + ctype = constants.MIMEPOSTForm + 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/client_mock.go b/client_mock.go new file mode 100644 index 0000000..be2327e --- /dev/null +++ b/client_mock.go @@ -0,0 +1,32 @@ +package decortsdk + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn" +) + +type MockDecortClient struct { + apiCaller *MockCaller +} + +func NewMockDecortClient(apiCaller *MockCaller) ClientInterface { + return &MockDecortClient{ + apiCaller: apiCaller, + } +} + +// CloudAPI builder +func (mdc *MockDecortClient) CloudAPI() *cloudapi.CloudAPI { + return cloudapi.New(mdc.apiCaller) +} + +// CloudBroker builder +func (mdc *MockDecortClient) CloudBroker() *cloudbroker.CloudBroker { + return cloudbroker.New(mdc.apiCaller) +} + +// SDN builder +func (mdc *MockDecortClient) SDN() *sdn.SDN { + return sdn.New(mdc.apiCaller) +} diff --git a/client_mock_gen.go b/client_mock_gen.go new file mode 100644 index 0000000..e5fec24 --- /dev/null +++ b/client_mock_gen.go @@ -0,0 +1,86 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: interfaces/caller.go +// +// Generated by this command: +// +// mockgen -package decortsdk -source interfaces/caller.go +// + +// Package decortsdk is a generated GoMock package. +package decortsdk + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockCaller is a mock of Caller interface. +type MockCaller struct { + ctrl *gomock.Controller + recorder *MockCallerMockRecorder + isgomock struct{} +} + +// MockCallerMockRecorder is the mock recorder for MockCaller. +type MockCallerMockRecorder struct { + mock *MockCaller +} + +// NewMockCaller creates a new mock instance. +func NewMockCaller(ctrl *gomock.Controller) *MockCaller { + mock := &MockCaller{ctrl: ctrl} + mock.recorder = &MockCallerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCaller) EXPECT() *MockCallerMockRecorder { + return m.recorder +} + +// DecortApiCall mocks base method. +func (m *MockCaller) DecortApiCall(ctx context.Context, method, url string, params any) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecortApiCall", ctx, method, url, params) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DecortApiCall indicates an expected call of DecortApiCall. +func (mr *MockCallerMockRecorder) DecortApiCall(ctx, method, url, params any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecortApiCall", reflect.TypeOf((*MockCaller)(nil).DecortApiCall), ctx, method, url, params) +} + +// DecortApiCallCtype mocks base method. +func (m *MockCaller) DecortApiCallCtype(ctx context.Context, method, url, ctype string, params any) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecortApiCallCtype", ctx, method, url, ctype, params) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DecortApiCallCtype indicates an expected call of DecortApiCallCtype. +func (mr *MockCallerMockRecorder) DecortApiCallCtype(ctx, method, url, ctype, params any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecortApiCallCtype", reflect.TypeOf((*MockCaller)(nil).DecortApiCallCtype), ctx, method, url, ctype, params) +} + +// DecortApiCallMP mocks base method. +func (m *MockCaller) DecortApiCallMP(ctx context.Context, method, url string, params any) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecortApiCallMP", ctx, method, url, params) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DecortApiCallMP indicates an expected call of DecortApiCallMP. +func (mr *MockCallerMockRecorder) DecortApiCallMP(ctx, method, url, params any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecortApiCallMP", reflect.TypeOf((*MockCaller)(nil).DecortApiCallMP), ctx, method, url, params) +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..6f85b5f --- /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/v15/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..dd7b8d2 --- /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/v15/internal/serialization" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..ba2ee8d --- /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/v15/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/debug.txt b/debug.txt deleted file mode 100644 index b2b5d0d..0000000 --- a/debug.txt +++ /dev/null @@ -1 +0,0 @@ -debug \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..20af101 --- /dev/null +++ b/go.mod @@ -0,0 +1,25 @@ +module repository.basistech.ru/BASIS/dynamix-golang-sdk/v15 + +go 1.24.0 + +require ( + github.com/go-playground/validator/v10 v10.28.0 + github.com/google/go-querystring v1.1.0 + github.com/joho/godotenv v1.5.1 + github.com/stretchr/testify v1.9.0 + go.uber.org/mock v0.6.0 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.10 // indirect + 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/leodido/go-urn v1.4.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..905981e --- /dev/null +++ b/go.sum @@ -0,0 +1,38 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= +github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +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.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688= +github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU= +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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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..026ac22 --- /dev/null +++ b/interfaces/caller.go @@ -0,0 +1,15 @@ +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) + + // DecortApiCallCtype method for sending requests to the platform + DecortApiCallCtype(ctx context.Context, method, url, ctype 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..737c781 --- /dev/null +++ b/internal/constants/constants.go @@ -0,0 +1,39 @@ +package constants + +const ( + RESTMACHINE = "/restmachine" +) + +const ( + MIMEJSON = "application/json" + MIMEHTML = "text/html" + MIMEXML = "application/xml" + MIMEXML2 = "text/xml" + MIMEPlain = "text/plain" + MIMEPOSTForm = "application/x-www-form-urlencoded" + MIMEMultipartPOSTForm = "multipart/form-data" + MIMEPROTOBUF = "application/x-protobuf" + MIMEMSGPACK = "application/x-msgpack" + MIMEMSGPACK2 = "application/msgpack" + MIMEYAML = "application/x-yaml" + MIMEYAML2 = "application/yaml" + MIMETOML = "application/toml" + MIMESTREAM = "application/octet-stream" +) + +var FileName = map[string]string{ + "OidcCertificate": "ca.crt", +} + +var K8sValues = []string{"labels", "taints", "annotations, additionalSANs"} + +var VersionMap = map[string]string{ + "4.7.0": "-", + "4.6.0": "-", + "4.5.0": "-", + "4.4.0": "-", + "4.3.0": "-", + "4.2.0": "-", + "4.1.1": "-", + "4.1.0": "-", +} diff --git a/internal/multierror/join.go b/internal/multierror/join.go new file mode 100644 index 0000000..e5afca3 --- /dev/null +++ b/internal/multierror/join.go @@ -0,0 +1,41 @@ +package multierror + +func Join(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + var b []byte + for i, err := range e.errs { + if i > 0 { + b = append(b, '\n') + } + b = append(b, err.Error()...) + } + return string(b) +} + +func (e *joinError) Unwrap() []error { + return e.errs +} diff --git a/internal/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..bd663a0 --- /dev/null +++ b/internal/validators/custom.go @@ -0,0 +1,486 @@ +package validators + +import ( + "errors" + "fmt" + "net/url" + "reflect" + "regexp" + "strconv" + "strings" + + "github.com/go-playground/validator/v10" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/multierror" +) + +// protoValidator is used to validate Proto fields. +func protoValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, protoValues) +} + +// apiGroupValidator is used to validate APIGroup fields +func apiGroupValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, apiGroupValues) +} + +// 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 +} + +// 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) +} + +// 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) +} + +// securityGroupDirectionValidator is used to validate Direction field +func securityGroupDirectionValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, securityGroupDirectionValues) +} + +// securityGroupEthertypeValidator is used to validate Ethertype field +func securityGroupEthertypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, securityGroupEthertypeValues) +} + +// securityGroupProtocolValidator is used to validate Protocol field +func securityGroupProtocolValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, securityGroupProtocolValues) +} + +// 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) +} + +// flipgroupClientTypeValidator is used to validate ClientType field. +func flipgroupClientTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, flipgroupClientTypeValues) +} + +// massCreateTypeValidator is used to validate net type field when mass creating kvm +func massCreateTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, massCreateNetTypeValues) +} + +// 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) +} + +// 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() + + return IsInSlice(fieldValue, chipsetValues) +} + +func preferredCPUValidator(fe validator.FieldLevel) bool { + fieldSlice, ok := fe.Field().Interface().([]int64) + if !ok { + return false + } + + for _, value := range fieldSlice { + if value < -1 { + return false + } + } + + return true +} + +// loaderTypeValidator is used to validate loaderType fields +func loaderTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, loaderTypeValues) +} + +// languageValidator is used to validate language fields +func languageValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, languageValues) +} + +func userProviderValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, userProviders) +} + +// sepTypeValidator is used to validate sepType fields +func sepTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, sepTypeValues) +} + +// deviceValidator is used to validate extnet device fields +func deviceValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, deviceValues) +} + +// ipTypesValidator is used to validate ip types version fields +func ipTypesValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, ipTypeValues) +} + +// 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...) +} + +// trunkTagsValidator checks if trunk_tags is in range from 1 to 4095 +func trunkTagsValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + numFieldValue, err := strconv.ParseInt(fieldValue, 10, 64) + if err != nil { + return false + } + return uint64(numFieldValue) >= uint64(trunkTagsMin) && uint64(numFieldValue) <= uint64(trunkTagsMax) +} + +// addressPoolNetTypeValidator is used to validate NetAddressType fields +func addressPoolNetTypeValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, addressPoolNetTypeValues) +} + +// sepTechStatusValidator is used to validate SepTechStatus fields +func sepTechStatusValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + return IsInSlice(fieldValue, sepTechStatusValues) +} + +// pciDeviceHWPathValidator is used to validate PCI device hardware path fields (e.g. 0000:81:00.0) +func pciDeviceHWPathValidator(fe validator.FieldLevel) bool { + fieldValue := fe.Field().String() + + ok, _ := regexp.MatchString(`^[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-7]$`, fieldValue) + return ok +} + +// sepNameValidator is used to validate SEP name fields +func sepNameValidator(fe validator.FieldLevel) bool { + ok, _ := regexp.MatchString(`^[a-zA-Zа-яА-ЯёЁ0-9][a-zA-Zа-яА-ЯёЁ0-9_.\[\]()\-]*$`, fe.Field().String()) + return ok +} + +// sepDescriptionValidator is used to validate SEP description fields +func sepDescriptionValidator(fe validator.FieldLevel) bool { + ok, _ := regexp.MatchString(`^[a-zA-Zа-яА-ЯёЁ0-9_.\[\]()\-]*$`, fe.Field().String()) + return ok +} diff --git a/internal/validators/helper.go b/internal/validators/helper.go new file mode 100644 index 0000000..2ac58a3 --- /dev/null +++ b/internal/validators/helper.go @@ -0,0 +1,53 @@ +package validators + +import ( + "errors" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..6894d6d --- /dev/null +++ b/internal/validators/messages.go @@ -0,0 +1,369 @@ +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 "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)) + + // apiGroup Validators + case "apiGroup": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(apiGroupValues)) + + // 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 "mtu": + return fmt.Sprint(prefix, fe.Field(), "must be ", mtuMin, "-", mtuMax) + + case "preferredCPU": + return fmt.Sprint(prefix, fe.Field(), "must be equal to or greater than", -1) + + 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)) + + // 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 Mass create validators + case "massCreateNetType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(massCreateNetTypeValues)) + + // 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 "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)) + + case "loaderType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(loaderTypeValues)) + + case "language": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(languageValues)) + + case "sepType": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(sepTypeValues)) + + // user validators + case "userProvider": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(userProviders)) + + // security group validators + case "securityGroupDirection": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(securityGroupDirectionValues)) + + case "securityGroupEthertype": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(securityGroupEthertypeValues)) + + case "securityGroupProtocol": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(securityGroupProtocolValues)) + + // trunk tags validator + case "trunkTags": + return fmt.Sprintf("%s %s must be in range from 1 to 4095", + prefix, + fe.Field()) + + // addressPoolNetTypeValidator validator + case "addressPoolNetTypeValidator": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(addressPoolNetTypeValues)) + + case "device": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(deviceValues)) + + case "ipTypes": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(ipTypeValues)) + + case "sepTechStatus": + return fmt.Sprintf("%s %s must be one of the following: %s", + prefix, + fe.Field(), + joinValues(sepTechStatusValues)) + } + + 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..5ad2545 --- /dev/null +++ b/internal/validators/validator.go @@ -0,0 +1,325 @@ +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("apiGroup", apiGroupValidator) + 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("imageBootType", imageBootTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("imageType", imageTypeValidator) + 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("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("flipgroupClientType", flipgroupClientTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("massCreateNetType", massCreateTypeValidator) + 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 + } + + err = validate.RegisterValidation("preferredCPU", preferredCPUValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("loaderType", loaderTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("language", languageValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("userProvider", userProviderValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("sepType", sepTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("device", deviceValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("trunkTags", trunkTagsValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("securityGroupDirection", securityGroupDirectionValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("securityGroupEthertype", securityGroupEthertypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("securityGroupProtocol", securityGroupProtocolValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("addressPoolNetTypeValidator", addressPoolNetTypeValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("ipTypes", ipTypesValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("sepTechStatus", sepTechStatusValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("pciDeviceHWPath", pciDeviceHWPathValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("sepName", sepNameValidator) + if err != nil { + return err + } + + err = validate.RegisterValidation("sepDescription", sepDescriptionValidator) + 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..17adc27 --- /dev/null +++ b/internal/validators/values.go @@ -0,0 +1,91 @@ +package validators + +var ( + apiGroupValues = []string{"cloudapi", "cloudbroker", "system"} + + 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_I", "gpu_units"} + + bserviceModeValues = []string{"ABSOLUTE", "RELATIVE"} + + computeTopologyValues = []string{"compute", "node"} + computePolicyValues = []string{"RECOMMENDED", "REQUIRED"} + computeModeValues = []string{"EQ", "EN", "ANY"} + computeNetTypeValues = []string{"EXTNET", "VINS"} + computex86NetTypeValues = []string{"EXTNET", "VINS", "VFNIC", "DPDK", "SDN", "EMPTY", "TRUNK"} + computeOrderValues = []string{"cdrom", "network", "hd"} + computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"} + + flipgroupClientTypeValues = []string{"compute", "vins"} + + massCreateNetTypeValues = []string{"EXTNET", "VINS", "TRUNK"} + kvmx86NetTypeValues = []string{"EXTNET", "VINS", "EMPTY", "VFNIC", "DPDK", "SDN", "TRUNK"} + + 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", "unknown"} + 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{"is_powered", "power_on", "shutdown", "force_shutdown", "reboot"} + + vmActionValues = []string{"stop", "move"} + + computeFeaturesValues = []string{"hugepages", "numa", "cpupin", "vfnic", "dpdk", "changemac", "trunk"} + + 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"} + + loaderTypeValues = []string{"linux", "windows", "unknown"} + + sepTypeValues = []string{"hitachi", "dorado", "tatlin", "shared", "local", "des", "ustor"} + + languageValues = []string{"ru", "en"} + + userProviders = []string{"bvs", "decs3o"} + + deviceValues = []string{"primary", "secondary"} + + securityGroupDirectionValues = []string{"inbound", "outbound"} + securityGroupEthertypeValues = []string{"IPv4", "IPv6"} + securityGroupProtocolValues = []string{"icmp", "tcp", "udp"} + + addressPoolNetTypeValues = []string{"IPv4", "IPv6", "MAC"} + + ipTypeValues = []string{"v4, v6"} + + sepTechStatusValues = []string{"ENABLED", "DISABLED"} +) + +const ( + mtuMin = 1500 + mtuMax = 9216 + + trunkTagsMin = 1 + trunkTagsMax = 4095 +) diff --git a/legacy-client.go b/legacy-client.go new file mode 100644 index 0000000..58d4ffb --- /dev/null +++ b/legacy-client.go @@ -0,0 +1,301 @@ +package decortsdk + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + "time" + + "github.com/google/go-querystring/query" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn" +) + +// 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 err := validators.ValidateConfig(cfg); err != nil { + panic(validators.ValidationErrors(validators.GetErrors(err))) + } + + 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) +} + +// SDN builder +func (ldc *LegacyDecortClient) SDN() *sdn.SDN { + return sdn.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 +} + +// DecortApiCallCtype method for sending requests to the platform with content type +func (ldc *LegacyDecortClient) DecortApiCallCtype(ctx context.Context, method, url, ctype string, params interface{}) ([]byte, error) { + // get token + if err := ldc.getToken(ctx); err != nil { + return nil, err + } + + var body *bytes.Buffer + + switch ctype { + case constants.MIMESTREAM: + body = bytes.NewBuffer(params.([]byte)) + case constants.MIMEJSON: + jsonBody, err := json.Marshal(params) + if err != nil { + return nil, err + } + body = bytes.NewBuffer(jsonBody) + default: + ctype = constants.MIMEPOSTForm + 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..48c1a2b --- /dev/null +++ b/pkg/cloudapi/account.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..4dbd5b1 --- /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/v15/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..68ee6d6 --- /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/v15/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..aa5c18f --- /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/v15/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/delete.go b/pkg/cloudapi/account/delete.go new file mode 100644 index 0000000..58b5d6e --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/delete" + + result, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(result), nil +} diff --git a/pkg/cloudapi/account/delete_user.go b/pkg/cloudapi/account/delete_user.go new file mode 100644 index 0000000..d8dedcd --- /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/v15/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..b38aca0 --- /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/v15/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..05da015 --- /dev/null +++ b/pkg/cloudapi/account/filter_test.go @@ -0,0 +1,149 @@ +package account + +import ( + "testing" +) + +var accounts = ListAccounts{ + Data: []ItemAccount{ + { + ACL: []ListRecordACL{ + { + 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: []ListRecordACL{ + { + 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: []ListRecordACL{ + { + 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..9ea6eda --- /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/v15/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..024e8f9 --- /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/v15/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..9442365 --- /dev/null +++ b/pkg/cloudapi/account/get_consumed_cloud_units_by_type.go @@ -0,0 +1,51 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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_I: returns number of public IPs +// - gpu_units: return number of GPU units +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..63d59d3 --- /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/v15/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..1b13a49 --- /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/v15/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..3fbe1e1 --- /dev/null +++ b/pkg/cloudapi/account/list.go @@ -0,0 +1,76 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..ff76cef --- /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/v15/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..f9ba7ae --- /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/v15/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..1a4eb13 --- /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/v15/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..2efe8d8 --- /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/v15/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..412f7ef --- /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/v15/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..fa05d73 --- /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/v15/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..823039f --- /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/v15/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..0df8a28 --- /dev/null +++ b/pkg/cloudapi/account/models.go @@ -0,0 +1,742 @@ +package account + +// Access Control List +type ListRecordACL 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"` +} + +// Access Control List +type RecordACL struct { + // Emails + Emails []string `json:"emails"` + + // 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"` + + // Number of graphics cores + GPUUnits float64 `json:"gpu_units"` + + // Storage policy + StoragePolicy []StoragePolicyItem `json:"storage_policy"` +} + +type StoragePolicyItem struct { + ID uint64 `json:"id"` + + Limit int `json:"limit"` +} + +// Main information in one of if the list of accounts +type ItemAccount struct { + // Access Control List + ACL []ListRecordACL `json:"acl"` + + // Compute Features + ComputeFeatures []string `json:"computeFeatures"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Description + Description string `json:"desc"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Status + Status string `json:"status"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Zones + ZoneIDs []uint64 `json:"zoneIds"` +} + +// List of accounts +type ListAccounts struct { + Data []ItemAccount `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// Policy +type Policy struct { + // Size of the disk + DiskSize float64 `json:"disksize"` + + // Max size of the disk + DiskSizeMax float64 `json:"disksizemax"` + + // SEPs used + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +// 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"` + + // Number of grafic cores + GPU int64 `json:"gpu"` + + // Policies + Policies map[string]Policy `json:"policies"` + + // 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"` +} + +// Detailed information about the account zone +type ZoneID struct { + // ID of zone + ID int64 `json:"id"` + + // Name of zone + Name string `json:"name"` +} + +// Main information about account +type RecordAccount struct { + // DCLocation + DCLocation string `json:"DCLocation"` + + // 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 uint64 `json:"cpu_allocation_ratio"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Description + Description string `json:"desc"` + + // 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"` + + // Storage policy ids + StoragePolicyIDs []uint64 `json:"storage_policy_ids"` + + // UniqPools + UniqPools []interface{} `json:"uniqPools"` + + // Updated By + UpdatedBy string `json:"updatedBy"` + + // Updated time + UpdatedTime uint64 `json:"updatedTime"` + + // Version + Version uint64 `json:"version"` + + // VINS + VINS []uint64 `json:"vins"` + + // VINSes + VINSes uint64 `json:"vinses"` + + // Zone + ZoneIDs []ZoneID `json:"zoneIds"` + + // Zones + DefaultZoneID uint64 `json:"defaultZoneId"` +} + +// 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"` + + // Number of grafic cores + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs + SEPs uint64 `json:"seps"` + + // Policies + Policies map[string]Policy `json:"policies"` +} + +// 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"` + + // Description + Description string `json:"desc"` + + // 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..0d2486e --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/account/restore" + + result, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(result), nil +} diff --git a/pkg/cloudapi/account/serialize.go b/pkg/cloudapi/account/serialize.go new file mode 100644 index 0000000..7122432 --- /dev/null +++ b/pkg/cloudapi/account/serialize.go @@ -0,0 +1,43 @@ +package account + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..135146b --- /dev/null +++ b/pkg/cloudapi/account/update.go @@ -0,0 +1,79 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update account +type UpdateRequest struct { + // ID an account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // 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 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"` + + // Default zone ID + // Required: false + DefaultZoneID uint64 `url:"defaultZoneId,omitempty" json:"defaultZoneId,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..c397b55 --- /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/v15/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..99249b5 --- /dev/null +++ b/pkg/cloudapi/audit.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..01bea61 --- /dev/null +++ b/pkg/cloudapi/audit/audit.go @@ -0,0 +1,15 @@ +package audit + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/filter.go b/pkg/cloudapi/audit/filter.go new file mode 100644 index 0000000..82f80e6 --- /dev/null +++ b/pkg/cloudapi/audit/filter.go @@ -0,0 +1,81 @@ +package audit + +// FilterByID returns ListAudits with specified ID. +func (la ListAudits) FilterByID(guid string) ListAudits { + predicate := func(ia ItemAudit) bool { + return ia.GUID == guid + } + + return la.FilterFunc(predicate) +} + +// FilterByCall returns ListAudits with specified call. +func (la ListAudits) FilterByCall(call string) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.Call == call + } + + return la.FilterFunc(predicate) +} + +// FilterByCorrelationID returns ListAudits with specified correlation id. +func (la ListAudits) FilterByCorrelationID(correlationID string) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.CorrelationID == correlationID + } + + return la.FilterFunc(predicate) +} + +// FilterByRemoteAddr returns ListAudits with specified remote address. +func (la ListAudits) FilterByRemoteAddr(remoteAddr string) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.RemoteAddr == remoteAddr + } + + return la.FilterFunc(predicate) +} + +// FilterByUser returns ListAudits with specified user name. +func (la ListAudits) FilterByUser(user string) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.User == user + } + + return la.FilterFunc(predicate) +} + +// FilterByStatusCode return ListAudits with specified status code. +func (la ListAudits) FilterByStatusCode(statusCode uint64) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.StatusCode == statusCode + } + + return la.FilterFunc(predicate) + +} + +// FilterFunc allows filtering ListAudits based on a user-specified predicate. +func (la ListAudits) FilterFunc(predicate func(ItemAudit) bool) ListAudits { + var result ListAudits + + for _, item := range la.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemAudit +// If none was found, returns an empty struct. +func (la ListAudits) FindOne() ItemAudit { + if len(la.Data) == 0 { + return ItemAudit{} + } + + return la.Data[0] +} diff --git a/pkg/cloudapi/audit/filter_test.go b/pkg/cloudapi/audit/filter_test.go new file mode 100644 index 0000000..8f09a1b --- /dev/null +++ b/pkg/cloudapi/audit/filter_test.go @@ -0,0 +1,115 @@ +package audit + +import ( + "testing" +) + +var audits = ListAudits{ + Data: []ItemAudit{ + { + Args: "[]", + Call: "/restmachine/cloudapi/audit/linkedJobs", + GUID: "550e8400-e29b-41d4-a716-446655440001", + CorrelationID: "550e8400-e29b-41d4-a716-446655440001", + Kwargs: `{\"audit_guid\":\"dd8623a1-a887-48c1-a500-c10210d404cf\"}`, + RemoteAddr: "192.168.1.100", + ResponseTime: 1, + Result: `[]`, + StatusCode: 200, + Timestamp: 1640995200, + TimestampEnd: 1640995201, + User: "test@example.com", + TTL: "2025-07-31T14:22:57.028000", + }, + { + Args: "[]", + Call: "/restmachine/cloudapi/audit/test", + GUID: "550e8400-e29b-41d4-a716-446655440002", + CorrelationID: "550e8400-e29b-41d4-a716-446655440002", + Kwargs: `{\"audit_guid\":\"dd8623a1-a887-48c1-a500-c10210d404cf\"}`, + RemoteAddr: "192.168.1.105", + ResponseTime: 5, + Result: `[]`, + StatusCode: 400, + Timestamp: 1640995200, + TimestampEnd: 1640995201, + User: "test2@example.com", + TTL: "2025-07-31T14:22:57.028000", + }, + }, + EntryCount: 2, +} + +func TestFilterByID(t *testing.T) { + actual := audits.FilterByID("550e8400-e29b-41d4-a716-446655440002").FindOne() + + if actual.GUID != "550e8400-e29b-41d4-a716-446655440002" { + t.Fatal("expected GUID 550e8400-e29b-41d4-a716-446655440002, found: ", actual.GUID) + } + + actualEmpty := audits.FilterByID("") + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByCorrelationID(t *testing.T) { + actual := audits.FilterByCorrelationID("550e8400-e29b-41d4-a716-446655440002").FindOne() + + if actual.CorrelationID != "550e8400-e29b-41d4-a716-446655440002" { + t.Fatal("expected GUID 550e8400-e29b-41d4-a716-446655440002, found: ", actual.CorrelationID) + } + + actualEmpty := audits.FilterByCorrelationID("") + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByRemoteAddr(t *testing.T) { + actual := audits.FilterByRemoteAddr("192.168.1.100").FindOne() + + if actual.RemoteAddr != "192.168.1.100" { + t.Fatal("expected remote address 192.168.1.100, found: ", actual.RemoteAddr) + } + + actualEmpty := audits.FilterByRemoteAddr("") + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByUser(t *testing.T) { + actual := audits.FilterByUser("test@example.com").FindOne() + + if actual.User != "test@example.com" { + t.Fatal("expected user test@example.com, found: ", actual.RemoteAddr) + } + + actualEmpty := audits.FilterByUser("") + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByCall(t *testing.T) { + actual := audits.FilterByCall("/restmachine/cloudapi/audit/test").FindOne() + + if actual.Call != "/restmachine/cloudapi/audit/test" { + t.Fatal("expected call /restmachine/cloudapi/audit/test, found: ", actual.Call) + } +} + +func TestFilterByStatusCode(t *testing.T) { + actual := audits.FilterByStatusCode(200) + + for _, item := range actual.Data { + if item.StatusCode != 200 { + t.Fatal("expected 200 status code, found: ", item.StatusCode) + } + } +} diff --git a/pkg/cloudapi/audit/get.go b/pkg/cloudapi/audit/get.go new file mode 100644 index 0000000..ece9d10 --- /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/v15/internal/validators" +) + +// GetRequest struct to get information about account +type GetRequest struct { + // Audit GUID + // Required: true + AuditGuid string `url:"audit_guid" json:"audit_guid" 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/list.go b/pkg/cloudapi/audit/list.go new file mode 100644 index 0000000..ade0609 --- /dev/null +++ b/pkg/cloudapi/audit/list.go @@ -0,0 +1,124 @@ +package audit + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to give list of account audits +type ListRequest struct { + + // Find all audits after point in time (unixtime) + // Required: false + TimestampAt uint64 `url:"timestamp_at,omitempty" json:"timestamp_at,omitempty"` + + // Find all audits before point in time (unixtime) + // Required: false + TimestampTo uint64 `url:"timestamp_to,omitempty" json:"timestamp_to,omitempty"` + + // Find by user (Mongo RegExp supported) + // Required: false + User string `url:"user,omitempty" json:"user,omitempty"` + + // Find by api endpoint (Mongo RegExp supported) + // Required: false + Call string `url:"call,omitempty" json:"call,omitempty"` + + // Find by request id + // Required: false + RequestID string `url:"request_id,omitempty" json:"request_id,omitempty"` + + // Find by HTTP min status code + // Required: false + MinStatusCode uint64 `url:"min_status_code,omitempty" json:"min_status_code,omitempty"` + + // Find by HTTP max status code + // Required: false + MaxStatusCode uint64 `url:"max_status_code,omitempty" json:"max_status_code,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"` + + // Find by compute id + // Required: false + ComputeID uint64 `url:"compute_id,omitempty" json:"compute_id,omitempty"` + + // Find by account id + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` + + // Find by vins id + // Required: false + VINSID uint64 `url:"vins_id,omitempty" json:"vins_id,omitempty"` + + // Find by service id + // Required: false + ServiceID uint64 `url:"service_id,omitempty" json:"service_id,omitempty"` + + // Find by k8s id + // Required: false + K8SID uint64 `url:"k8s_id,omitempty" json:"k8s_id,omitempty"` + + // Find by flipgroup id + // Required: false + FLIPGroupID uint64 `url:"flipgroup_id,omitempty" json:"flipgroup_id,omitempty"` + + // Find by load balancer id + // Required: false + LBID uint64 `url:"lb_id,omitempty" json:"lb_id,omitempty"` + + // Find by sep id + // Required: false + SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"` + + // Exclude audit lines from response + // Required: false + ExcludeAuditLines bool `url:"exclude_audit_lines,omitempty" json:"exclude_audit_lines,omitempty"` +} + +// List gets audit records for the specified account object +func (a Audit) List(ctx context.Context, req ListRequest) (*ListAudits, error) { + + res, err := a.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of audit records an array of bytes +func (a Audit) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/audit/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/audit/models.go b/pkg/cloudapi/audit/models.go new file mode 100644 index 0000000..98626cf --- /dev/null +++ b/pkg/cloudapi/audit/models.go @@ -0,0 +1,95 @@ +package audit + +// Main info about audit +type RecordAudit struct { + + // Arguments + Arguments string `json:"args"` + + // Call + Call string `json:"call"` + + // GUID + GUID string `json:"guid"` + + // Correlation ID + CorrelationID string `json:"correlation_id"` + + // Kwargs + Kwargs string `json:"kwargs"` + + // RemoteAddr + RemoteAddr string `json:"remote_addr"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Result + Result string `json:"result"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Tags + Tags string `json:"tags"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // TimestampEnd + TimestampEnd float64 `json:"timestampEnd"` + + // User + User string `json:"user"` +} + +// Main info about audit +type ItemAudit struct { + // Args + Args string `json:"args"` + + // Call + Call string `json:"call"` + + // GUID + GUID string `json:"guid"` + + // Correlation ID + CorrelationID string `json:"correlation_id"` + + // Kwargs + Kwargs string `json:"kwargs"` + + // RemoteAddr + RemoteAddr string `json:"remote_addr"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Result + Result string `json:"result"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // Timestamp End + TimestampEnd float64 `json:"timestampEnd"` + + // User + User string `json:"user"` + + // TTL + TTL string `json:"_ttl"` +} + +// List of audits +type ListAudits struct { + // Data + Data []ItemAudit `json:"data"` + + // EntryCount + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/bservice.go b/pkg/cloudapi/bservice.go new file mode 100644 index 0000000..5d340fb --- /dev/null +++ b/pkg/cloudapi/bservice.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..951299a --- /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/v15/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..9688c32 --- /dev/null +++ b/pkg/cloudapi/bservice/create.go @@ -0,0 +1,54 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,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..8473604 --- /dev/null +++ b/pkg/cloudapi/bservice/delete.go @@ -0,0 +1,43 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 + // Default: 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..d000e4d --- /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/v15/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..2e685bd --- /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/v15/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..83d55ab --- /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/v15/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..e5b8713 --- /dev/null +++ b/pkg/cloudapi/bservice/group_add.go @@ -0,0 +1,111 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // 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"` + + // Chipset "i440fx" or "Q35 + // Default value : Q35 + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"chipset,omitempty"` + + // ID of the chosen storage policy + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,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..397a8f5 --- /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/v15/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..f600e44 --- /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/v15/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..7d82e4a --- /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/v15/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..07bf656 --- /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/v15/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..1fa5444 --- /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/v15/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..636e605 --- /dev/null +++ b/pkg/cloudapi/bservice/group_resize.go @@ -0,0 +1,59 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Chipset for new computes, either i440fx or Q35 (i440fx by default) + // Available values : i440fx, Q35 + // Default value : Q35 + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Either delta or absolute value of computes + // Should be one of: + // - ABSOLUTE + // - RELATIVE + // Required: false + Mode string `url:"mode,omitempty" json:"mode,omitempty" validate:"omitempty,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..d50d9e1 --- /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/v15/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..4f93467 --- /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/v15/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..ca282a3 --- /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/v15/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..c229f83 --- /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/v15/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..c2a5c52 --- /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/v15/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..072bb2a --- /dev/null +++ b/pkg/cloudapi/bservice/list.go @@ -0,0 +1,92 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..3e67bed --- /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/v15/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/migrate_to_zone.go b/pkg/cloudapi/bservice/migrate_to_zone.go new file mode 100644 index 0000000..09a3b35 --- /dev/null +++ b/pkg/cloudapi/bservice/migrate_to_zone.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move basic service to another zone +type MigrateToZoneRequest struct { + // ID of the BasicService to move + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zoneId" json:"zoneId" validate:"required"` +} + +// MigrateToZone moves basic service instance to new zone +func (b BService) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/bservice/migrateToZone" + + 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/models.go b/pkg/cloudapi/bservice/models.go new file mode 100644 index 0000000..e59041b --- /dev/null +++ b/pkg/cloudapi/bservice/models.go @@ -0,0 +1,398 @@ +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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // NodeID + NodeID uint64 `json:"node_id"` + + // 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"` + + //Chipset + Chipset string `json:"chipset"` +} + +// 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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..defc294 --- /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/v15/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..5b0a8fc --- /dev/null +++ b/pkg/cloudapi/bservice/serialize.go @@ -0,0 +1,43 @@ +package bservice + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..c9a388e --- /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/v15/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..5d24663 --- /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/v15/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..0833729 --- /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/v15/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..152dbc7 --- /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/v15/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..5f45816 --- /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/v15/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..1b9fca9 --- /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/v15/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..cb3b66e --- /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/v15/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..ed2e079 --- /dev/null +++ b/pkg/cloudapi/compute.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/abort_shared_snapshot_merge.go b/pkg/cloudapi/compute/abort_shared_snapshot_merge.go new file mode 100644 index 0000000..972f448 --- /dev/null +++ b/pkg/cloudapi/compute/abort_shared_snapshot_merge.go @@ -0,0 +1,68 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AbortSharedSnapshotMergeRequest struct to abort shared snapshots merge +type AbortSharedSnapshotMergeRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Label of the snapshot + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +type wrapperAbortSharedSnapshotMergeRequest struct { + AbortSharedSnapshotMergeRequest + AsyncMode bool `url:"asyncMode"` +} + +// AbortSharedSnapshotMerge shared snapshots merge abort +func (c Compute) AbortSharedSnapshotMerge(ctx context.Context, req AbortSharedSnapshotMergeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperAbortSharedSnapshotMergeRequest{ + AbortSharedSnapshotMergeRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/abort_shared_snapshot_merge" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} + +// AbortSharedSnapshotMergeAsync shared snapshots merge abort in async mode +func (c Compute) AbortSharedSnapshotMergeAsync(ctx context.Context, req AbortSharedSnapshotMergeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperAbortSharedSnapshotMergeRequest{ + AbortSharedSnapshotMergeRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/abort_shared_snapshot_merge" + + 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/affinity_group_check_start.go b/pkg/cloudapi/compute/affinity_group_check_start.go new file mode 100644 index 0000000..04b3a23 --- /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/v15/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..3fa7213 --- /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/v15/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..1f4cfbe --- /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/v15/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..06f6297 --- /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/v15/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..fc38139 --- /dev/null +++ b/pkg/cloudapi/compute/affinity_rule_add.go @@ -0,0 +1,65 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: true + Value string `url:"value" json:"value" validate:"required"` +} + +// 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..1772e94 --- /dev/null +++ b/pkg/cloudapi/compute/affinity_rule_remove.go @@ -0,0 +1,65 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: true + Value string `url:"value" json:"value" validate:"required"` +} + +// 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..f878028 --- /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/v15/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..fce86dc --- /dev/null +++ b/pkg/cloudapi/compute/anti_affinity_rule_add.go @@ -0,0 +1,65 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: true + Value string `url:"value" json:"value" validate:"required"` +} + +// 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..6c2e288 --- /dev/null +++ b/pkg/cloudapi/compute/anti_affinity_rule_remove.go @@ -0,0 +1,65 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: true + Value string `url:"value" json:"value" validate:"required"` +} + +// 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..d691ec2 --- /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/v15/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..c29c774 --- /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/v15/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..6211890 --- /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/v15/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..3e6c789 --- /dev/null +++ b/pkg/cloudapi/compute/audits.go @@ -0,0 +1,76 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AuditsRequest struct to get audit records +type AuditsRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Find all audits after point in time + // Required: false + TimestampAT uint64 `url:"timestamp_at,omitempty" json:"timestamp_at,omitempty"` + + // Find all audits before point in time + // Required: false + TimestampTO uint64 `url:"timestamp_to,omitempty" json:"timestamp_to,omitempty"` + + // Find by user + // Required: false + User string `url:"user,omitempty" json:"user,omitempty"` + + // Find by api endpoints + // Required: false + Call string `url:"call,omitempty" json:"call,omitempty"` + + // Sort by one of supported fields, format ± + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,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"` + + // Find by HTTP min status code + // Required: false + MinStatusCode uint64 `url:"min_status_code,omitempty" json:"min_status_code,omitempty"` + + // Find by HTTP max status code + // Required: false + MaxStatusCode uint64 `url:"max_status_code,omitempty" json:"max_status_code,omitempty"` +} + +// 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.MethodGet, 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..8b4d729 --- /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/v15/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..6bfb9a7 --- /dev/null +++ b/pkg/cloudapi/compute/boot_order_get.go @@ -0,0 +1,39 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..bae4bba --- /dev/null +++ b/pkg/cloudapi/compute/boot_order_set.go @@ -0,0 +1,47 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..b3a24e4 --- /dev/null +++ b/pkg/cloudapi/compute/cd_eject.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperCDEjectRequest struct { + CDEjectRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperCDEjectRequest{ + CDEjectRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/cdEject" + + res, err := c.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 +} + +// CDEjectAsync ejects CD image to compute's CD-ROM with AsyncMode +func (c Compute) CDEjectAsync(ctx context.Context, req CDEjectRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCDEjectRequest{ + CDEjectRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/cdEject" + + 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/cd_insert.go b/pkg/cloudapi/compute/cd_insert.go new file mode 100644 index 0000000..54919ad --- /dev/null +++ b/pkg/cloudapi/compute/cd_insert.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperCDInsertRequest struct { + CDInsertRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperCDInsertRequest{ + CDInsertRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/cdInsert" + + res, err := c.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 +} + +// CDInsertAsync inserts new CD image to compute's CD-ROM with AsyncMode +func (c Compute) CDInsertAsync(ctx context.Context, req CDInsertRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCDInsertRequest{ + CDInsertRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/cdInsert" + + 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/change_ip.go b/pkg/cloudapi/compute/change_ip.go new file mode 100644 index 0000000..732ce82 --- /dev/null +++ b/pkg/cloudapi/compute/change_ip.go @@ -0,0 +1,87 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeIPRequest struct to change IP for network +type ChangeIPRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Network type + // 'EXTNET' for connect to external network directly + // 'VINS' for connect to ViNS + // Required: true + NetType string `url:"net_type" json:"net_type" validate:"computeNetType"` + + // Network ID for connect to + // For EXTNET - external network ID + // For VINS - VINS ID + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // IP address to which we will change the existing one, it must be from the same subnet + // Required: true + IPAddr string `url:"ip_addr" json:"ip_addr" validate:"required"` +} + +type wrapperChangeIPRequest struct { + ChangeIPRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperChangeIPRequest{ + ChangeIPRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/changeIp" + + res, err := c.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 +} + +// ChangeIPAsync change reserved IP for compute instance with AsyncMode +func (c Compute) ChangeIPAsync(ctx context.Context, req ChangeIPRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeIPRequest{ + ChangeIPRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/changeIp" + + 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/change_link_state.go b/pkg/cloudapi/compute/change_link_state.go new file mode 100644 index 0000000..62ee51a --- /dev/null +++ b/pkg/cloudapi/compute/change_link_state.go @@ -0,0 +1,80 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperChangeLinkStateRequest struct { + ChangeLinkStateRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperChangeLinkStateRequest{ + ChangeLinkStateRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/changeLinkState" + + res, err := c.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 +} + +// ChangeLinkStateAsync changes the status link virtual of compute with AsyncMode +func (c Compute) ChangeLinkStateAsync(ctx context.Context, req ChangeLinkStateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeLinkStateRequest{ + ChangeLinkStateRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/changeLinkState" + + 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/change_mac.go b/pkg/cloudapi/compute/change_mac.go new file mode 100644 index 0000000..021bd14 --- /dev/null +++ b/pkg/cloudapi/compute/change_mac.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeMACRequest struct to change MAC for network +type ChangeMACRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Current mac address + // Required: true + СurrentMAC string `url:"current_mac_address" json:"current_mac_address" validate:"required"` + + // the MAC address to which we will change the existing one + // Required: true + NewMAC string `url:"new_mac_address" json:"new_mac_address" validate:"required"` +} + +// ChangeMAC change MAC for compute instance +func (c Compute) ChangeMAC(ctx context.Context, req ChangeMACRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/changeMac" + + 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_mtu.go b/pkg/cloudapi/compute/change_mtu.go new file mode 100644 index 0000000..fffba84 --- /dev/null +++ b/pkg/cloudapi/compute/change_mtu.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeMTURequest struct to change MTU for a compute +type ChangeMTURequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Interface name or MAC address + // Required: true + Interface string `url:"interface" json:"interface" validate:"required"` + + // Maximum transmission unit + // Required: true + MTU uint64 `url:"mtu" json:"mtu" validate:"required" validate:"omitempty,mtu"` +} + +type wrapperChangeMTURequest struct { + ChangeMTURequest + + AsyncMode bool `url:"asyncMode"` +} + +// ChangeMTU change MTU for compute instance +func (c Compute) ChangeMTU(ctx context.Context, req ChangeMTURequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeMTURequest{ + ChangeMTURequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/change_mtu" + + res, err := c.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 +} + +// ChangeMTUAsync change MTU for compute instance with AsyncMode +func (c Compute) ChangeMTUAsync(ctx context.Context, req ChangeMTURequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeMTURequest{ + ChangeMTURequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/change_mtu" + + 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/change_secutity_group.go b/pkg/cloudapi/compute/change_secutity_group.go new file mode 100644 index 0000000..f8b3db6 --- /dev/null +++ b/pkg/cloudapi/compute/change_secutity_group.go @@ -0,0 +1,83 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeSecGroupsRequest struct to change security groups for compute +type ChangeSecGroupsRequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Interface name or MAC address + // Required: true + Interface string `url:"interface" json:"interface" validate:"required"` + + // List of security group IDs to assign to this interface + // Required: false + SecGroups []uint64 `url:"security_groups,omitempty" json:"security_groups,omitempty"` + + // Flag indicating whether security groups are enabled for this interface + // Required: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +type wrapperChangeSecGroupsRequest struct { + ChangeSecGroupsRequest + + AsyncMode bool `url:"asyncMode"` +} + +// ChangeSecGroups changes security groups for compute +func (c Compute) ChangeSecGroups(ctx context.Context, req ChangeSecGroupsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeSecGroupsRequest{ + ChangeSecGroupsRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/change_security_groups" + + res, err := c.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 +} + +// ChangeSecGroupsAsync changes security groups for compute with AsyncMode +func (c Compute) ChangeSecGroupsAsync(ctx context.Context, req ChangeSecGroupsRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeSecGroupsRequest{ + ChangeSecGroupsRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/change_security_groups" + + 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/clone.go b/pkg/cloudapi/compute/clone.go new file mode 100644 index 0000000..ad8f8a2 --- /dev/null +++ b/pkg/cloudapi/compute/clone.go @@ -0,0 +1,95 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // ID of the Storage Policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // The name of the pool to migrate disks to + // Required: false + PoolName string `url:"pool_name" json:"pool_name"` + + // The ID of the SEP to migrate disks to + // Required: false + SEPID uint64 `url:"sep_id" json:"sep_id"` +} + +type wrapperCloneRequest struct { + CloneRequest + + AsyncMode bool `url:"asyncMode" json:"asyncMode"` +} + +// Clone clones compute instance +func (c Compute) Clone(ctx context.Context, req CloneRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCloneRequest{ + CloneRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/clone" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} + +// CloneAsync clones compute instance with AsyncMode +func (c Compute) CloneAsync(ctx context.Context, req CloneRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCloneRequest{ + CloneRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/clone" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudapi/compute/clone_abort.go b/pkg/cloudapi/compute/clone_abort.go new file mode 100644 index 0000000..a282c1d --- /dev/null +++ b/pkg/cloudapi/compute/clone_abort.go @@ -0,0 +1,41 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CloneAbortRequest struct to abort a compute clone +type CloneAbortRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// CloneAbort aborts a compute clone +func (c Compute) CloneAbort(ctx context.Context, req CloneAbortRequest) (ListCloneAbort, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/clone_abort" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + var result ListCloneAbort + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/pkg/cloudapi/compute/clone_status.go b/pkg/cloudapi/compute/clone_status.go new file mode 100644 index 0000000..d93c394 --- /dev/null +++ b/pkg/cloudapi/compute/clone_status.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetCloneStatusRequest struct to get information about compute clone status +type GetCloneStatusRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// GetCloneStatus gets information about compute clone status as a RecordCloneStatus struct +func (c Compute) GetCloneStatus(ctx context.Context, req GetCloneStatusRequest) ([]RecordCloneStatus, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/clone_status" + + res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return nil, err + } + + cloneStatus := make([]RecordCloneStatus, 0) + + err = json.Unmarshal(res, &cloneStatus) + if err != nil { + return nil, err + } + + return cloneStatus, nil +} diff --git a/pkg/cloudapi/compute/compute.go b/pkg/cloudapi/compute/compute.go new file mode 100644 index 0000000..ebc61db --- /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/v15/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..ca484e6 --- /dev/null +++ b/pkg/cloudapi/compute/create_template.go @@ -0,0 +1,45 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/createTemplate" + + 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/create_template_from_blank.go b/pkg/cloudapi/compute/create_template_from_blank.go new file mode 100644 index 0000000..6630204 --- /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/v15/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"` + + // Storage policy id of disk. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // 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"` + + // 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..419a3e2 --- /dev/null +++ b/pkg/cloudapi/compute/delete.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDeleteRequest struct { + DeleteRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDeleteRequest{ + DeleteRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/delete" + + res, err := c.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 +} + +// DeleteAsync deletes compute with AsyncMode +func (c Compute) DeleteAsync(ctx context.Context, req DeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDeleteRequest{ + DeleteRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/delete" + + 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/delete_cpu_alignment_profile.go b/pkg/cloudapi/compute/delete_cpu_alignment_profile.go new file mode 100644 index 0000000..152fe97 --- /dev/null +++ b/pkg/cloudapi/compute/delete_cpu_alignment_profile.go @@ -0,0 +1,39 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteCPUAlignmentProfileRequest struct to delete CPU alignment profile for computes +type DeleteCPUAlignmentProfileRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"compute_ids" json:"compute_ids" validate:"min=1"` +} + +// DeleteCPUAlignmentProfile deletes CPU alignment profile for computes +func (c Compute) DeleteCPUAlignmentProfile(ctx context.Context, req DeleteCPUAlignmentProfileRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/delete_cpu_alignment_profile" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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..bdbeada --- /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/v15/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..623c31d --- /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/v15/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..7be3ed9 --- /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/v15/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..883d987 --- /dev/null +++ b/pkg/cloudapi/compute/disable.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DisableRequest struct to disable compute +type DisableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperDisableRequest struct { + DisableRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDisableRequest{ + DisableRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/disable" + + res, err := c.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 +} + +// DisableAsync disables compute with AsyncMode +func (c Compute) DisableAsync(ctx context.Context, req DisableRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDisableRequest{ + DisableRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/disable" + + 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/disk_add.go b/pkg/cloudapi/compute/disk_add.go new file mode 100644 index 0000000..0fd12ee --- /dev/null +++ b/pkg/cloudapi/compute/disk_add.go @@ -0,0 +1,113 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 policy id of disk. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // Desired PCI slot (hex string, e.g. "0x1A") + // Required: false + PCISlot string `url:"pci_slot,omitempty" json:"pci_slot,omitempty"` + + // Desired bus number (hex string, e.g. "0x03") + // Required: false + BusNumber string `url:"bus_number,omitempty" json:"bus_number,omitempty"` + + // Mount disk in read-only mode + // Required: false + ReadOnly bool `url:"read_only,omitempty" json:"read_only,omitempty"` +} + +type wrapperDiskAddRequest struct { + DiskAddRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskAddRequest{ + DiskAddRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/diskAdd" + + 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 +} + +// DiskAddAsync creates new disk and attach to compute with AsyncMode +func (c Compute) DiskAddAsync(ctx context.Context, req DiskAddRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskAddRequest{ + DiskAddRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/diskAdd" + + 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/disk_attach.go b/pkg/cloudapi/compute/disk_attach.go new file mode 100644 index 0000000..9e776d6 --- /dev/null +++ b/pkg/cloudapi/compute/disk_attach.go @@ -0,0 +1,87 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Desired PCI slot (hex string, e.g. "0x1A") + // Required: false + PCISlot string `url:"pci_slot,omitempty" json:"pci_slot,omitempty"` + + // Desired bus number (hex string, e.g. "0x03") + // Required: false + BusNumber string `url:"bus_number,omitempty" json:"bus_number,omitempty"` + + // Mount disk in read-only mode + // Required: false + ReadOnly bool `url:"read_only,omitempty" json:"read_only,omitempty"` +} + +type wrapperDiskAttachRequest struct { + DiskAttachRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskAttachRequest{ + DiskAttachRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/diskAttach" + + res, err := c.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 +} + +// DiskAttachAsync attaches disk to compute with AsyncMode +func (c Compute) DiskAttachAsync(ctx context.Context, req DiskAttachRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskAttachRequest{ + DiskAttachRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/diskAttach" + + 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/disk_del.go b/pkg/cloudapi/compute/disk_del.go new file mode 100644 index 0000000..ab8c648 --- /dev/null +++ b/pkg/cloudapi/compute/disk_del.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskDelRequest struct { + DiskDelRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskDelRequest{ + DiskDelRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/diskDel" + + res, err := c.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 +} + +// DiskDelAsync deletes disk and detaches from compute with AsyncMode +func (c Compute) DiskDelAsync(ctx context.Context, req DiskDelRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskDelRequest{ + DiskDelRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/diskDel" + + 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/disk_detach.go b/pkg/cloudapi/compute/disk_detach.go new file mode 100644 index 0000000..7e0692b --- /dev/null +++ b/pkg/cloudapi/compute/disk_detach.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskDetachRequest struct { + DiskDetachRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskDetachRequest{ + DiskDetachRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/diskDetach" + + res, err := c.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 +} + +// DiskDetachAsync detaches disk from compute with AsyncMode +func (c Compute) DiskDetachAsync(ctx context.Context, req DiskDetachRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskDetachRequest{ + DiskDetachRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/diskDetach" + + 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/disk_migrate.go b/pkg/cloudapi/compute/disk_migrate.go new file mode 100644 index 0000000..ca9200b --- /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/v15/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..bff89ed --- /dev/null +++ b/pkg/cloudapi/compute/disk_qos.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskQOSRequest struct { + DiskQOSRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskQOSRequest{ + DiskQOSRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/diskQos" + + res, err := c.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 +} + +// DiskQOSAsync changes QoS of the disk with AsyncMode +func (c Compute) DiskQOSAsync(ctx context.Context, req DiskQOSRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskQOSRequest{ + DiskQOSRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/diskQos" + + 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/disk_resize.go b/pkg/cloudapi/compute/disk_resize.go new file mode 100644 index 0000000..b0b4bb6 --- /dev/null +++ b/pkg/cloudapi/compute/disk_resize.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskResizeRequest struct { + DiskResizeRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskResizeRequest{ + DiskResizeRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/diskResize" + + res, err := c.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 +} + +// DiskResizeAsync changes disk size with AsyncMode +func (c Compute) DiskResizeAsync(ctx context.Context, req DiskResizeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskResizeRequest{ + DiskResizeRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/diskResize" + + 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/disk_switch_to_replication.go b/pkg/cloudapi/compute/disk_switch_to_replication.go new file mode 100644 index 0000000..92d4ae3 --- /dev/null +++ b/pkg/cloudapi/compute/disk_switch_to_replication.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskSwitchToReplicationRequest struct { + DiskSwitchToReplicationRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskSwitchToReplicationRequest{ + DiskSwitchToReplicationRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/diskSwitchToReplication" + + res, err := c.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 +} + +// DiskSwitchToReplicationAsync switches disk to it's replication with AsyncMode +func (c Compute) DiskSwitchToReplicationAsync(ctx context.Context, req DiskSwitchToReplicationRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskSwitchToReplicationRequest{ + DiskSwitchToReplicationRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/diskSwitchToReplication" + + 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/enable.go b/pkg/cloudapi/compute/enable.go new file mode 100644 index 0000000..6e685a9 --- /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/v15/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..316cae1 --- /dev/null +++ b/pkg/cloudapi/compute/filter.go @@ -0,0 +1,170 @@ +package compute + +import ( + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/k8s" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..12a1ff6 --- /dev/null +++ b/pkg/cloudapi/compute/filter_test.go @@ -0,0 +1,240 @@ +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, + }, + }, + Driver: "KVM_X86", + GID: 212, + GUID: 48500, + ID: 48500, + Interfaces: []ItemVNFInterface{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363500, + Name: "test", + PinnedToNode: true, + 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, + ZoneID: 1, + }, + { + 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, + }, + }, + Driver: "KVM_X86", + GID: 212, + GUID: 48556, + ID: 48556, + Interfaces: []ItemVNFInterface{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363853, + Name: "compute_2", + RAM: 4096, + ReferenceID: "a542c449-5b1c-4f90-88c5-7bb5f8ae68ff", + Registered: true, + PinnedToNode: 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, + ZoneID: 5, + }, + }, + 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..cae9c03 --- /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/v15/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..55c5341 --- /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/v15/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..6ccf18a --- /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/v15/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_cpu_alignment_profile.go b/pkg/cloudapi/compute/get_cpu_alignment_profile.go new file mode 100644 index 0000000..6f9ee3b --- /dev/null +++ b/pkg/cloudapi/compute/get_cpu_alignment_profile.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetCPUAlignmentProfileRequest struct to get CPU alignment profile for compute +type GetCPUAlignmentProfileRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// GetCPUAlignmentProfile gets CPU alignment profile for compute +func (c Compute) GetCPUAlignmentProfile(ctx context.Context, req GetCPUAlignmentProfileRequest) (*CPUAlignmentProfile, error) { + res, err := c.GetCPUAlignmentProfileRaw(ctx, req) + if err != nil { + return nil, err + } + + info := CPUAlignmentProfile{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetCPUAlignmentProfileRaw gets CPU alignment profile for compute as an array of bytes +func (c Compute) GetCPUAlignmentProfileRaw(ctx context.Context, req GetCPUAlignmentProfileRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/get_cpu_alignment_profile" + + res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudapi/compute/get_custom_fields.go b/pkg/cloudapi/compute/get_custom_fields.go new file mode 100644 index 0000000..38c4b96 --- /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/v15/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..c025477 --- /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/v15/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.MethodGet, 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/guest_agent_disable.go b/pkg/cloudapi/compute/guest_agent_disable.go new file mode 100644 index 0000000..121e090 --- /dev/null +++ b/pkg/cloudapi/compute/guest_agent_disable.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentDisableRequest struct to disable guest agent +type GuestAgentDisableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +type wrapperGuestAgentDisableRequest struct { + GuestAgentDisableRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Disable guest agent at a specific compute +func (c Compute) GuestAgentDisable(ctx context.Context, req GuestAgentDisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentDisableRequest{ + GuestAgentDisableRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/guest_agent_disable" + + res, err := c.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 +} + +// GuestAgentDisableAsync disables guest agent at a specific compute with AsyncMode +func (c Compute) GuestAgentDisableAsync(ctx context.Context, req GuestAgentDisableRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentDisableRequest{ + GuestAgentDisableRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/guest_agent_disable" + + 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/guest_agent_enable.go b/pkg/cloudapi/compute/guest_agent_enable.go new file mode 100644 index 0000000..df91d1e --- /dev/null +++ b/pkg/cloudapi/compute/guest_agent_enable.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentEnableRequest struct to enable guest agent +type GuestAgentEnableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +type wrapperGuestAgentEnableRequest struct { + GuestAgentEnableRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Enable guest agent at a specific compute +func (c Compute) GuestAgentEnable(ctx context.Context, req GuestAgentEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentEnableRequest{ + GuestAgentEnableRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/guest_agent_enable" + + res, err := c.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 +} + +// GuestAgentEnableAsync enables guest agent at a specific compute with AsyncMode +func (c Compute) GuestAgentEnableAsync(ctx context.Context, req GuestAgentEnableRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentEnableRequest{ + GuestAgentEnableRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/guest_agent_enable" + + 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/guest_agent_execute.go b/pkg/cloudapi/compute/guest_agent_execute.go new file mode 100644 index 0000000..7d4168a --- /dev/null +++ b/pkg/cloudapi/compute/guest_agent_execute.go @@ -0,0 +1,81 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentExecuteRequest struct to execute command from user to agent +type GuestAgentExecuteRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Custom command from user to agent + // Required: true + Command string `url:"command" json:"command" validate:"required"` + + // Arguments to command + // Required: true + Arguments string `url:"arguments" json:"arguments" validate:"required"` +} + +type wrapperGuestAgentExecuteRequest struct { + GuestAgentExecuteRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Execute guest agent command +func (c Compute) GuestAgentExecuteRequest(ctx context.Context, req GuestAgentExecuteRequest) (map[string]interface{}, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentExecuteRequest{ + GuestAgentExecuteRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/guest_agent_execute" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + var result map[string]interface{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// GuestAgentExecuteRequestAsync executes guest agent command with AsyncMode +func (c Compute) GuestAgentExecuteRequestAsync(ctx context.Context, req GuestAgentExecuteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentExecuteRequest{ + GuestAgentExecuteRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/guest_agent_execute" + + 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/guest_agent_feature_get.go b/pkg/cloudapi/compute/guest_agent_feature_get.go new file mode 100644 index 0000000..093f232 --- /dev/null +++ b/pkg/cloudapi/compute/guest_agent_feature_get.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentFeatureGetRequest struct to feature get guest agent +type GuestAgentFeatureGetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// List of features +func (c Compute) GuestAgentFeatureGet(ctx context.Context, req GuestAgentFeatureGetRequest) ([]string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/guest_agent_feature_get" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + features := make([]string, 0) + + err = json.Unmarshal(res, &features) + if err != nil { + return nil, err + } + + return features, nil +} diff --git a/pkg/cloudapi/compute/guest_agent_feature_update.go b/pkg/cloudapi/compute/guest_agent_feature_update.go new file mode 100644 index 0000000..2a96e1b --- /dev/null +++ b/pkg/cloudapi/compute/guest_agent_feature_update.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentFeatureUpdateRequest struct to feature update guest agent +type GuestAgentFeatureUpdateRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +type wrapperGuestAgentFeatureUpdateRequest struct { + GuestAgentFeatureUpdateRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Feature update guest agent +func (c Compute) GuestAgentFeatureUpdate(ctx context.Context, req GuestAgentFeatureUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentFeatureUpdateRequest{ + GuestAgentFeatureUpdateRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/guest_agent_feature_update" + + res, err := c.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 +} + +// GuestAgentFeatureUpdateAsync feature updates guest agent with AsyncMode +func (c Compute) GuestAgentFeatureUpdateAsync(ctx context.Context, req GuestAgentFeatureUpdateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentFeatureUpdateRequest{ + GuestAgentFeatureUpdateRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/guest_agent_feature_update" + + 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/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..eb96901 --- /dev/null +++ b/pkg/cloudapi/compute/list.go @@ -0,0 +1,105 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..04c225d --- /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/v15/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..ffa65d9 --- /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/v15/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..7fac32f --- /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/v15/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/migrate_to_zone.go b/pkg/cloudapi/compute/migrate_to_zone.go new file mode 100644 index 0000000..d951825 --- /dev/null +++ b/pkg/cloudapi/compute/migrate_to_zone.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToRGZone struct to move compute to another zone +type MigrateToZoneRequest struct { + // ID of the compute instance to move + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zoneId" json:"zoneId " validate:"required"` +} + +type wrapperMigrateToZoneRequest struct { + MigrateToZoneRequest + + AsyncMode bool `url:"asyncMode"` +} + +// MoveToRG moves compute instance to new resource group +func (c Compute) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateToZoneRequest{ + MigrateToZoneRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/migrateToZone" + + res, err := c.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 +} + +// MigrateToZoneAsync moves compute to another zone with AsyncMode +func (c Compute) MigrateToZoneAsync(ctx context.Context, req MigrateToZoneRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateToZoneRequest{ + MigrateToZoneRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/migrateToZone" + + 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/models.go b/pkg/cloudapi/compute/models.go new file mode 100644 index 0000000..656e137 --- /dev/null +++ b/pkg/cloudapi/compute/models.go @@ -0,0 +1,1461 @@ +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 { + // Compute info + Compute RecordCompute `json:"compute"` + + // List of disk IDs + Disks []uint64 `json:"disks"` + + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Memory dump image ID + MemoryDumpImage uint64 `json:"memory_dump_image"` + + // 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 { + // 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"` + + // Enable security groups + EnableSecGroups bool `json:"enable_secgroups"` + + // FLIPGroup ID + FLIPGroupID uint64 `json:"flipgroupId"` + + // GUID + GUID string `json:"guid"` + + // IP address + IPAddress string `json:"ipAddress"` + + // Libvirt Settings + LibvirtSettings LibvirtSettings `json:"libvirtSettings"` + + // Listen SSH + ListenSSH bool `json:"listenSsh"` + + // 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"` + + // Node id + NodeID int `json:"nodeId"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // SDN interface ID + SDNInterfaceID string `json:"sdn_interface_id"` + + // List of security groups + SecurityGroups []uint64 `json:"security_groups"` + + // QOS + QOS QOS `json:"qos"` + + // Target + Target string `json:"target"` + + // Type + Type string `json:"type"` + + // List of trunk tags + TrunkTags []uint64 `json:"trunk_tags"` + + // List VNF IDs + VNFs []uint64 `json:"vnfs"` +} + +// 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 struct { + // Data + Data []ItemAudit `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// 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"` + + // Auto start when node restarted + AutoStart bool `json:"autoStart"` + + // Architecture + Architecture string `json:"arch"` + + // Boot image ID + BootImageID uint64 `json:"boot_image_id"` + + // Boot order + BootOrder []string `json:"bootOrder"` + + // Boot type + BootType string `json:"bootType"` + + // 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"` + + // Clock + Clock string `json:"clock"` + + // 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"` + + // Hot resize + HotResize bool `json:"hotResize"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Image name + ImageName string `json:"imageName"` + + // List interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Loader meta iso information + LoaderMetaIso LoaderMetaIso `json:"loaderMetaIso"` + + // Live migration job ID + LiveMigrationJobID uint64 `json:"live_migration_job_id"` + + // Loader type + LoaderType string `json:"loaderType"` + + // 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"` + + // Network interface naming + NetworkInterfaceNaming string `json:"networkInterfaceNaming"` + + // 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"` + + // Name of OS + OSVersion string `json:"os_version"` + + // List OS Users + OSUsers ListOSUser `json:"osUsers"` + + // Pinned to node + PinnedToNode bool `json:"pinnedToNode"` + + // PreferredCPU + PreferredCPU []int64 `json:"preferredCpu"` + + // CPU alignment profile + CPUAlignmentProfile CPUAlignmentProfile `json:"cpu_alignment_profile"` + + // Qemu_quest + QemuQuest QemuQuest `json:"qemu_guest"` + + // ReadOnly indicates read-only mode state + ReadOnly bool `json:"read_only"` + + // 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"` + + // vGPUs list + VGPUs []VGPUItem `json:"vgpus"` + + // VNC password + VNCPassword string `json:"vncPasswd"` + + // Weight + Weight uint64 `json:"weight"` + + // Zone ID + // Required: false + ZoneID uint64 `json:"zoneId"` +} + +type LoaderMetaIso struct { + // Name + DeviceName string `json:"devicename"` + + // Path + Path string `json:"path"` +} + +type QemuQuest struct { + Enabled bool `json:"enabled"` + EnabledAgentFeatures []string `json:"enabled_agent_features"` + GUID string `json:"guid"` + LastUpdate uint64 `json:"last_update"` + User string `json:"user"` +} + +type CPUAlignmentProfile struct { + Model string `json:"model"` + Name string `json:"name"` + Vendor string `json:"vendor"` +} + +type VGPUItem struct { + // ID + ID uint64 `json:"id"` + + // GID + GID uint64 `json:"gid"` + + // Type + Type string `json:"type"` + + // Mode + Mode string `json:"mode"` + + // Status + Status string `json:"status"` + + // ProfileID + ProfileID uint64 `json:"profileId"` + + // RAM + RAM uint64 `json:"ram"` + + // LastUpdateTime + LastUpdateTime uint64 `json:"lastUpdateTime"` + + // CreatedTime + CreatedTime uint64 `json:"createdTime"` + + // DeletedTime + DeletedTime uint64 `json:"deletedTime"` + + // VMID + VMID uint64 `json:"vmid"` + + // PGPuid + PGPuid uint64 `json:"pgpuid"` + + // ReferenceID + ReferenceID string `json:"referenceId"` + + // AccountID + AccountID uint64 `json:"accountId"` + + // RgID + RgID uint64 `json:"rgId"` + + // LastClaimedBy + LastClaimedBy uint64 `json:"lastClaimedBy"` + + // PCISlot + PCISlot uint64 `json:"pciSlot"` + + // BusNumber + BusNumber uint64 `json:"bus_number"` + + // GUID + GUID uint64 `json:"guid"` +} + +// 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 { + // Compute info + Compute RecordCompute `json:"compute"` + + // List disk ID + Disks []uint64 `json:"disks"` + + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Memory dump image ID + MemoryDumpImage uint64 `json:"memory_dump_image"` + + // 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"` + + // Enable security groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // List of security groups + SecGroups []uint64 `json:"security_groups"` + + // SDN interface ID + SDNInterfaceID string `json:"sdn_interface_id"` + + // Target + Target string `json:"target"` + + // Trunk tags + TrunkTags string `json:"trunk_tags"` + + // 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"` + + // Discard + Discard string `json:"discard"` + + // Block Size + BlockSize string `json:"block_size"` + + // Boot partition + BootPartition uint64 `json:"bootPartition"` + + // Bus number + BusNumber uint64 `json:"bus_number"` + + // Chache + Cache string `json:"cache"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Device name + DeviceName string `json:"devicename"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // 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"` + + // Independent + Independent bool `json:"independent"` + + // 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"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // Password + Passwd string `json:"passwd"` + + // PCI slot + PCISlot int64 `json:"pci_slot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo map[string]uint64 `json:"presentTo"` + + // Provision + Provision string `json:"provision"` + + // 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 available + SizeAvailable float64 `json:"sizeAvailable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + //Size used + SizeUsed float64 `json:"sizeUsed"` + + // List extend snapshots + Snapshots SnapshotExtendList `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Storage policy id of compute. + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // Type + Type string `json:"type"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // UpdatedTime + UpdatedTime uint64 `json:"updatedTime"` + + // Read-only + ReadOnly bool `json:"read_only"` +} + +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"` + + // Auto start when node restarted + AutoStart bool `json:"autoStart"` + + // Architecture + Architecture string `json:"arch"` + + // Boot image ID + BootImageID uint64 `json:"boot_image_id"` + + // Boot order + BootOrder []string `json:"bootOrder"` + + // Boot type + BootType string `json:"bootType"` + + // 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"` + + // Clock + Clock string `json:"clock"` + + // 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"` + + // Hot resize + HotResize bool `json:"hotResize"` + + // HPBacked + HPBacked bool `json:"hpBacked"` + + // ID + ID uint64 `json:"id"` + + // List interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Live migration job ID + LiveMigrationJobID uint64 `json:"live_migration_job_id"` + + // Loader type + LoaderType string `json:"loaderType"` + + // 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"` + + // network interface naming + NetworkInterfaceNaming string `json:"networkInterfaceNaming"` + + // Numa Affinity + NumaAffinity string `json:"numaAffinity"` + + //NumaNodeId + NumaNodeId int64 `json:"numaNodeId"` + + // Pinned to node + PinnedToNode bool `json:"pinnedToNode"` + + // PreferredCPU + PreferredCPU []int64 `json:"preferredCpu"` + + // Number of RAM + RAM uint64 `json:"ram"` + + // Name of OS + OSVersion string `json:"os_version"` + + // CPU alignment profile + CPUAlignmentProfile CPUAlignmentProfile `json:"cpu_alignment_profile"` + + // Qemu_quest + QemuQuest QemuQuest `json:"qemu_guest"` + + // ReadOnly indicates read-only mode state + ReadOnly bool `json:"read_only"` + + // 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"` + + // Weight + Weight uint64 `json:"weight"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// ListInfoDisks +type ListInfoDisks []InfoDisk + +// Information Disk +type InfoDisk struct { + // ID + ID uint64 `json:"id"` + + // SEP ID + SepID int64 `json:"sepId"` + + // Read-only + ReadOnly bool `json:"read_only"` +} + +// 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"` + + // Node ID + NodeID uint64 `json:"nodeId"` + + // 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"` +} + +type RecordCloneStatus struct { + // Disk ID + DiskID int `json:"disk_id"` + + // Clone Status + Status CloneStatus `json:"status"` +} + +// Information about aborted clone disk +type RecordCloneAbort struct { + // Disk ID + DiskID uint64 `json:"disk_id"` + + // Aborted + Aborted bool `json:"aborted"` + + // Blockcopy abort job ID + BlockcopyAbortJobID string `json:"blockcopy_abort_job_id"` +} + +// List of aborted clone disks +type ListCloneAbort []RecordCloneAbort + +type CloneStatus struct { + // Type + Type int `json:"type"` + + // Copy speed + Bandwidth int `json:"bandwidth"` + + // Current progress + Cur int `json:"cur"` + + // Total size + End int `json:"end"` + + // Operation status + Ready bool `json:"ready"` + + // Progress percent + ProgressPercent int `json:"progress_percent"` +} diff --git a/pkg/cloudapi/compute/move_to_rg.go b/pkg/cloudapi/compute/move_to_rg.go new file mode 100644 index 0000000..e9f320a --- /dev/null +++ b/pkg/cloudapi/compute/move_to_rg.go @@ -0,0 +1,90 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperMoveToRGRequest struct { + MoveToRGRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperMoveToRGRequest{ + MoveToRGRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/moveToRg" + + res, err := c.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 +} + +// MoveToRGAsync moves compute instance to new resource group with AsyncMode +func (c Compute) MoveToRGAsync(ctx context.Context, req MoveToRGRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMoveToRGRequest{ + MoveToRGRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/moveToRg" + + 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/net_attach.go b/pkg/cloudapi/compute/net_attach.go new file mode 100644 index 0000000..8b3d680 --- /dev/null +++ b/pkg/cloudapi/compute/net_attach.go @@ -0,0 +1,141 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 + // `EMPTY` for connect empty network + // `SDN` for connect to SDN + // `TRUNK` for connect to TRUNK + // 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"` + + // MAC address + // Required: false + MACAddr string `url:"mac_addr,omitempty" json:"mac_addr,omitempty"` + + // Used for EXTNET, TRUNK and DPDK + // Must be 1500-9216 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` + + // Net mask + // Used only to DPDK or VFNIC net type + // Required: false + NetMask uint64 `url:"netMask,omitempty" json:"netMask,omitempty"` + + // SDN Segment ID + // Required: false + SDNSegmentID string `url:"sdn_segment_id,omitempty" json:"sdn_segment_id,omitempty"` + + // SDN Object Group IDs + // Required: false + SDNObjectGroupIDs []string `url:"sdn_object_group_ids,omitempty" json:"sdn_object_group_ids,omitempty"` + + // SDN Logical Port Display Name + // Required: false + SDNLogicalPortDisplayName string `url:"sdn_logical_port_display_name,omitempty" json:"sdn_logical_port_display_name,omitempty"` + + // SDN Logical Port Description + // Required: false + SDNLogicalPortDescription string `url:"sdn_logical_port_description,omitempty" json:"sdn_logical_port_description,omitempty"` + + // Unique identifier of logical port on SDN side + // Required: false + SDNInterfaceID string `url:"sdn_interface_id,omitempty" json:"sdn_interface_id,omitempty" validate:"omitempty"` + + // List of security group IDs to assign to this interface + // Required: false + SecGroups []uint64 `url:"security_groups,omitempty" json:"security_groups,omitempty"` + + // Flag indicating whether security groups are enabled for this interface + // Not applicable to netType VFNIC, TRUNK, or SDN + // Required: false + EnableSecGroups bool `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty"` + + // Flag indicating whether this interface is enabled (only for VINS, EXTNET, DPDK, SDN, TRUNK) + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` +} + +type wrapperNetAttachRequest struct { + NetAttachRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperNetAttachRequest{ + NetAttachRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/netAttach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + info := RecordNetAttach{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// NetAttachAsync attaches network to compute with AsyncMode +func (c Compute) NetAttachAsync(ctx context.Context, req NetAttachRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperNetAttachRequest{ + NetAttachRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/netAttach" + + 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/net_detach.go b/pkg/cloudapi/compute/net_detach.go new file mode 100644 index 0000000..1401eb4 --- /dev/null +++ b/pkg/cloudapi/compute/net_detach.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperNetDetachRequest struct { + NetDetachRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperNetDetachRequest{ + NetDetachRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/netDetach" + + res, err := c.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 +} + +// NetDetachAsync detaches network from compute with AsyncMode +func (c Compute) NetDetachAsync(ctx context.Context, req NetDetachRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperNetDetachRequest{ + NetDetachRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/netDetach" + + 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/pause.go b/pkg/cloudapi/compute/pause.go new file mode 100644 index 0000000..5d8b2e4 --- /dev/null +++ b/pkg/cloudapi/compute/pause.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// PauseRequest struct to pause compute +type PauseRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperPauseRequest struct { + PauseRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperPauseRequest{ + PauseRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/pause" + + res, err := c.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 +} + +// PauseAsync pauses compute with AsyncMode +func (c Compute) PauseAsync(ctx context.Context, req PauseRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPauseRequest{ + PauseRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/pause" + + 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/pfw_add.go b/pkg/cloudapi/compute/pfw_add.go new file mode 100644 index 0000000..ceb0b1b --- /dev/null +++ b/pkg/cloudapi/compute/pfw_add.go @@ -0,0 +1,89 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperPFWAddRequest struct { + PFWAddRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperPFWAddRequest{ + PFWAddRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/pfwAdd" + + 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 +} + +// PFWAddAsync adds port forward rule with AsyncMode +func (c Compute) PFWAddAsync(ctx context.Context, req PFWAddRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPFWAddRequest{ + PFWAddRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/pfwAdd" + + 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/pfw_del.go b/pkg/cloudapi/compute/pfw_del.go new file mode 100644 index 0000000..f4b6909 --- /dev/null +++ b/pkg/cloudapi/compute/pfw_del.go @@ -0,0 +1,92 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperPFWDelRequest struct { + PFWDelRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperPFWDelRequest{ + PFWDelRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/pfwDel" + + res, err := c.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 +} + +// PFWDelAsync deletes port forward rule with AsyncMode +func (c Compute) PFWDelAsync(ctx context.Context, req PFWDelRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPFWDelRequest{ + PFWDelRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/pfwDel" + + 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/pfw_list.go b/pkg/cloudapi/compute/pfw_list.go new file mode 100644 index 0000000..256e980 --- /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/v15/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_node.go b/pkg/cloudapi/compute/pin_to_node.go new file mode 100644 index 0000000..faf4864 --- /dev/null +++ b/pkg/cloudapi/compute/pin_to_node.go @@ -0,0 +1,76 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// PinToNodeRequest struct to pin compute to node +type PinToNodeRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Auto start when node restarted + // Required: false + // Default: false + AutoStart bool `url:"autoStart" json:"autoStart"` +} + +type wrapperPinToNodeRequest struct { + PinToNodeRequest + + AsyncMode bool `url:"asyncMode"` +} + +// PinToNode pin compute to current node +func (c Compute) PinToNode(ctx context.Context, req PinToNodeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPinToNodeRequest{ + PinToNodeRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/pin_to_node" + + res, err := c.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 +} + +// PinToNodeAsync pins compute to current node with AsyncMode +func (c Compute) PinToNodeAsync(ctx context.Context, req PinToNodeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPinToNodeRequest{ + PinToNodeRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/pin_to_node" + + 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/power_cycle.go b/pkg/cloudapi/compute/power_cycle.go new file mode 100644 index 0000000..da1cd71 --- /dev/null +++ b/pkg/cloudapi/compute/power_cycle.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperPowerCycleRequest struct { + PowerCycleRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperPowerCycleRequest{ + PowerCycleRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/powerCycle" + + res, err := c.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 +} + +// PowerCycleAsync makes force stop and start compute with AsyncMode +func (c Compute) PowerCycleAsync(ctx context.Context, req PowerCycleRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPowerCycleRequest{ + PowerCycleRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/powerCycle" + + 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/reboot.go b/pkg/cloudapi/compute/reboot.go new file mode 100644 index 0000000..e69c390 --- /dev/null +++ b/pkg/cloudapi/compute/reboot.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// RebootRequest struct to reboot compute +type RebootRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperRebootRequest struct { + RebootRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperRebootRequest{ + RebootRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/reboot" + + res, err := c.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 +} + +// RebootAsync reboots compute with AsyncMode +func (c Compute) RebootAsync(ctx context.Context, req RebootRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRebootRequest{ + RebootRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/reboot" + + 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/redeploy.go b/pkg/cloudapi/compute/redeploy.go new file mode 100644 index 0000000..45783f2 --- /dev/null +++ b/pkg/cloudapi/compute/redeploy.go @@ -0,0 +1,100 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// RedeployRequest struct to redeploy +type RedeployRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Storage policy id of compute. The rules of the specified storage policy will be used. + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,omitempty"` + + // ID of the new OS image, if image change is required + // Required: false + ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,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"` +} + +type wrapperRedeployRequest struct { + RedeployRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperRedeployRequest{ + RedeployRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/redeploy" + + res, err := c.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 +} + +// RedeployAsync redeploys compute with AsyncMode +func (c Compute) RedeployAsync(ctx context.Context, req RedeployRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRedeployRequest{ + RedeployRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/redeploy" + + 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/reset.go b/pkg/cloudapi/compute/reset.go new file mode 100644 index 0000000..059abe2 --- /dev/null +++ b/pkg/cloudapi/compute/reset.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ResetRequest struct to reset compute +type ResetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperResetRequest struct { + ResetRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperResetRequest{ + ResetRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/reset" + + res, err := c.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 +} + +// ResetAsync resets compute with AsyncMode +func (c Compute) ResetAsync(ctx context.Context, req ResetRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperResetRequest{ + ResetRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/reset" + + 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/resize.go b/pkg/cloudapi/compute/resize.go new file mode 100644 index 0000000..3cf375e --- /dev/null +++ b/pkg/cloudapi/compute/resize.go @@ -0,0 +1,99 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False + // Required: false + PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` +} + +// GetRAM returns RAM field values +func (r ResizeRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +type wrapperResizeRequest struct { + ResizeRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperResizeRequest{ + ResizeRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/resize" + + res, err := c.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 +} + +// ResizeAsync resizes compute instance with AsyncMode +func (c Compute) ResizeAsync(ctx context.Context, req ResizeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperResizeRequest{ + ResizeRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/resize" + + 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/restore.go b/pkg/cloudapi/compute/restore.go new file mode 100644 index 0000000..c32cdc1 --- /dev/null +++ b/pkg/cloudapi/compute/restore.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// RestoreRequest struct to restore compute +type RestoreRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperRestoreRequest struct { + RestoreRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperRestoreRequest{ + RestoreRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/restore" + + res, err := c.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 +} + +// RestoreAsync restores compute from recycle bin with AsyncMode +func (c Compute) RestoreAsync(ctx context.Context, req RestoreRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRestoreRequest{ + RestoreRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/restore" + + 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/resume.go b/pkg/cloudapi/compute/resume.go new file mode 100644 index 0000000..1d604e1 --- /dev/null +++ b/pkg/cloudapi/compute/resume.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ResumeRequest struct to resume compute +type ResumeRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperResumeRequest struct { + ResumeRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperResumeRequest{ + ResumeRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/resume" + + res, err := c.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 +} + +// ResumeAsync resumes Compute from paused state with AsyncMode +func (c Compute) ResumeAsync(ctx context.Context, req ResumeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperResumeRequest{ + ResumeRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/resume" + + 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/serialize.go b/pkg/cloudapi/compute/serialize.go new file mode 100644 index 0000000..2dc1ff1 --- /dev/null +++ b/pkg/cloudapi/compute/serialize.go @@ -0,0 +1,43 @@ +package compute + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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_cpu_alignment_profile.go b/pkg/cloudapi/compute/set_cpu_alignment_profile.go new file mode 100644 index 0000000..4d2e51c --- /dev/null +++ b/pkg/cloudapi/compute/set_cpu_alignment_profile.go @@ -0,0 +1,43 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// SetCPUAlignmentProfileRequest struct to set CPU alignment profile for computes +type SetCPUAlignmentProfileRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []int64 `url:"compute_ids" json:"compute_ids" validate:"min=1"` + + // CPU alignment profile name + // Required: true + CPUAlignmentProfile string `url:"cpu_alignment_profile" json:"cpu_alignment_profile" validate:"required"` +} + +// SetCPUAlignmentProfile sets CPU alignment profile for computes +func (c Compute) SetCPUAlignmentProfile(ctx context.Context, req SetCPUAlignmentProfileRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/set_cpu_alignment_profile" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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/set_custom_fields.go b/pkg/cloudapi/compute/set_custom_fields.go new file mode 100644 index 0000000..cbfdb3e --- /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/v15/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/shared_snapshot_merge_status.go b/pkg/cloudapi/compute/shared_snapshot_merge_status.go new file mode 100644 index 0000000..df00521 --- /dev/null +++ b/pkg/cloudapi/compute/shared_snapshot_merge_status.go @@ -0,0 +1,33 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// SharedSnapshotMergeStatusRequest struct to get shared snapshot merge status +type SharedSnapshotMergeStatusRequest struct { + // ID of compute instance to get log for + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// SharedSnapshotMergeStatus shared snapshots merge status +// returns a string representing either the current status or the progress percentage +func (c Compute) SharedSnapshotMergeStatus(ctx context.Context, req SharedSnapshotMergeStatusRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/shared_snapshot_merge_status" + + 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/snapshot_create.go b/pkg/cloudapi/compute/snapshot_create.go new file mode 100644 index 0000000..bf7ca5b --- /dev/null +++ b/pkg/cloudapi/compute/snapshot_create.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 + // Allowed characters: a-z, 0-9, spaces, punctuation except '<' and '>' + // Maximum length: 36 characters + // Required: true + Label string `url:"label" json:"label" validate:"required,max=36,excludesall=<>"` + + // Create snapshot with memory dump + // Required: false + // Default: false + WithMemory bool `url:"with_memory" json:"with_memory"` +} + +type wrapperSnapshotCreateRequest struct { + SnapshotCreateRequest + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperSnapshotCreateRequest{ + SnapshotCreateRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/snapshotCreate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} + +// SnapshotCreateAsync creates compute snapshot in async mode +func (c Compute) SnapshotCreateAsync(ctx context.Context, req SnapshotCreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSnapshotCreateRequest{ + SnapshotCreateRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/snapshotCreate" + + 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_delete.go b/pkg/cloudapi/compute/snapshot_delete.go new file mode 100644 index 0000000..d733081 --- /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/v15/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..efe9e40 --- /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/v15/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..63654be --- /dev/null +++ b/pkg/cloudapi/compute/snapshot_rollback.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Rollback with memory dump restore + // Required: false + // Default: true + WithMemory interface{} `url:"with_memory,omitempty" json:"with_memory,omitempty" validate:"omitempty,isBool"` +} + +type wrapperSnapshotRollbackRequest struct { + SnapshotRollbackRequest + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperSnapshotRollbackRequest{ + SnapshotRollbackRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/snapshotRollback" + + res, err := c.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 +} + +// SnapshotRollbackAsync rollbacks specified compute snapshot in async mode +func (c Compute) SnapshotRollbackAsync(ctx context.Context, req SnapshotRollbackRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSnapshotRollbackRequest{ + SnapshotRollbackRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/snapshotRollback" + + 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_usage.go b/pkg/cloudapi/compute/snapshot_usage.go new file mode 100644 index 0000000..1ede991 --- /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/v15/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..19d74e4 --- /dev/null +++ b/pkg/cloudapi/compute/start.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperStartRequest struct { + StartRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperStartRequest{ + StartRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/start" + + res, err := c.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 +} + +// StartAsync starts compute with AsyncMode +func (c Compute) StartAsync(ctx context.Context, req StartRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperStartRequest{ + StartRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/start" + + 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/stop.go b/pkg/cloudapi/compute/stop.go new file mode 100644 index 0000000..f233c82 --- /dev/null +++ b/pkg/cloudapi/compute/stop.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperStopRequest struct { + StopRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperStopRequest{ + StopRequest: req, + AsyncMode: false, + } + + url := "/cloudapi/compute/stop" + + res, err := c.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 +} + +// StopAsync stops compute with AsyncMode +func (c Compute) StopAsync(ctx context.Context, req StopRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperStopRequest{ + StopRequest: req, + AsyncMode: true, + } + + url := "/cloudapi/compute/stop" + + 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/tag_add.go b/pkg/cloudapi/compute/tag_add.go new file mode 100644 index 0000000..b911211 --- /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/v15/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..bfaeb36 --- /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/v15/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_node.go b/pkg/cloudapi/compute/unpin_from_node.go new file mode 100644 index 0000000..4af232d --- /dev/null +++ b/pkg/cloudapi/compute/unpin_from_node.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UnpinFromNodeRequest struct for unpin from node +type UnpinFromNodeRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// UnpinFromNode unpins compute from current node +func (c Compute) UnpinFromNode(ctx context.Context, req UnpinFromNodeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/compute/unpin_from_node" + + 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..71537a5 --- /dev/null +++ b/pkg/cloudapi/compute/update.go @@ -0,0 +1,99 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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, true or false + // Required: false + CPUPin interface{} `url:"cpupin,omitempty" json:"cpupin,omitempty" validate:"omitempty,isBool"` + + // 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, true or false + // Required: false + HPBacked interface{} `url:"hpBacked,omitempty" json:"hpBacked,omitempty" validate:"omitempty,isBool"` + + // Auto start when node restarted, true or false + // Required: false + AutoStart interface{} `url:"autoStart,omitempty" json:"autoStart,omitempty" validate:"omitempty,isBool"` + + // Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False + // Required: false + PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` + + // VM type linux, windows or unknown + // Required: false + LoaderType string `url:"loaderType,omitempty" json:"loaderType,omitempty" validate:"omitempty,loaderType"` + + // Boot type of image bios or uefi + // Required: false + BootType string `url:"bootType,omitempty" json:"bootType,omitempty" validate:"omitempty,imageBootType"` + + // Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming. + // Required: false + NetworkInterfaceNaming string `url:"networkInterfaceNaming,omitempty" json:"networkInterfaceNaming,omitempty" validate:"omitempty,networkInterfaceNaming"` + + // Does this machine supports hot resize, true or false + // Required: false + HotResize interface{} `url:"hotResize,omitempty" json:"hotResize,omitempty" validate:"omitempty,isBool"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,omitempty"` + + // Clock type for the VM + // Required: false + // Default: null + Clock string `url:"clock,omitempty" json:"clock,omitempty"` +} + +// 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..a826fbc --- /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/v15/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..86f1ef3 --- /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/v15/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..a3588a8 --- /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/v15/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..cc95bd7 --- /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/v15/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..c470dce --- /dev/null +++ b/pkg/cloudapi/disks.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/change_disk_storage_policy.go b/pkg/cloudapi/disks/change_disk_storage_policy.go new file mode 100644 index 0000000..648cc1a --- /dev/null +++ b/pkg/cloudapi/disks/change_disk_storage_policy.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeDiskStoragePolicyRequest struct to change storage policy for disk +type ChangeDiskStoragePolicyRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"disk_id" json:"disk_id" validate:"required"` + + // ID of the storage policy to which to connect for disk + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// ChangeDiskStoragePolicy changes storage policy for disk +func (d Disks) ChangeDiskStoragePolicy(ctx context.Context, req ChangeDiskStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/disks/change_disk_storage_policy" + + 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/create.go b/pkg/cloudapi/disks/create.go new file mode 100644 index 0000000..f249a38 --- /dev/null +++ b/pkg/cloudapi/disks/create.go @@ -0,0 +1,62 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create disk +type CreateRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Name of disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of the storage policy under the disk will be created + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // 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..b3d345a --- /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/v15/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..4f18bf1 --- /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/v15/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..93d7cfc --- /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/v15/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..d69d858 --- /dev/null +++ b/pkg/cloudapi/disks/filter.go @@ -0,0 +1,321 @@ +package disks + +import ( + "context" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/k8s" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..8a2b1f6 --- /dev/null +++ b/pkg/cloudapi/disks/filter_test.go @@ -0,0 +1,548 @@ +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, + 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, + 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, + 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, + 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{ + { + 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: map[string]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, + VMID: 0, + }, + { + 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, + 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..21b1528 --- /dev/null +++ b/pkg/cloudapi/disks/from_platform_disk.go @@ -0,0 +1,111 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // 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"` + + // Pool for image create + // Required: false + PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // 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..7563b33 --- /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/v15/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..6c6a5d7 --- /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/v15/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..9ccb9ef --- /dev/null +++ b/pkg/cloudapi/disks/list.go @@ -0,0 +1,103 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Find by sep ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by storage policy id + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,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"` + + // ID of the resource group + // Required: false + RGID uint64 `url:"rg_id,omitempty" json:"rg_id,omitempty"` + + // ID of the compute + // Required: false + ComputeID uint64 `url:"compute_id,omitempty" json:"compute_id,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..67756ad --- /dev/null +++ b/pkg/cloudapi/disks/list_deleted.go @@ -0,0 +1,72 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // 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_unattached.go b/pkg/cloudapi/disks/list_unattached.go new file mode 100644 index 0000000..b145a55 --- /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/v15/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"` + + // 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 storage policy id + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,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..cd2ab3e --- /dev/null +++ b/pkg/cloudapi/disks/models.go @@ -0,0 +1,568 @@ +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"` + + // Discard + Discard string `json:"discard"` + + // Block size of disk + BlockSize string `json:"block_size"` + + // Computes + Computes map[string]string `json:"computes"` + + // Computes read only + ComputesReadOnly map[string]bool `json:"computes_read_only"` + + //Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // 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"` + + // Independent + Independent bool `json:"independent"` + + // 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"` + + // 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"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo map[string]uint64 `json:"presentTo"` + + // Provision + Provision string `json:"provision"` + + // 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 available + SizeAvailable float64 `json:"sizeAvailable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List of snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` + + // Update time + UpdatedTime uint64 `json:"updatedTime"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Cache mode of disk + Cache string `json:"cache"` +} + +type ItemDiskUnattached struct { + // 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"` + + // Discard + Discard string `json:"discard"` + + // Block size of disk + BlockSize string `json:"block_size"` + + // Boot Partition + BootPartition uint64 `json:"bootPartition"` + + // Cache + Cache string `json:"cache"` + + // 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 map[string]uint64 `json:"presentTo"` + + // Provision + Provision string `json:"provision"` + + // 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"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // ID of the Storage Policy + StoragePolicyID uint64 `json:"storage_policy_id"` + + // 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"` + + // Discard + Discard string `json:"discard"` + + // Block size of disk + BlockSize string `json:"block_size"` + + // Computes + Computes map[string]string `json:"computes"` + + // Computes read only + ComputesReadOnly map[string]bool `json:"computes_read_only"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // 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"` + + // Independent + Independent bool `json:"independent"` + + // 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"` + + // 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"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo map[string]uint64 `json:"presentTo"` + + // Provision + Provision string `json:"provision"` + + // 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 available + SizeAvailable float64 `json:"sizeAvailable"` + + // Size max + SizeMax uint64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List of snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` + + // Update time + UpdatedTime uint64 `json:"updatedTime"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Cache mode of disk + Cache string `json:"cache"` +} + +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"` +} diff --git a/pkg/cloudapi/disks/rename.go b/pkg/cloudapi/disks/rename.go new file mode 100644 index 0000000..e651774 --- /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/v15/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..1b7c680 --- /dev/null +++ b/pkg/cloudapi/disks/replicate.go @@ -0,0 +1,56 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // ID of the storage policy under the disk will be created + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// Replicate 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..4da510c --- /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/v15/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..41e92ee --- /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/v15/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..cc34595 --- /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/v15/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..b4e514a --- /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/v15/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..05f74bd --- /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/v15/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..79bea69 --- /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/v15/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..fd01efa --- /dev/null +++ b/pkg/cloudapi/disks/resize.go @@ -0,0 +1,45 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +// 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..e5b1dc9 --- /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/v15/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..e03f578 --- /dev/null +++ b/pkg/cloudapi/disks/serialize.go @@ -0,0 +1,99 @@ +package disks + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..de8dd1e --- /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/v15/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..64d6c1e --- /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/v15/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..38f8877 --- /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/v15/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..6de363f --- /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/v15/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..b692a80 --- /dev/null +++ b/pkg/cloudapi/dpdknet.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..84a4c3d --- /dev/null +++ b/pkg/cloudapi/dpdknet/dpdknet.go @@ -0,0 +1,15 @@ +package dpdknet + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..9c99949 --- /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/v15/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..8435d75 --- /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/v15/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..ebcca93 --- /dev/null +++ b/pkg/cloudapi/dpdknet/models.go @@ -0,0 +1,98 @@ +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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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..3b3ae5c --- /dev/null +++ b/pkg/cloudapi/extnet.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..3f55702 --- /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/v15/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..4cf2759 --- /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/v15/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/get_reserved_ip.go b/pkg/cloudapi/extnet/get_reserved_ip.go new file mode 100644 index 0000000..466987b --- /dev/null +++ b/pkg/cloudapi/extnet/get_reserved_ip.go @@ -0,0 +1,50 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about reserved address or address poll +type GetReservedIP struct { + // AccountID of the account whose reservation information we want to receive + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Field for specifying the ID of extnet whose reservation information we want to receive + // Required: false + ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"` +} + +// GetReservedIP gets information about reserved address or address poll as a slice of RecordReservedIP struct +func (e ExtNet) GetReservedIP(ctx context.Context, req GetReservedIP) ([]RecordReservedIP, error) { + res, err := e.GetReservedIPRaw(ctx, req) + if err != nil { + return nil, err + } + + reservedIP := make([]RecordReservedIP, 0) + + err = json.Unmarshal(res, &reservedIP) + if err != nil { + return nil, err + } + + return reservedIP, nil +} + +// GetRaw gets detailed information about external network as an array of bytes +func (e ExtNet) GetReservedIPRaw(ctx context.Context, req GetReservedIP) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/extnet/getReservedIp" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} 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..9f57702 --- /dev/null +++ b/pkg/cloudapi/extnet/list.go @@ -0,0 +1,92 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` + + //openVswitch bridge name + //Required: false + OVSBridge string `url:"ovsBridge,omitempty" json:"ovsBridge,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..98b77ed --- /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/v15/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..a525908 --- /dev/null +++ b/pkg/cloudapi/extnet/models.go @@ -0,0 +1,280 @@ +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"` + + // Free IPs + FreeIPs uint64 `json:"freeIps"` +} + +// 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 { + // Account ID + AccountID uint64 `json:"account_id"` + + // 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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // MTU + MTU uint64 `json:"mtu"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // Network IDs + NetworkIDs NetworkIDs `json:"networkIds"` + + // NTP + NTP []string `json:"ntp"` + + // OVS Bridge + OVSBridge string `json:"ovsBridge"` + + // PreReservation IP num + PreReservationsNum uint64 `json:"preReservationsNum"` + + // Prefix + Prefix uint64 `json:"prefix"` + + // PriVNFDevID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // Redundant + Redundant bool `json:"redundant"` + + // SecVnfDevId + SecVNFDevID uint64 `json:"secVnfDevId"` + + // List reservations + Reservations ListReservations `json:"reservations"` + + // List pre-reservations + PreReservations ListReservations `json:"pre-reservations"` + + // Shared with + SharedWith []uint64 `json:"sharedWith"` + + // Status + Status string `json:"status"` + + // VLAN ID + VLANID uint64 `json:"vlanId"` + + // VNFs + VNFs VNFs `json:"vnfs"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +type NetworkIDs struct { + // Primary + Primary uint64 `json:"primary"` + + // Secondary + Secondary uint64 `json:"secondary"` +} + +// Detailed information about reserved address or address pool +type RecordReservedIP struct { + ExtnetID int `json:"extnet_id"` + Reservations []Reservations `json:"reservations"` +} + +type Reservations struct { + AccountID int `json:"account_id"` + ClientType string `json:"clientType"` + DomainName string `json:"domainname"` + Hostname string `json:"hostname"` + IP string `json:"ip"` + Mac string `json:"mac"` + Type string `json:"type"` + VMID int `json:"vmId"` +} diff --git a/pkg/cloudapi/extnet/serialize.go b/pkg/cloudapi/extnet/serialize.go new file mode 100644 index 0000000..f15e175 --- /dev/null +++ b/pkg/cloudapi/extnet/serialize.go @@ -0,0 +1,43 @@ +package extnet + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..15bb040 --- /dev/null +++ b/pkg/cloudapi/flipgroup.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..aaa8ab6 --- /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/v15/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..492670c --- /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/v15/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..fb21443 --- /dev/null +++ b/pkg/cloudapi/flipgroup/create.go @@ -0,0 +1,70 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + // Default: "compute" + ClientType string `url:"clientType,omitempty" json:"clientType,omitempty"` + + // 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..fd11fee --- /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/v15/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..2efea98 --- /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/v15/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..1cf2932 --- /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/v15/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..16adc8d --- /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/v15/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..6fcb031 --- /dev/null +++ b/pkg/cloudapi/flipgroup/list.go @@ -0,0 +1,95 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 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..c7f130d --- /dev/null +++ b/pkg/cloudapi/flipgroup/models.go @@ -0,0 +1,163 @@ +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"` + + // 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..9f322bd --- /dev/null +++ b/pkg/cloudapi/flipgroup/serialize.go @@ -0,0 +1,43 @@ +package flipgroup + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..2a91c4f --- /dev/null +++ b/pkg/cloudapi/image.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/change_storage_policy.go b/pkg/cloudapi/image/change_storage_policy.go new file mode 100644 index 0000000..5bf1953 --- /dev/null +++ b/pkg/cloudapi/image/change_storage_policy.go @@ -0,0 +1,41 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type ChangeStoragePolicyRequest struct { + // ID of the image to change the storage policy + // Required: true + ImageID uint64 `url:"image_id" json:"image_id" validate:"required"` + + // ID of the storage policy to move the image to + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// ChangeStoragePolicy changes the storage policy of the image chosen +func (i Image) ChangeStoragePolicy(ctx context.Context, req ChangeStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/change_storage_policy" + + 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/create.go b/pkg/cloudapi/image/create.go new file mode 100644 index 0000000..4a4a375 --- /dev/null +++ b/pkg/cloudapi/image/create.go @@ -0,0 +1,129 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // ID of the chosen storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` +} + +type asyncWrapperCreateRequest struct { + CreateRequest + AsyncMode bool `url:"asyncMode"` +} + +// 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 +} + +// AsyncCreate creates image from a media identified by URL in async mode +func (i Image) AsyncCreate(ctx context.Context, req CreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/image/create" + + asyncReq := asyncWrapperCreateRequest{CreateRequest: req, AsyncMode: true} + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, asyncReq) + if err != nil { + return " ", err + } + + var taskID string + + err = json.Unmarshal(res, &taskID) + if err != nil { + return "", err + } + + return taskID, nil +} diff --git a/pkg/cloudapi/image/create_virtual.go b/pkg/cloudapi/image/create_virtual.go new file mode 100644 index 0000000..fd49365 --- /dev/null +++ b/pkg/cloudapi/image/create_virtual.go @@ -0,0 +1,46 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // AccountID to make the virtual image exclusive + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" 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..f15df58 --- /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/v15/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..7d7bf64 --- /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/v15/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..ab68fdf --- /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/v15/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..31575dd --- /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/v15/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..af20d2b --- /dev/null +++ b/pkg/cloudapi/image/list.go @@ -0,0 +1,108 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 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"` + + // Find by storage policy id + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,omitempty"` +} + +// List gets list of available images as a ListImages struct, optionally filtering by account ID +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.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + return res, err +} diff --git a/pkg/cloudapi/image/models.go b/pkg/cloudapi/image/models.go new file mode 100644 index 0000000..45fe0f9 --- /dev/null +++ b/pkg/cloudapi/image/models.go @@ -0,0 +1,217 @@ +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"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // 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"` + + // 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"` + + // Independent + Independent bool `json:"independent"` + + // Last modified + LastModified uint64 `json:"lastModified"` + + // Link to + LinkTo uint64 `json:"linkTo"` + + // Links to + LinksTo []uint64 `json:"linksTo"` + + // 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 map[string]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"` + + // snapshot ID + SnapshotID string `json:"snapshotId"` + + // Status + Status string `json:"status"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // 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..f3efe2d --- /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/v15/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..6c4bde6 --- /dev/null +++ b/pkg/cloudapi/image/serialize.go @@ -0,0 +1,43 @@ +package image + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..2df3056 --- /dev/null +++ b/pkg/cloudapi/k8ci.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..e0dd592 --- /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/v15/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..061f11b --- /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/v15/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..753b981 --- /dev/null +++ b/pkg/cloudapi/k8ci/list.go @@ -0,0 +1,75 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 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..9ae3351 --- /dev/null +++ b/pkg/cloudapi/k8ci/list_deleted.go @@ -0,0 +1,60 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 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..be1196a --- /dev/null +++ b/pkg/cloudapi/k8ci/serialize.go @@ -0,0 +1,43 @@ +package k8ci + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..8ee6618 --- /dev/null +++ b/pkg/cloudapi/k8s.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..d474a12 --- /dev/null +++ b/pkg/cloudapi/k8s/create.go @@ -0,0 +1,208 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 the chosen storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // 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 + // Default: Q35 + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` +} + +// 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..a04b598 --- /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/v15/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..c75df1b --- /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/v15/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..85f65b9 --- /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/v15/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..a49ea05 --- /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/v15/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..b151a35 --- /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/v15/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..6d6046b --- /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/v15/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..ded9748 --- /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/v15/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..071cf8c --- /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/v15/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..e449323 --- /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/v15/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..7e227cd --- /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/v15/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..387e18e --- /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/v15/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..dd361bf --- /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/v15/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..5bb3ef6 --- /dev/null +++ b/pkg/cloudapi/k8s/list.go @@ -0,0 +1,96 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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..b9d45b6 --- /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/v15/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/migrate_to_zone.go b/pkg/cloudapi/k8s/migrate_to_zone.go new file mode 100644 index 0000000..9eae30d --- /dev/null +++ b/pkg/cloudapi/k8s/migrate_to_zone.go @@ -0,0 +1,42 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move k8s cluster to another zone +type MigrateToZoneRequest struct { + // Kubernetes cluster ID to move + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zoneId" json:"zoneId" validate:"required"` +} + +// MigrateToZone moves k8s cluster instance to new zone +func (k8s K8S) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/k8s/migrateToZone" + + 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/models.go b/pkg/cloudapi/k8s/models.go new file mode 100644 index 0000000..8d49c13 --- /dev/null +++ b/pkg/cloudapi/k8s/models.go @@ -0,0 +1,335 @@ +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"` + + // Description + Description string `json:"desc"` + + // 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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..69b2a94 --- /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/v15/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..25e7b7f --- /dev/null +++ b/pkg/cloudapi/k8s/serialize.go @@ -0,0 +1,43 @@ +package k8s + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..d36648f --- /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/v15/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..1c7b30f --- /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/v15/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..d096968 --- /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/v15/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..8b1c084 --- /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/v15/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..5afa156 --- /dev/null +++ b/pkg/cloudapi/k8s/worker_add.go @@ -0,0 +1,53 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 + // Default: Q35 + 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..57b2c8a --- /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/v15/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..96007bf --- /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/v15/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..4814861 --- /dev/null +++ b/pkg/cloudapi/k8s/workers_group_add.go @@ -0,0 +1,98 @@ +package k8s + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 the chosen storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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 + // Default: Q35 + 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..20048f6 --- /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/v15/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..631d532 --- /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/v15/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..7f7dc0f --- /dev/null +++ b/pkg/cloudapi/kvmx86.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..e6c373d --- /dev/null +++ b/pkg/cloudapi/kvmx86/create.go @@ -0,0 +1,256 @@ +package kvmx86 + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type Interface struct { + // Network type + // Should be one of: + // - VINS + // - EXTNET + // - VFNIC + // - DPDK + // - EMPTY + // - SDN + // - TRUNK + 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"` + + // Used for EXTNET, TRUNK and DPDK + // Must be 1500-9216 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` + + // Net mask + // Used only to DPDK or VFNIC net type + // Required: false + NetMask uint64 `url:"netMask,omitempty" json:"netMask,omitempty"` + + // MAC address to assign to this VM when connecting to the specified network + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty" validate:"omitempty"` + + // SDN Segment ID + // Required: false + SDNSegmentID string `url:"sdn_segment_id,omitempty" json:"sdn_segment_id,omitempty"` + + // SDN Object Group IDs + // Required: false + SDNObjectGroupIDs []string `url:"sdn_object_group_ids,omitempty" json:"sdn_object_group_ids,omitempty"` + + // SDN Logical Port Display Name + // Required: false + SDNLogicalPortDisplayName string `url:"sdn_logical_port_display_name,omitempty" json:"sdn_logical_port_display_name,omitempty"` + + // SDN Logical Port Description + // Required: false + SDNLogicalPortDescription string `url:"sdn_logical_port_description,omitempty" json:"sdn_logical_port_description,omitempty"` + + // SDN interface id + // Required: false + SDNInterfaceID string `url:"sdn_interface_id,omitempty" json:"sdn_interface_id,omitempty"` + + // List of security group IDs to assign to this interface + // Required: false + SecGroups []uint64 `url:"security_groups,omitempty" json:"security_groups,omitempty"` + + // Flag indicating whether security groups are enabled for this interface + // Not applicable to netType VFNIC, TRUNK, or SDN + // Required: false + EnableSecGroups bool `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty"` + + // Flag indicating whether this interface is enabled (only for VINS, EXTNET, DPDK, SDN, TRUNK) + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` +} + +// 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 policy id of disk. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // Storage policy id of сompute. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,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"` + + // Custom fields for compute. Must be a dict + // Required: false + CustomFields string `url:"customFields,omitempty" json:"customFields,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 + // Default: Q35 + 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"` + + // Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False + // Required: false + PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // CPU alignment profile name + // Required: false + CPUAlignmentProfile string `url:"cpu_alignment_profile,omitempty" json:"cpu_alignment_profile,omitempty"` + + // Clock type for the VM + // Required: false + // Default: default + Clock string `url:"clock,omitempty" json:"clock,omitempty"` +} + +// GetRAM returns RAM field values +func (r CreateRequest) GetRAM() map[string]uint64 { + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +// 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)) + } + + url := "/cloudapi/kvmx86/create" + + res, err := k.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return 0, err + } + + return strconv.ParseUint(string(res), 10, 64) +} diff --git a/pkg/cloudapi/kvmx86/create_blank.go b/pkg/cloudapi/kvmx86/create_blank.go new file mode 100644 index 0000000..dc4e844 --- /dev/null +++ b/pkg/cloudapi/kvmx86/create_blank.go @@ -0,0 +1,154 @@ +package kvmx86 + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Storage policy id of compute. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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 the emulated system, Q35 or i440fx + // Required: false + // Default: Q35 + 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:"hp_backed" json:"hp_backed"` + + // 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:"cpu_pin" json:"cpu_pin"` + + // 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:"numa_affinity,omitempty" json:"numa_affinity,omitempty" validate:"omitempty,numaAffinity"` + + // Text description of this VM + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False + // Required: false + PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` + + // VM type linux, windows or unknown + // Required: false + LoaderType string `url:"loaderType,omitempty" json:"loaderType,omitempty" validate:"omitempty,loaderType"` + + // Boot type of image bios or uefi + // Required: false + BootType string `url:"bootType,omitempty" json:"bootType,omitempty" validate:"omitempty,imageBootType"` + + // Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming. + // 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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,omitempty"` + + // CPU alignment profile name + // Required: false + CPUAlignmentProfile string `url:"cpu_alignment_profile,omitempty" json:"cpu_alignment_profile,omitempty"` + + // Clock type for the VM + // Required: false + // Default: default + Clock string `url:"clock,omitempty" json:"clock,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 +} + +// 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)) + } + + url := "/cloudapi/kvmx86/createBlank" + + res, err := k.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return 0, err + } + + return strconv.ParseUint(string(res), 10, 64) +} diff --git a/pkg/cloudapi/kvmx86/kvmx86.go b/pkg/cloudapi/kvmx86/kvmx86.go new file mode 100644 index 0000000..bf84b46 --- /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/v15/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..e1029cd --- /dev/null +++ b/pkg/cloudapi/lb.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..fd1e061 --- /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/v15/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..6de223d --- /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/v15/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..5f3b955 --- /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/v15/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..b0c0ecb --- /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/v15/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..76a563b --- /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/v15/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..463059e --- /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/v15/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..bf444c6 --- /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/v15/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..7fd09b0 --- /dev/null +++ b/pkg/cloudapi/lb/create.go @@ -0,0 +1,102 @@ +package lb + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 interface{} `url:"start,omitempty" json:"start,omitempty" validate:"omitempty,isBool"` + + // Text description of this load balancer + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,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..d38e119 --- /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/v15/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..f3b888d --- /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/v15/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..936e67c --- /dev/null +++ b/pkg/cloudapi/lb/filter.go @@ -0,0 +1,91 @@ +package lb + +import ( + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..88c7cb5 --- /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/v15/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..9abc6d3 --- /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/v15/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..c155f58 --- /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/v15/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..9cbfff9 --- /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/v15/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..2534a58 --- /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/v15/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..9272a63 --- /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/v15/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..9c11131 --- /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/v15/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..ece596f --- /dev/null +++ b/pkg/cloudapi/lb/list.go @@ -0,0 +1,96 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..d1c5a4d --- /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/v15/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..2e55e0e --- /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/v15/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 := "/cloudapi/lb/makeHighlyAvailable" + + 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/migrate_to_zone.go b/pkg/cloudapi/lb/migrate_to_zone.go new file mode 100644 index 0000000..8bce1d8 --- /dev/null +++ b/pkg/cloudapi/lb/migrate_to_zone.go @@ -0,0 +1,42 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move lb to another zone +type MigrateToZoneRequest struct { + // ID of the load balancer instance to move + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zoneId" json:"zoneId" validate:"required"` +} + +// MigrateToZone moves lb instance to new zone +func (l LB) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/lb/migrateToZone" + + 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/models.go b/pkg/cloudapi/lb/models.go new file mode 100644 index 0000000..60471b7 --- /dev/null +++ b/pkg/cloudapi/lb/models.go @@ -0,0 +1,259 @@ +package lb + +// Detailed information about load balancer +type RecordLB struct { + //HAMode + HAMode bool `json:"HAmode"` + + // Access Control List + ACL interface{} `json:"acl"` + + //Account ID + AccountID uint64 `json:"accountId"` + + // 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 map[string]string `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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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..606bae0 --- /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/v15/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..27db835 --- /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/v15/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..de8b399 --- /dev/null +++ b/pkg/cloudapi/lb/serialize.go @@ -0,0 +1,43 @@ +package lb + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..f7eab2c --- /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/v15/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..7048e3e --- /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/v15/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..404c390 --- /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/v15/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..52e01a8 --- /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/v15/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_list.go b/pkg/cloudapi/locations/get_list.go new file mode 100644 index 0000000..0178ded --- /dev/null +++ b/pkg/cloudapi/locations/get_list.go @@ -0,0 +1,71 @@ +package locations + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListGetRequest struct to get list of locations +type ListGetRequest 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"` +} + +// ListGet gets list of all locations as a ListLocations struct +func (l Locations) ListGet(ctx context.Context, req ListGetRequest) (*ListLocations, error) { + + res, err := l.ListGetRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListLocations{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListGetRaw gets list of all locations as an array of bytes +func (l Locations) ListGetRaw(ctx context.Context, req ListGetRequest) ([]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.MethodGet, url, req) + return res, err +} 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..e51941e --- /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/v15/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..663237f --- /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/v15/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..16db21e --- /dev/null +++ b/pkg/cloudapi/locations/models.go @@ -0,0 +1,52 @@ +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"` + + // Network Modes + NetworkModes []string `json:"network_modes"` + + // Flag + Flag string `json:"flag"` + + // Meta + Meta []interface{} `json:"_meta"` + + // CKey + CKey string `json:"_ckey"` + + // Support of SDN + SDNSupport bool `json:"sdn_support"` + + // Is Zero Access enabled + ZeroAccessEnabled bool `json:"zeroaccess_enabled"` + + // Is BRO enabled + BROEnabled bool `json:"bro_enabled"` +} + +// 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..94c3650 --- /dev/null +++ b/pkg/cloudapi/locations/serialize.go @@ -0,0 +1,43 @@ +package locations + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..1074d6c --- /dev/null +++ b/pkg/cloudapi/locatons.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..f677f7c --- /dev/null +++ b/pkg/cloudapi/pcidevice.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..c707549 --- /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/v15/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..61c8a83 --- /dev/null +++ b/pkg/cloudapi/pcidevice/models.go @@ -0,0 +1,49 @@ +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"` + + // Node ID + NodeID uint64 `json:"nodeId"` + + // 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..0d6f853 --- /dev/null +++ b/pkg/cloudapi/pcidevice/pcidevice.go @@ -0,0 +1,15 @@ +package pcidevice + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..46fcb09 --- /dev/null +++ b/pkg/cloudapi/pcidevice/serialize.go @@ -0,0 +1,42 @@ +package pcidevice + +import ( + "encoding/json" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/prometheus.go b/pkg/cloudapi/prometheus.go new file mode 100644 index 0000000..8570215 --- /dev/null +++ b/pkg/cloudapi/prometheus.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/prometheus" + +// Accessing the Resmon method group +func (ca *CloudAPI) Prometheus() *prometheus.Prometheus { + return prometheus.New(ca.client) +} diff --git a/pkg/cloudapi/prometheus/compute_cpu_load.go b/pkg/cloudapi/prometheus/compute_cpu_load.go new file mode 100644 index 0000000..5f7e36b --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_cpu_load.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeCPUload" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_memory_available.go b/pkg/cloudapi/prometheus/compute_memory_available.go new file mode 100644 index 0000000..bb3f0f0 --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_memory_available.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + 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 := "/cloudapi/prometheus/computeMemoryAvailable" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_memory_unused.go b/pkg/cloudapi/prometheus/compute_memory_unused.go new file mode 100644 index 0000000..4a63037 --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_memory_unused.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + 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 := "/cloudapi/prometheus/computeMemoryUnused" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_memory_usable.go b/pkg/cloudapi/prometheus/compute_memory_usable.go new file mode 100644 index 0000000..4f537c8 --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_memory_usable.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + 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 := "/cloudapi/prometheus/computeMemoryUsable" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_memory_usage.go b/pkg/cloudapi/prometheus/compute_memory_usage.go new file mode 100644 index 0000000..8c359d1 --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_memory_usage.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + 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 := "/cloudapi/prometheus/computeMemoryUsage" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_memory_used.go b/pkg/cloudapi/prometheus/compute_memory_used.go new file mode 100644 index 0000000..3d46c8d --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_memory_used.go @@ -0,0 +1,53 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + 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 := "/cloudapi/prometheus/computeMemoryUsed" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_read_bytes.go b/pkg/cloudapi/prometheus/compute_read_bytes.go new file mode 100644 index 0000000..1da9cd9 --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_read_bytes.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeReadBytes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_read_requests.go b/pkg/cloudapi/prometheus/compute_read_requests.go new file mode 100644 index 0000000..cf1735e --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_read_requests.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeReadRequests" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_receive_bytes.go b/pkg/cloudapi/prometheus/compute_receive_bytes.go new file mode 100644 index 0000000..6705e3e --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_receive_bytes.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeReceiveBytes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_receive_packets.go b/pkg/cloudapi/prometheus/compute_receive_packets.go new file mode 100644 index 0000000..b62d864 --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_receive_packets.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeReceivePackets" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_transmit_bytes.go b/pkg/cloudapi/prometheus/compute_transmit_bytes.go new file mode 100644 index 0000000..722ff5a --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_transmit_bytes.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeTransmitBytes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_transmit_packets.go b/pkg/cloudapi/prometheus/compute_transmit_packets.go new file mode 100644 index 0000000..888c25c --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_transmit_packets.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeTransmitPackets" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_write_bytes.go b/pkg/cloudapi/prometheus/compute_write_bytes.go new file mode 100644 index 0000000..3c93d58 --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_write_bytes.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeWriteBytes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/compute_write_requests.go b/pkg/cloudapi/prometheus/compute_write_requests.go new file mode 100644 index 0000000..845a57c --- /dev/null +++ b/pkg/cloudapi/prometheus/compute_write_requests.go @@ -0,0 +1,57 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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 := "/cloudapi/prometheus/computeWriteRequests" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/computes.go b/pkg/cloudapi/prometheus/computes.go new file mode 100644 index 0000000..95922e1 --- /dev/null +++ b/pkg/cloudapi/prometheus/computes.go @@ -0,0 +1,77 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +const ( + ComputeCPULoad = "computeCPUload" + ComputeMemoryUsage = "computeMemoryUsage" + ComputeMemoryUsable = "computeMemoryUsable" + ComputeMemoryUnused = "computeMemoryUnused" + ComputeMemoryUsed = "computeMemoryUsed" + ComputeMemoryAvailable = "computeMemoryAvailable" + ComputeReadBytes = "computeReadBytes" + ComputeReadRequests = "computeReadRequests" + ComputeReceiveBytes = "computeReceiveBytes" + ComputeTransmitBytes = "computeTransmitBytes" + ComputeTransmitPackets = "computeTransmitPackets" + ComputeWriteBytes = "computeWriteBytes" + ComputeWriteRequests = "computeWriteRequests" +) + +type ComputesRequest struct { + // List of compute IDs to fetch metrics for + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"required"` + + // List of compute IDs to fetch metrics for + // Required: true + MetricIDs []string `url:"metricIds" json:"metricIds" 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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Get multiple metrics for multiple compute instances +func (p Prometheus) Computes(ctx context.Context, req ComputesRequest) (*ComputesData, error) { + res, err := p.ComputesRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ComputesData{} + + 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) ComputesRaw(ctx context.Context, req ComputesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/prometheus/computes" + + res, err := p.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/prometheus/models.go b/pkg/cloudapi/prometheus/models.go new file mode 100644 index 0000000..76ee3bc --- /dev/null +++ b/pkg/cloudapi/prometheus/models.go @@ -0,0 +1,40 @@ +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"` +} + +// ComputesData represents an array of data points for computes +type ComputesData []ItemCompute + +// ItemCompute represents a single data of compute +type ItemCompute struct { + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Array of metrics + Metrics []ItemMetric `json:"metrics"` + + // Error + Error string `json:"error"` +} + +// ItemMetric represents a single data point of metric +type ItemMetric struct { + // Metric ID + MetricID string `json:"metricId"` + + // Data represents an array of data points + Data PrometheusData `json:"data"` + + // Error + Error string `json:"error"` +} diff --git a/pkg/cloudapi/prometheus/prometheus.go b/pkg/cloudapi/prometheus/prometheus.go new file mode 100644 index 0000000..66997b1 --- /dev/null +++ b/pkg/cloudapi/prometheus/prometheus.go @@ -0,0 +1,15 @@ +package prometheus + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +type Prometheus struct { + client interfaces.Caller +} + +func New(client interfaces.Caller) *Prometheus { + return &Prometheus{ + client: client, + } +} diff --git a/pkg/cloudapi/rg.go b/pkg/cloudapi/rg.go new file mode 100644 index 0000000..e6061c0 --- /dev/null +++ b/pkg/cloudapi/rg.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..c3c2226 --- /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/v15/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..6f06cc7 --- /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/v15/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/add_storage_policy.go b/pkg/cloudapi/rg/add_storage_policy.go new file mode 100644 index 0000000..d7bc59f --- /dev/null +++ b/pkg/cloudapi/rg/add_storage_policy.go @@ -0,0 +1,46 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AddStoragePolicyRequest struct for adding storage policy to the resource group +type AddStoragePolicyRequest struct { + // ID of resource group to add to + // Required: true + RGID uint64 `url:"resgroup_id" json:"resgroup_id" validate:"required"` + + // ID of the storage policy to which to connect resource group + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // Limit storage resources GB. Or -1 unlimit + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} + +// AddStoragePolicy add storage policy to the account. +func (r RG) AddStoragePolicy(ctx context.Context, req AddStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/add_storage_policy" + + 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..d3e8ea2 --- /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/v15/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..f728e22 --- /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/v15/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..b23c824 --- /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/v15/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..89d46a8 --- /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/v15/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..8fc8d4a --- /dev/null +++ b/pkg/cloudapi/rg/create.go @@ -0,0 +1,106 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Storage policies + // Required: false + StoragePolicies []StoragePolicy `url:"storage_policies" json:"storage_policies"` + + // 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 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"` + + // SDN access group id + // Required: false + SDNAccessGroupID string `url:"sdn_access_group_id,omitempty" json:"sdn_access_group_id,omitempty"` +} + +type StoragePolicy struct { + ID uint64 `url:"id" json:"id"` + Limit int `url:"limit" json:"limit"` +} + +// 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.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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/del_storage_policy.go b/pkg/cloudapi/rg/del_storage_policy.go new file mode 100644 index 0000000..cf41b75 --- /dev/null +++ b/pkg/cloudapi/rg/del_storage_policy.go @@ -0,0 +1,42 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DelStoragePolicyRequest struct for deleting storage policy to the resource group +type DelStoragePolicyRequest struct { + // ID of resource group + // Required: true + RGID uint64 `url:"resgroup_id" json:"resgroup_id" validate:"required"` + + // ID of the storage policy to which to disconnect account + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// DelStoragePolicy delete storage policy to the account. +func (r RG) DelStoragePolicy(ctx context.Context, req DelStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/rg/del_storage_policy" + + 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/delete.go b/pkg/cloudapi/rg/delete.go new file mode 100644 index 0000000..6338cd0 --- /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/v15/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..d940d02 --- /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/v15/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..af39e61 --- /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/v15/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..0514891 --- /dev/null +++ b/pkg/cloudapi/rg/filter_test.go @@ -0,0 +1,230 @@ +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", + ResourceLimits: ResourceLimits{ + CUC: -1, + CUI: -1, + CUM: -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", + ResourceLimits: ResourceLimits{ + CUC: -1, + CUI: -1, + CUM: -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", + ResourceLimits: ResourceLimits{ + CUC: -1, + CUI: -1, + CUM: -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..2f3256a --- /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/v15/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..7882d5e --- /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/v15/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..186f741 --- /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/v15/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..19c11f0 --- /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/v15/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..12510c9 --- /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/v15/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..6c114bf --- /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/v15/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..80b51d6 --- /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/v15/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..419f104 --- /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/v15/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..4988e95 --- /dev/null +++ b/pkg/cloudapi/rg/models.go @@ -0,0 +1,857 @@ +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"` + + // 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 uint64 `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"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // List of resource types + ResTypes []string `json:"resourceTypes"` + + // Storage policy ids + StoragePolicyIDs []uint64 `json:"storage_policy_ids"` + + // SDN access group id + SDNAccessGroupID string `json:"sdn_access_group_id"` + + // 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 uint64 `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"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // List of resource types + ResTypes []string `json:"resourceTypes"` + + // SDN access group id + SDNAccessGroupID string `json:"sdn_access_group_id"` + + // Storage policy ids + StoragePolicyIDs []uint64 `json:"storage_policy_ids"` + + // 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 { + // Email + Email string `json:"email"` + + // 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"` + + // GPU units + GPUUnits float64 `json:"gpu_units"` + + // Storage policies + StoragePolicies []StoragePolicy `json:"storage_policy"` +} + +// 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"` + + // 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..1ac6237 --- /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/v15/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..610146f --- /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/v15/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..5a1cda6 --- /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/v15/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..9fe7f0c --- /dev/null +++ b/pkg/cloudapi/rg/serialize.go @@ -0,0 +1,43 @@ +package rg + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..d90c3c7 --- /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/v15/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..92deec9 --- /dev/null +++ b/pkg/cloudapi/rg/update.go @@ -0,0 +1,76 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,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"` + + // Storage policies + // Required: false + StoragePolicies []StoragePolicy `url:"-" json:"storage_policies,omitempty"` +} + +// 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.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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..21df3dc --- /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/v15/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/secgroup/create.go b/pkg/cloudapi/secgroup/create.go new file mode 100644 index 0000000..bd0c488 --- /dev/null +++ b/pkg/cloudapi/secgroup/create.go @@ -0,0 +1,46 @@ +package secgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type CreateRequest struct { + // Account ID that owns security group + // Required: true + AccountID uint64 `url:"account_id" json:"account_id" validate:"required"` + + // Security group name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Security group description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` +} + +func (sg SecurityGroup) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/security_group/create" + + res, err := sg.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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/secgroup/create_rule.go b/pkg/cloudapi/secgroup/create_rule.go new file mode 100644 index 0000000..aabc7c6 --- /dev/null +++ b/pkg/cloudapi/secgroup/create_rule.go @@ -0,0 +1,63 @@ +package secgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type CreateRuleRequest struct { + // Security group ID + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` + + // Traffic direction (inbound/outbound) + // Required: true + Direction string `url:"direction" json:"direction" validate:"required,securityGroupDirection"` + + // IP protocol version + // Default: IPv4 + // Required: false + Ethertype string `url:"ethertype,omitempty" json:"ethertype,omitempty" validate:"omitempty,securityGroupEthertype"` + + // Network protocol, available values : icmp, tcp, udp + // Required: false + Protocol string `url:"protocol,omitempty" json:"protocol,omitempty" validate:"omitempty,securityGroupProtocol"` + + // Start port number (for TCP/UDP) + // Required: false + PortRangeMin uint64 `url:"port_range_min,omitempty" json:"port_range_min,omitempty"` + + // End port number (for TCP/UDP) + // Required: false + PortRangeMax uint64 `url:"port_range_max,omitempty" json:"port_range_max,omitempty"` + + // Remote IP prefix in CIDR notation + // Required: false + RemoteIPPrefix string `url:"remote_ip_prefix,omitempty" json:"remote_ip_prefix,omitempty"` +} + +func (sg SecurityGroup) CreateRule(ctx context.Context, req CreateRuleRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/security_group/create_rule" + + res, err := sg.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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/secgroup/delete.go b/pkg/cloudapi/secgroup/delete.go new file mode 100644 index 0000000..8a413b4 --- /dev/null +++ b/pkg/cloudapi/secgroup/delete.go @@ -0,0 +1,36 @@ +package secgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type DeleteRequest struct { + // Security group ID + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` +} + +func (sg SecurityGroup) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/security_group/delete" + + res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/secgroup/delete_rule.go b/pkg/cloudapi/secgroup/delete_rule.go new file mode 100644 index 0000000..5db2a02 --- /dev/null +++ b/pkg/cloudapi/secgroup/delete_rule.go @@ -0,0 +1,40 @@ +package secgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type DeleteRuleRequest struct { + // Security group ID + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` + + // Rule ID + // Required: true + RuleID uint64 `url:"rule_id" json:"rule_id" validate:"required"` +} + +func (sg SecurityGroup) DeleteRule(ctx context.Context, req DeleteRuleRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/security_group/delete_rule" + + res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/secgroup/filter.go b/pkg/cloudapi/secgroup/filter.go new file mode 100644 index 0000000..6a03da3 --- /dev/null +++ b/pkg/cloudapi/secgroup/filter.go @@ -0,0 +1,80 @@ +package secgroup + +// FilterByID returns ListSecurityGroups with specified ID. +func (lsg ListSecurityGroups) FilterByID(id uint64) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.ID == id + } + + return lsg.FilterFunc(predicate) +} + +// FilterByID returns ListSecurityGroups with specified Name. +func (lsg ListSecurityGroups) FilterByName(name string) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.Name == name + } + + return lsg.FilterFunc(predicate) +} + +// FilterByCreatedBy returns ListSecurityGroups with specified CreatedBy. +func (lsg ListSecurityGroups) FilterByCreatedBy(createdBy string) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.CreatedBy == createdBy + } + + return lsg.FilterFunc(predicate) +} + +// FilterByDescription returns ListSecurityGroups with specified Description. +func (lsg ListSecurityGroups) FilterByDescription(description string) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.Description == description + } + + return lsg.FilterFunc(predicate) +} + +// FilterByUpdatedBy returns ListSecurityGroups with specified UpdatedBy. +func (lsg ListSecurityGroups) FilterByUpdatedBy(updatedBy string) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.UpdatedBy == updatedBy + } + + return lsg.FilterFunc(predicate) +} + +// FilterByAccountID returns ListSecurityGroups with specified AccountID. +func (lsg ListSecurityGroups) FilterByAccountID(accountID uint64) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.AccountID == accountID + } + + return lsg.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListSecurityGroups based on a user-specified predicate. +func (lsg ListSecurityGroups) FilterFunc(predicate func(ItemSecurityGroup) bool) ListSecurityGroups { + var result ListSecurityGroups + + for _, item := range lsg.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemSecurityGroup +// If none was found, returns an empty struct. +func (lsg ListSecurityGroups) FindOne() ItemSecurityGroup { + if len(lsg.Data) == 0 { + return ItemSecurityGroup{} + } + + return lsg.Data[0] +} diff --git a/pkg/cloudapi/secgroup/filter_test.go b/pkg/cloudapi/secgroup/filter_test.go new file mode 100644 index 0000000..2231f60 --- /dev/null +++ b/pkg/cloudapi/secgroup/filter_test.go @@ -0,0 +1,87 @@ +package secgroup + +import "testing" + +var securityGroups = ListSecurityGroups{ + Data: []ItemSecurityGroup{ + { + ID: 1, + AccountID: 1, + Name: "sg1", + Description: "some desc", + CreatedBy: "user", + }, + { + ID: 3, + AccountID: 3, + Name: "sg3", + Description: "some desc", + CreatedBy: "anotheruser", + }, + { + ID: 5, + AccountID: 3, + Name: "sg5", + Description: "some other desc", + CreatedBy: "anotheruser", + UpdatedBy: "user", + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := securityGroups.FilterByID(1).FindOne() + if actual.ID != 1 { + t.Fatal("expected ID 1, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := securityGroups.FilterByName("sg3").FindOne() + if actual.Name != "sg3" { + t.Fatal("expected Name sg3, found: ", actual.Name) + } +} + +func TestFilterByDescription(t *testing.T) { + actual := securityGroups.FilterByDescription("some desc") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Description != "some desc" { + t.Fatal("expected Description 'some desc', found: ", item.Description) + } + } +} + +func TestFilterByAccountID(t *testing.T) { + actual := securityGroups.FilterByAccountID(1).FindOne() + if actual.AccountID != 1 { + t.Fatal("expected AccountID 1, found: ", actual.AccountID) + } +} + +func TestFilterByCreatedBy(t *testing.T) { + actual := securityGroups.FilterByCreatedBy("anotheruser") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedBy != "anotheruser" { + t.Fatal("expected CreatedBy 'anotheruser', found: ", item.CreatedBy) + } + } +} + +func TestFilterByUpdatedBy(t *testing.T) { + actual := securityGroups.FilterByUpdatedBy("user").FindOne() + if actual.UpdatedBy != "user" { + t.Fatal("expected UpdatedBy 'user', found: ", actual.UpdatedBy) + } +} diff --git a/pkg/cloudapi/secgroup/get.go b/pkg/cloudapi/secgroup/get.go new file mode 100644 index 0000000..2948236 --- /dev/null +++ b/pkg/cloudapi/secgroup/get.go @@ -0,0 +1,43 @@ +package secgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type GetRequest struct { + // ID of security group + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` +} + +func (sg SecurityGroup) Get(ctx context.Context, req GetRequest) (*RecordSecurityGroup, error) { + res, err := sg.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordSecurityGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sg SecurityGroup) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/security_group/get" + + res, err := sg.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudapi/secgroup/list.go b/pkg/cloudapi/secgroup/list.go new file mode 100644 index 0000000..a56b936 --- /dev/null +++ b/pkg/cloudapi/secgroup/list.go @@ -0,0 +1,86 @@ +package secgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type ListRequest struct { + // Search by security group id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Search by account id + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` + + // Search by security group name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Search by security group description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Search by created after time (unix timestamp) + // Required: false + CreatedMin uint64 `url:"created_min,omitempty" json:"created_min,omitempty"` + + // Search by created before time (unix timestamp) + // Required: false + CreatedMax uint64 `url:"created_max,omitempty" json:"created_max,omitempty"` + + // Search by updated after time (unix timestamp) + // Required: false + UpdatedMin uint64 `url:"updated_min,omitempty" json:"updated_min,omitempty"` + + // Search by updated before time (unix timestamp) + // Required: false + UpdatedMax uint64 `url:"updated_max,omitempty" json:"updated_max,omitempty"` + + // Sort by one of supported fields, format ± + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,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"` +} + +// List gets list of security groups as a ListSecurityGroups struct +func (sg SecurityGroup) List(ctx context.Context, req ListRequest) (*ListSecurityGroups, error) { + + res, err := sg.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListSecurityGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of security groups as an array of bytes +func (sg SecurityGroup) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/security_group/list" + + res, err := sg.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudapi/secgroup/models.go b/pkg/cloudapi/secgroup/models.go new file mode 100644 index 0000000..766d731 --- /dev/null +++ b/pkg/cloudapi/secgroup/models.go @@ -0,0 +1,94 @@ +package secgroup + +type ListSecurityGroups struct { + // List + Data []ItemSecurityGroup `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type ItemSecurityGroup struct { + // ID of the security group + ID uint64 `json:"id"` + + // Account ID that owns the security group + AccountID uint64 `json:"account_id"` + + // Name of the security group + Name string `json:"name"` + + // Description of the security group + Description string `json:"description"` + + // List of rules + Rules Rules `json:"rules"` + + // Created at + CreatedAt uint64 `json:"created_at"` + + // Updated at + UpdatedAt uint64 `json:"updated_at"` + + // Created by + CreatedBy string `json:"created_by"` + + // Updated by + UpdatedBy string `json:"updated_by"` +} + +type RecordSecurityGroup struct { + // ID of the security group + ID uint64 `json:"id"` + + // Account ID that owns the security group + AccountID uint64 `json:"account_id"` + + // Name of the security group + Name string `json:"name"` + + // Description of the security group + Description string `json:"description"` + + // List of rules + Rules Rules `json:"rules"` + + // Created at + CreatedAt uint64 `json:"created_at"` + + // Updated at + UpdatedAt uint64 `json:"updated_at"` + + // Created by + CreatedBy string `json:"created_by"` + + // Updated by + UpdatedBy string `json:"updated_by"` +} + +type Rules []Rule + +type Rule struct { + // ID of the rule + ID uint64 `json:"id"` + + // Traffic direction (inbound/outbound) + Direction string `json:"direction"` + + // IP protocol version + Ethertype string `json:"ethertype"` + + // Network protocol + Protocol string `json:"protocol"` + + // Start port number (for TCP/UDP) + PortRangeMin uint64 `json:"port_range_min"` + + // End port number (for TCP/UDP) + PortRangeMax uint64 `json:"port_range_max"` + + // Remote IP prefix in CIDR notation + RemoteIPPrefix string `json:"remote_ip_prefix"` + + RemoteGroupID uint64 `json:"remote_group_id"` +} diff --git a/pkg/cloudapi/secgroup/security_group.go b/pkg/cloudapi/secgroup/security_group.go new file mode 100644 index 0000000..ac90e5f --- /dev/null +++ b/pkg/cloudapi/secgroup/security_group.go @@ -0,0 +1,15 @@ +package secgroup + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + +// Structure for creating request to storage policy +type SecurityGroup struct { + client interfaces.Caller +} + +// Builder for security policy endpoint +func New(client interfaces.Caller) *SecurityGroup { + return &SecurityGroup{ + client: client, + } +} diff --git a/pkg/cloudapi/secgroup/sorting.go b/pkg/cloudapi/secgroup/sorting.go new file mode 100644 index 0000000..57df52b --- /dev/null +++ b/pkg/cloudapi/secgroup/sorting.go @@ -0,0 +1,41 @@ +package secgroup + +import "sort" + +// SortByCreatedAt sorts ListSecurityGroups by the CreatedAt field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lsg ListSecurityGroups) SortByCreatedAt(inverse bool) ListSecurityGroups { + if len(lsg.Data) < 2 { + return lsg + } + + sort.Slice(lsg.Data, func(i, j int) bool { + if inverse { + return lsg.Data[i].CreatedAt > lsg.Data[j].CreatedAt + } + + return lsg.Data[i].CreatedAt < lsg.Data[j].CreatedAt + }) + + return lsg +} + +// SortByUpdatedAt sorts ListSecurityGroups by the UpdatedAt field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lsg ListSecurityGroups) SortByUpdatedAt(inverse bool) ListSecurityGroups { + if len(lsg.Data) < 2 { + return lsg + } + + sort.Slice(lsg.Data, func(i, j int) bool { + if inverse { + return lsg.Data[i].UpdatedAt > lsg.Data[j].UpdatedAt + } + + return lsg.Data[i].UpdatedAt < lsg.Data[j].UpdatedAt + }) + + return lsg +} diff --git a/pkg/cloudapi/secgroup/update.go b/pkg/cloudapi/secgroup/update.go new file mode 100644 index 0000000..b769f3e --- /dev/null +++ b/pkg/cloudapi/secgroup/update.go @@ -0,0 +1,51 @@ +package secgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type UpdateRequest struct { + // Security group ID + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` + + // New security group name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New security group description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` +} + +func (sg SecurityGroup) Update(ctx context.Context, req UpdateRequest) (*RecordSecurityGroup, error) { + res, err := sg.UpdateRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordSecurityGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sg SecurityGroup) UpdateRaw(ctx context.Context, req UpdateRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/security_group/update" + + res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/securitygroup.go b/pkg/cloudapi/securitygroup.go new file mode 100644 index 0000000..6324383 --- /dev/null +++ b/pkg/cloudapi/securitygroup.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + secgroup "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/secgroup" +) + +// Accessing the Security Group method group +func (ca *CloudAPI) SecurityGroup() *secgroup.SecurityGroup { + return secgroup.New(ca.client) +} diff --git a/pkg/cloudapi/sep.go b/pkg/cloudapi/sep.go new file mode 100644 index 0000000..0450927 --- /dev/null +++ b/pkg/cloudapi/sep.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/sep" + +// Accessing the SEP method group +func (cb *CloudAPI) SEP() *sep.SEP { + return sep.New(cb.client) +} diff --git a/pkg/cloudapi/sep/filter.go b/pkg/cloudapi/sep/filter.go new file mode 100644 index 0000000..b97bd45 --- /dev/null +++ b/pkg/cloudapi/sep/filter.go @@ -0,0 +1,83 @@ +package sep + +// FilterBySEPID returns ListAvailableSEP with the specified SEPID. +func (sl ListAvailableSEP) FilterBySEPID(sepID uint64) ListAvailableSEP { + predicate := func(sd SEPData) bool { + return sd.SEPID == sepID + } + + return sl.FilterFunc(predicate) +} + +// FilterBySEPName returns ListAvailableSEP with the specified SEPName. +func (sl ListAvailableSEP) FilterBySEPName(SEPName string) ListAvailableSEP { + predicate := func(sd SEPData) bool { + return sd.SEPName == SEPName + } + + return sl.FilterFunc(predicate) +} + +// FilterBySEPType returns ListAvailableSEP with the specified SEPType. +func (sl ListAvailableSEP) FilterBySEPType(SEPType string) ListAvailableSEP { + predicate := func(sd SEPData) bool { + return sd.SEPType == SEPType + } + + return sl.FilterFunc(predicate) +} + +// FilterByPoolType returns ListAvailableSEP where at least one pool has the specified type. +func (sl ListAvailableSEP) FilterByPoolType(poolType string) ListAvailableSEP { + predicate := func(sd SEPData) bool { + for _, pool := range sd.Pools { + for _, pt := range pool.Types { + if pt == poolType { + return true + } + } + } + return false + } + + return sl.FilterFunc(predicate) +} + +// FilterBySystemPool returns ListAvailableSEP where at least one pool is a system pool. +func (sl ListAvailableSEP) FilterBySystemPool(isSystem bool) ListAvailableSEP { + predicate := func(sd SEPData) bool { + for _, pool := range sd.Pools { + if pool.System == isSystem { + return true + } + } + return false + } + + return sl.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListAvailableSEP based on a user-defined predicate. +func (sl ListAvailableSEP) FilterFunc(predicate func(SEPData) bool) ListAvailableSEP { + var result ListAvailableSEP + + for _, item := range sl.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns the first found SEPData. +// If nothing is found, returns an empty struct. +func (sl ListAvailableSEP) FindOne() SEPData { + if len(sl.Data) == 0 { + return SEPData{} + } + + return sl.Data[0] +} diff --git a/pkg/cloudapi/sep/filter_test.go b/pkg/cloudapi/sep/filter_test.go new file mode 100644 index 0000000..979f907 --- /dev/null +++ b/pkg/cloudapi/sep/filter_test.go @@ -0,0 +1,134 @@ +package sep + +import ( + "testing" +) + +var seps = ListAvailableSEP{ + EntryCount: 3, + Data: []SEPData{ + { + SEPID: 1, + SEPName: "sep_1", + SEPType: "TATLIN", + Pools: []Pool{ + { + Name: "pool_1", + Types: []string{"DES"}, + System: false, + }, + }, + }, + { + SEPID: 2, + SEPName: "sep_2", + SEPType: "SHARED", + Pools: []Pool{ + { + Name: "pool_2", + Types: []string{"DES"}, + System: true, + }, + }, + }, + { + SEPID: 3, + SEPName: "sep_3", + SEPType: "DES", + Pools: []Pool{ + { + Name: "pool_3", + Types: []string{"DES"}, + System: false, + }, + }, + }, + }, +} + +func TestFilterBySEPID(t *testing.T) { + actual := seps.FilterBySEPID(1).FindOne() + + if actual.SEPID != 1 { + t.Fatal("expected SEPID 1, found: ", actual.SEPID) + } +} + +func TestFilterBySEPName(t *testing.T) { + actual := seps.FilterBySEPName("sep_2").FindOne() + + if actual.SEPName != "sep_2" { + t.Fatal("expected SEPName 'sep_2', found: ", actual.SEPName) + } +} + +func TestFilterBySEPType(t *testing.T) { + actual := seps.FilterBySEPType("TATLIN").FindOne() + + if actual.SEPType != "TATLIN" { + t.Fatal("expected SEPType 'TATLIN', found: ", actual.SEPType) + } +} + +func TestFilterByPoolType(t *testing.T) { + actual := seps.FilterByPoolType("DES") + + if len(actual.Data) != 3 { + t.Fatal("expected 3 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + found := false + for _, pool := range item.Pools { + for _, poolType := range pool.Types { + if poolType == "DES" { + found = true + break + } + } + if found { + break + } + } + if !found { + t.Fatal("expected Pool type 'DES', not found in SEP: ", item.SEPID) + } + } +} + +func TestFilterBySystemPool(t *testing.T) { + actual := seps.FilterBySystemPool(true) + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + found := false + for _, pool := range item.Pools { + if pool.System { + found = true + break + } + } + if !found { + t.Fatal("expected System pool, not found in SEP: ", item.SEPID) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := seps.FilterFunc(func(sd SEPData) bool { + return len(sd.Pools) > 0 + }) + + if len(actual.Data) != 3 { + t.Fatal("expected 3 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if len(item.Pools) == 0 { + t.Fatal("expected Pools to contain at least 1 element, found: ", len(item.Pools)) + } + } +} diff --git a/pkg/cloudapi/sep/ids.go b/pkg/cloudapi/sep/ids.go new file mode 100644 index 0000000..aea3dea --- /dev/null +++ b/pkg/cloudapi/sep/ids.go @@ -0,0 +1,10 @@ +package sep + +// IDs gets array of SEPIDs from ListSEP struct +func (ls ListAvailableSEP) IDs() []uint64 { + res := make([]uint64, 0, len(ls.Data)) + for _, s := range ls.Data { + res = append(res, s.SEPID) + } + return res +} diff --git a/pkg/cloudapi/sep/list_available_sep_and_pools.go b/pkg/cloudapi/sep/list_available_sep_and_pools.go new file mode 100644 index 0000000..f15c58d --- /dev/null +++ b/pkg/cloudapi/sep/list_available_sep_and_pools.go @@ -0,0 +1,52 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListAvailableSEPAndPoolsRequest struct to get dict with entry count and list of dict with SEPs and pools details accessible by the Account and RG +type ListAvailableSEPAndPoolsRequest struct { + // Account ID + // Required: true + AccountID uint64 `url:"account_id" json:"account_id" validate:"required"` + + // RG ID + // Required: false + RGID uint64 `url:"rg_id,omitempty" json:"rg_id,omitempty"` +} + +// ListAvailableSEPAndPools get dict with entry count and list of dict with SEPs and pools details accessible by the Account and RG +func (s SEP) ListAvailableSEPAndPools(ctx context.Context, req ListAvailableSEPAndPoolsRequest) (*ListAvailableSEP, error) { + + res, err := s.ListAvailableSEPAndPoolsRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListAvailableSEP{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list as an array of bytes +func (s SEP) ListAvailableSEPAndPoolsRaw(ctx context.Context, req ListAvailableSEPAndPoolsRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/sep/listAvailableSepAndPools" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/sep/models.go b/pkg/cloudapi/sep/models.go new file mode 100644 index 0000000..e32e540 --- /dev/null +++ b/pkg/cloudapi/sep/models.go @@ -0,0 +1,34 @@ +package sep + +type Pool struct { + // Pool name + Name string `json:"name"` + + // Pool types + Types []string `json:"types"` + + // System + System bool `json:"system"` +} + +type SEPData struct { + // SEP ID + SEPID uint64 `json:"sepId"` + + // SEP name + SEPName string `json:"sepName"` + + // Sep type + SEPType string `json:"sepType"` + + // Pools + Pools []Pool `json:"pools"` +} + +type ListAvailableSEP struct { + // Entry count + EntryCount uint64 `json:"entryCount"` + + // Data + Data []SEPData `json:"data"` +} diff --git a/pkg/cloudapi/sep/sep.go b/pkg/cloudapi/sep/sep.go new file mode 100644 index 0000000..d5a115d --- /dev/null +++ b/pkg/cloudapi/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/v15/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/cloudapi/sep/serialize.go b/pkg/cloudapi/sep/serialize.go new file mode 100644 index 0000000..b82ee1e --- /dev/null +++ b/pkg/cloudapi/sep/serialize.go @@ -0,0 +1,43 @@ +package sep + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 ListAvailableSEP) 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 SEPData) 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/cloudapi/storage_policy.go b/pkg/cloudapi/storage_policy.go new file mode 100644 index 0000000..42b2979 --- /dev/null +++ b/pkg/cloudapi/storage_policy.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/stpolicy" +) + +// Accessing the Storage Policy method group +func (ca *CloudAPI) StPolicy() *stpolicy.StPolicy { + return stpolicy.New(ca.client) +} diff --git a/pkg/cloudapi/stpolicy/get.go b/pkg/cloudapi/stpolicy/get.go new file mode 100644 index 0000000..66bb92b --- /dev/null +++ b/pkg/cloudapi/stpolicy/get.go @@ -0,0 +1,43 @@ +package stpolicy + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type GetRequest struct { + // ID of storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +func (sp StPolicy) Get(ctx context.Context, req GetRequest) (*InfoStoragePolicy, error) { + res, err := sp.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := InfoStoragePolicy{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sp StPolicy) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/storage_policy/get" + + res, err := sp.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudapi/stpolicy/list.go b/pkg/cloudapi/stpolicy/list.go new file mode 100644 index 0000000..d6ad325 --- /dev/null +++ b/pkg/cloudapi/stpolicy/list.go @@ -0,0 +1,94 @@ +package stpolicy + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +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"` + + // Search by storage policy ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Search by storage policy name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Search by storage policy status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Search by storage policy desc + // Required: false + Desc string `url:"desc,omitempty" json:"desc,omitempty"` + + // Search by storage policy iops limit + // Required: false + LimitIOPS uint64 `url:"limit_iops,omitempty" json:"limit_iops,omitempty"` + + // Sort by one of supported fields, format ± + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // ID of account ID + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` + + // Search by resgroup id + // Required: false + ResgroupID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"` + + // Search by sep id + // Required: false + SepID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"` + + // Search by pool name + // Required: false + PoolName string `url:"pool_name,omitempty" json:"pool_name,omitempty"` + + // Filter SEP's by tech status (ENABLED, DISABLED) + // Required: false + SepTechStatus string `url:"sep_tech_status,omitempty" json:"sep_tech_status,omitempty" validate:"omitempty,sepTechStatus"` +} + +// List gets list of storage policies as a ListStoragePolicies struct +func (sp StPolicy) List(ctx context.Context, req ListRequest) (*ListStoragePolicies, error) { + + res, err := sp.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListStoragePolicies{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of storage policies as an array of bytes +func (sp StPolicy) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/storage_policy/list" + + res, err := sp.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudapi/stpolicy/models.go b/pkg/cloudapi/stpolicy/models.go new file mode 100644 index 0000000..eaeb2bc --- /dev/null +++ b/pkg/cloudapi/stpolicy/models.go @@ -0,0 +1,91 @@ +package stpolicy + +type ListStoragePolicies struct { + // List + Data []ItemStoragePolicy `json:"data"` + + // Entry Count + EntryCount uint64 `json:"entryCount"` +} + +type ItemStoragePolicy struct { + // ID of the storage policy + ID uint64 `json:"id"` + + // GUID + GUID uint64 `json:"guid"` + + // Name of the storage policy + Name string `json:"name"` + + // Description of the storage policy + Description string `json:"description"` + + // List of pools in SEP for storage policy + AccessSEPPools ListAccessSEPPools `json:"access_seps_pools"` + + // Status of the storage policy + Status string `json:"status"` + + // Max IOPS for the sotrage policy + LimitIOPS uint64 `json:"limit_iops"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Which accounts and resource groups use the storage policy + Usage Usage `json:"usage"` +} + +type InfoStoragePolicy struct { + // ID of the storage policy + ID uint64 `json:"id"` + + // GUID + GUID uint64 `json:"guid"` + + // Name of the storage policy + Name string `json:"name"` + + // Description of the storage policy + Description string `json:"description"` + + // List of pools in SEP for storage policy + AccessSEPPools ListAccessSEPPools `json:"access_seps_pools"` + + // Status of the storage policy + Status string `json:"status"` + + // Max IOPS for the sotrage policy + LimitIOPS uint64 `json:"limit_iops"` + + // ID of the storage policy + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Which accounts and resource groups use the storage policy + Usage Usage `json:"usage"` +} + +type ListAccessSEPPools []AccessSEPPool + +type AccessSEPPool struct { + // SEP ID + SEPID uint64 `json:"sep_id"` + + // SEP name + Name string `json:"sep_name"` + + // Pool names + PoolNames []string `json:"pool_names"` + + // Technical status of the SEP + SepTechStatus string `json:"sep_tech_status"` +} + +type Usage struct { + // List of accounts + Accounts []uint64 `json:"accounts"` + + // List of resource groups + Resgroups []uint64 `json:"resgroups"` +} diff --git a/pkg/cloudapi/stpolicy/storage_policy.go b/pkg/cloudapi/stpolicy/storage_policy.go new file mode 100644 index 0000000..3d3f57a --- /dev/null +++ b/pkg/cloudapi/stpolicy/storage_policy.go @@ -0,0 +1,15 @@ +package stpolicy + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + +// Structure for creating request to storage policy +type StPolicy struct { + client interfaces.Caller +} + +// Builder for storage policy endpoint +func New(client interfaces.Caller) *StPolicy { + return &StPolicy{ + client: client, + } +} diff --git a/pkg/cloudapi/tasks.go b/pkg/cloudapi/tasks.go new file mode 100644 index 0000000..58b7f23 --- /dev/null +++ b/pkg/cloudapi/tasks.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..397cd76 --- /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/v15/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..5438805 --- /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/v15/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..0680d41 --- /dev/null +++ b/pkg/cloudapi/tasks/models.go @@ -0,0 +1,143 @@ +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 +} + +// 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..4ad897b --- /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/v15/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/trunk.go b/pkg/cloudapi/trunk.go new file mode 100644 index 0000000..4723581 --- /dev/null +++ b/pkg/cloudapi/trunk.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/trunk" +) + +// Accessing the Trunk method group +func (ca *CloudAPI) Trunk() *trunk.Trunk { + return trunk.New(ca.client) +} diff --git a/pkg/cloudapi/trunk/get.go b/pkg/cloudapi/trunk/get.go new file mode 100644 index 0000000..5627f58 --- /dev/null +++ b/pkg/cloudapi/trunk/get.go @@ -0,0 +1,46 @@ +package trunk + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about a trunk +type GetRequest struct { + // ID of trunk + // Required: true + TrunkID uint64 `url:"id" json:"id" validate:"required"` +} + +// Get gets detailed information about a trunk as a ItemTrunk struct +func (t Trunk) Get(ctx context.Context, req GetRequest) (*ItemTrunk, error) { + res, err := t.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ItemTrunk{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed information about a trunk as an array of bytes +func (t Trunk) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/trunk/get" + + res, err := t.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudapi/trunk/list.go b/pkg/cloudapi/trunk/list.go new file mode 100644 index 0000000..f9566d2 --- /dev/null +++ b/pkg/cloudapi/trunk/list.go @@ -0,0 +1,64 @@ +package trunk + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get list of trunks +type ListRequest struct { + // Account access ID to filter by + AccountIDs []uint64 `url:"account_ids,omitempty" json:"account_ids,omitempty"` + + // ID of the trunk to filter by + IDs []uint64 `url:"ids,omitempty" json:"ids,omitempty"` + + // Sort by one of supported fields, format ± + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Trunk tags to filter by + TrunkTags string `url:"trunk_tags,omitempty" json:"trunk_tags,omitempty" validate:"omitempty,trunkTags"` + + // Page number + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Status + Status string `url:"status,omitempty" json:"status,omitempty"` +} + +// List gets list of all trunks as a ListTrunks struct +func (t Trunk) List(ctx context.Context, req ListRequest) (*ListTrunks, error) { + + res, err := t.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListTrunks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all trunks as an array of bytes +func (t Trunk) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/trunk/list" + + res, err := t.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudapi/trunk/models.go b/pkg/cloudapi/trunk/models.go new file mode 100644 index 0000000..cc42a96 --- /dev/null +++ b/pkg/cloudapi/trunk/models.go @@ -0,0 +1,62 @@ +package trunk + +type ItemTrunk struct { + + // List of account IDs with access to this trunk + AccountIDs []uint64 `json:"accountIds"` + + // Created at + CreatedAt uint64 `json:"created_at"` + + // Created by + CreatedBy string `json:"created_by"` + + // Deleted at + DeletedAt uint64 `json:"deleted_at"` + + // Deleted by + DeletedBy string `json:"deleted_by"` + + // Description of a trunk + Description string `json:"description"` + + // GUID + GUID uint64 `json:"guid"` + + // ID of a trunk + ID uint64 `json:"id"` + + // MAC + MAC string `json:"mac"` + + // MTU + MTU uint64 `json:"mtu"` + + // Name of a trunk + Name string `json:"name"` + + // Native VLAN ID + NativeVLANID uint64 `json:"nativeVlanId"` + + // OVS bridge name + OVSBridge string `json:"ovsBridge"` + + // If the trunk is enabled + Status string `json:"status"` + + // List of trunk tags (values between 1-4095) + TrunkTags string `json:"trunkTags"` + + // Updated at + UpdatedAt uint64 `json:"updated_at"` + + // Updated by + UpdatedBy string `json:"updated_by"` +} + +// List of trunks +type ListTrunks struct { + Data []ItemTrunk `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudapi/trunk/trunk.go b/pkg/cloudapi/trunk/trunk.go new file mode 100644 index 0000000..d685657 --- /dev/null +++ b/pkg/cloudapi/trunk/trunk.go @@ -0,0 +1,18 @@ +// API Actor API for trunk nerworks +package trunk + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to trunk +type Trunk struct { + client interfaces.Caller +} + +// Builder for trunk endpoints +func New(client interfaces.Caller) *Trunk { + return &Trunk{ + client, + } +} diff --git a/pkg/cloudapi/user.go b/pkg/cloudapi/user.go new file mode 100644 index 0000000..23df7ec --- /dev/null +++ b/pkg/cloudapi/user.go @@ -0,0 +1,7 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..28f5588 --- /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/v15/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..6172f83 --- /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/v15/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..8c1e7b7 --- /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/v15/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..3253c88 --- /dev/null +++ b/pkg/cloudapi/user/get_audit.go @@ -0,0 +1,61 @@ +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"` + + // 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"` +} + +// 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..586cf5f --- /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/v15/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..d9c982c --- /dev/null +++ b/pkg/cloudapi/user/models.go @@ -0,0 +1,178 @@ +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"` + + // 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..c214b3f --- /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/v15/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..2056f48 --- /dev/null +++ b/pkg/cloudapi/user/user.go @@ -0,0 +1,15 @@ +package user + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..ed2fc65 --- /dev/null +++ b/pkg/cloudapi/vfpool.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..4da9fe1 --- /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/v15/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..f346a84 --- /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/v15/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..48d7f4f --- /dev/null +++ b/pkg/cloudapi/vfpool/serialize.go @@ -0,0 +1,59 @@ +package vfpool + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..e0e05be --- /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/v15/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/vgpu.go b/pkg/cloudapi/vgpu.go new file mode 100644 index 0000000..0b50eb2 --- /dev/null +++ b/pkg/cloudapi/vgpu.go @@ -0,0 +1,8 @@ +package cloudapi + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/vgpu" + +// Accessing the VGPU method group +func (ca *CloudAPI) VGPU() *vgpu.VGPU { + return vgpu.New(ca.client) +} diff --git a/pkg/cloudapi/vgpu/ids.go b/pkg/cloudapi/vgpu/ids.go new file mode 100644 index 0000000..0ae6338 --- /dev/null +++ b/pkg/cloudapi/vgpu/ids.go @@ -0,0 +1,10 @@ +package vgpu + +// IDs gets array of VGPU IDs from ListVGPU struct +func (l ListVGPU) IDs() []uint64 { + res := make([]uint64, 0, len(l.Data)) + for _, v := range l.Data { + res = append(res, v.ID) + } + return res +} diff --git a/pkg/cloudapi/vgpu/list.go b/pkg/cloudapi/vgpu/list.go new file mode 100644 index 0000000..50f7c93 --- /dev/null +++ b/pkg/cloudapi/vgpu/list.go @@ -0,0 +1,88 @@ +package vgpu + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudapi/vgpu/list" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/vgpu/models.go b/pkg/cloudapi/vgpu/models.go new file mode 100644 index 0000000..696f4bf --- /dev/null +++ b/pkg/cloudapi/vgpu/models.go @@ -0,0 +1,69 @@ +package vgpu + +type ItemVGPU struct { + // 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"` + + // Bus number + BusNumber int `json:"bus_number"` + + // PCI Slot + PCISlot int `json:"pciSlot"` + + // PGPUID + PGPUID uint64 `json:"pgpuid"` + + // Profile ID + ProfileID uint64 `json:"profileId"` + + // RAM + RAM uint64 `json:"ram"` + + // Reference ID + ReferenceID string `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/cloudapi/vgpu/serialize.go b/pkg/cloudapi/vgpu/serialize.go new file mode 100644 index 0000000..3969185 --- /dev/null +++ b/pkg/cloudapi/vgpu/serialize.go @@ -0,0 +1,43 @@ +package vgpu + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/cloudapi/vgpu/vgpu.go b/pkg/cloudapi/vgpu/vgpu.go new file mode 100644 index 0000000..46be8af --- /dev/null +++ b/pkg/cloudapi/vgpu/vgpu.go @@ -0,0 +1,15 @@ +package vgpu + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/cloudapi/vins.go b/pkg/cloudapi/vins.go new file mode 100644 index 0000000..5f49261 --- /dev/null +++ b/pkg/cloudapi/vins.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..7bae3ae --- /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/v15/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..6efd32e --- /dev/null +++ b/pkg/cloudapi/vins/create_in_account.go @@ -0,0 +1,114 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // Enable security groups for VINS + // Required: false + // Default: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +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..ec6b14b --- /dev/null +++ b/pkg/cloudapi/vins/create_in_rg.go @@ -0,0 +1,108 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // Enable security groups for VINS + // Required: false + // Default: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +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..a611988 --- /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/v15/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..5c044d9 --- /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/v15/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..7cce3a0 --- /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/v15/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..0519191 --- /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/v15/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..4a5e8ea --- /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/v15/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..108a432 --- /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/v15/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..f05c832 --- /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/v15/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..25cf1fc --- /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/v15/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..187710e --- /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/v15/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..125fcb9 --- /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/v15/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..f285cd8 --- /dev/null +++ b/pkg/cloudapi/vins/list.go @@ -0,0 +1,92 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 status + // Required: false + Status string `url:"status,omitempty" json:"status,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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..2b72ded --- /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/v15/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/migrate_to_zone.go b/pkg/cloudapi/vins/migrate_to_zone.go new file mode 100644 index 0000000..d6ed2e4 --- /dev/null +++ b/pkg/cloudapi/vins/migrate_to_zone.go @@ -0,0 +1,42 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move VINS to another zone +type MigrateToZoneRequest struct { + // VINSID to move + // Required: true + VINSID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zone_id" json:"zone_id" validate:"required"` +} + +// MigrateToZone moves VINS instance to new zone +func (v VINS) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/vins/migrateToZone" + + 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/models.go b/pkg/cloudapi/vins/models.go new file mode 100644 index 0000000..2c48ca3 --- /dev/null +++ b/pkg/cloudapi/vins/models.go @@ -0,0 +1,846 @@ +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"` + + // VNC password + VNCPassword string `json:"vncPasswd"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` + + // Live migration job ID + LiveMigrationJobID uint64 `json:"live_migration_job_id"` +} + +// 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"` + + // Node ID + NodeID uint64 `json:"node_id"` + + // 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"` + + // Enable security groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // List of security groups + SecGroups []uint64 `json:"security_groups"` + + // SDNInterfaceID + SDNInterfaceID string `json:"sdn_interface_id"` + + // 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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 { + // Account ID + AccountID uint64 `json:"account_id"` + + // 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..a39b042 --- /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/v15/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..ca86632 --- /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/v15/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..a84ff89 --- /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/v15/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..4ba67eb --- /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/v15/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..fc3e615 --- /dev/null +++ b/pkg/cloudapi/vins/serialize.go @@ -0,0 +1,43 @@ +package vins + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..348cf14 --- /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/v15/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..3ce1438 --- /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/v15/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..c25e25f --- /dev/null +++ b/pkg/cloudapi/vins/static_route_add.go @@ -0,0 +1,50 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +// 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..b42e408 --- /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/v15/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..42acc66 --- /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/v15/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..15f28f2 --- /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/v15/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..7da58a7 --- /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/v15/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..657ba2e --- /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/v15/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/cloudapi/zone.go b/pkg/cloudapi/zone.go new file mode 100644 index 0000000..2897ced --- /dev/null +++ b/pkg/cloudapi/zone.go @@ -0,0 +1,10 @@ +package cloudapi + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi/zone" +) + +// Accessing the Zone method group +func (ca *CloudAPI) Zone() *zone.Zone { + return zone.New(ca.client) +} diff --git a/pkg/cloudapi/zone/filter.go b/pkg/cloudapi/zone/filter.go new file mode 100644 index 0000000..365108c --- /dev/null +++ b/pkg/cloudapi/zone/filter.go @@ -0,0 +1,53 @@ +package zone + +// FilterByID returns ListZones with specified ID. +func (list ListZones) FilterByID(id uint64) ListZones { + predicate := func(izone ItemZone) bool { + return izone.ID == id + } + + return list.FilterFunc(predicate) +} + +// FilterByName returns ListZones with specified Name. +func (list ListZones) FilterByName(name string) ListZones { + predicate := func(izone ItemZone) bool { + return izone.Name == name + } + + return list.FilterFunc(predicate) +} + +// FilterByStatus returns ListZones with specified Status. +func (list ListZones) FilterByStatus(status string) ListZones { + predicate := func(izone ItemZone) bool { + return izone.Status == status + } + + return list.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListZones based on a user-specified predicate. +func (list ListZones) FilterFunc(predicate func(ItemZone) bool) ListZones { + var result ListZones + + for _, item := range list.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemZone +// If none was found, returns an empty struct. +func (list ListZones) FindOne() ItemZone { + if list.EntryCount == 0 { + return ItemZone{} + } + + return list.Data[0] +} diff --git a/pkg/cloudapi/zone/filter_test.go b/pkg/cloudapi/zone/filter_test.go new file mode 100644 index 0000000..973c638 --- /dev/null +++ b/pkg/cloudapi/zone/filter_test.go @@ -0,0 +1,86 @@ +package zone + +import "testing" + +var zones = ListZones{ + Data: []ItemZone{ + + { + ID: 2, + GUID: 0, + GID: 0, + Name: "System Config", + Description: "", + Deletable: true, + Status: "LOCKED", + CreatedTime: 1640995200, // 2022-01-01 + UpdatedTime: 1640995200, + NodeIDs: nil, + }, + { + ID: 5, + GUID: 5500, + GID: 6600, + Name: "ssss Nodes", + Description: " infrastructure", + Deletable: true, + Status: "DISABLED", + CreatedTime: 1577836800, // 2020-01-01 + UpdatedTime: 1580515200, // 2020-02-01 + NodeIDs: []uint64{777, 888, 999}, + }, + { + ID: 10, + GUID: 5500, + GID: 6600, + Name: "node", + Description: "infrastructure", + Deletable: true, + Status: "DISABLED", + CreatedTime: 1577836800, + UpdatedTime: 1580515200, + NodeIDs: []uint64{777, 888, 999}, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := zones.FilterByID(10).FindOne() + + if actual.ID != 10 { + t.Fatal("expected ID 10, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + name := "node" + actual := zones.FilterByName(name).FindOne() + + if actual.Name != name { + t.Fatal("expected ", name, " found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := zones.FilterByStatus("DISABLED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "DISABLED" { + t.Fatal("expected Status 'DISABLED', found: ", item.Status) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := zones.FilterFunc(func(ien ItemZone) bool { + return ien.Deletable == true + }) + + if len(actual.Data) != 3 { + t.Fatal("expected 3 elements, found: ", len(actual.Data)) + } +} diff --git a/pkg/cloudapi/zone/get.go b/pkg/cloudapi/zone/get.go new file mode 100644 index 0000000..83e0076 --- /dev/null +++ b/pkg/cloudapi/zone/get.go @@ -0,0 +1,46 @@ +package zone + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get detailed information about zone +type GetRequest struct { + // ID of zone + // Required: true + ID uint64 `url:"id" json:"id" validate:"required"` +} + +// Get gets detailed information about zone struct +func (e Zone) Get(ctx context.Context, req GetRequest) (*RecordZone, error) { + res, err := e.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordZone{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed information about zone as an array of bytes +func (e Zone) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/zone/get" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/zone/ids.go b/pkg/cloudapi/zone/ids.go new file mode 100644 index 0000000..33f6193 --- /dev/null +++ b/pkg/cloudapi/zone/ids.go @@ -0,0 +1,10 @@ +package zone + +// IDs gets array of IDs from ListZones struct +func (le ListZones) 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/zone/list.go b/pkg/cloudapi/zone/list.go new file mode 100644 index 0000000..1a27644 --- /dev/null +++ b/pkg/cloudapi/zone/list.go @@ -0,0 +1,84 @@ +package zone + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get list of zones +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 deletable + // Required: false + Deletable bool `url:"deletable,omitempty" json:"deletable,omitempty"` + + // Find by node ID + // Required: false + NodeID uint64 `url:"nodeId,omitempty" json:"nodeId,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 zones as a ListZones struct +func (e Zone) List(ctx context.Context, req ListRequest) (*ListZones, error) { + + res, err := e.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListZones{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all available zones as an array of bytes +func (e Zone) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudapi/zone/list" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudapi/zone/migrate_to_zone.go b/pkg/cloudapi/zone/migrate_to_zone.go new file mode 100644 index 0000000..6be865f --- /dev/null +++ b/pkg/cloudapi/zone/migrate_to_zone.go @@ -0,0 +1 @@ +package zone diff --git a/pkg/cloudapi/zone/models.go b/pkg/cloudapi/zone/models.go new file mode 100644 index 0000000..bffd048 --- /dev/null +++ b/pkg/cloudapi/zone/models.go @@ -0,0 +1,186 @@ +package zone + +// CPU alignment profile +type CpuAlignmentProfile struct { + // Profile name + Name string `json:"name"` + + // Vendor + Vendor string `json:"vendor"` + + // Model + Model string `json:"model"` +} + +type ListZones struct { + // Entry count + EntryCount uint64 `json:"entryCount"` + + // Data + Data []ItemZone `json:"data"` +} + +// Detailed information about the zone record +type RecordZone struct { + // If true, all nodes belonging to the given zone will be marked for autostart + AutoStart bool `json:"autostart"` + + // ID + ID uint64 `json:"id"` + + // GUID + GUID uint64 `json:"guid"` + + // GID + GID uint64 `json:"gid"` + + // Name + Name string `json:"name"` + + // List of associated account IDs + AccountIDs []uint64 `json:"accountIds"` + + // List of associated bservice IDs + BserviceIDs []uint64 `json:"bserviceIds"` + + // List of associated compute IDs + ComputeIDs []uint64 `json:"computeIds"` + + // Description + Description string `json:"description"` + + // Deletable flag + Deletable bool `json:"deletable"` + + // List of associated ExtNet IDs + ExtnetIDs []uint64 `json:"extnetIds"` + + // List of associated K8s IDs + K8SIDs []uint64 `json:"k8sIds"` + + // List of associated LB IDs + LBIDs []uint64 `json:"lbsIds"` + + // Status + Status string `json:"status"` + + // Created timestamp + CreatedTime uint64 `json:"createdTime"` + + // Updated timestamp + UpdatedTime uint64 `json:"updatedTime"` + + // List of associated Node IDs + NodeIDs []uint64 `json:"nodeIds"` + + // List of associated VINS IDs + VinsIDs []uint64 `json:"vinsIds"` + + // DRS + DRS bool `json:"drs"` + + // DRS UID + DRSUID string `json:"drs_uid"` + + // App ID + AppID string `json:"app_id"` + + // Decort URL + DecortURL string `json:"decort_url"` + + // DRS Name + DRSName string `json:"drs_name"` + + // SSO URL + SSOURL string `json:"sso_url"` + + // SSO type + SSOType string `json:"sso_type"` + + // Ping address + PingAddr string `json:"ping_addr"` + + // Broadcast address + BroadcastAddr string `json:"broadcast_addr"` + + // Skip ssl verify + SSLSkipVerify bool `json:"ssl_skip_verify"` + + // Domain + Domain string `json:"domain"` + + // CPU alignment profiles + CpuAlignmentProfiles []CpuAlignmentProfile `json:"cpu_alignment_profiles"` +} + +// A zone item from a list +type ItemZone struct { + // App ID + AppID string `json:"app_id"` + + // If true, all nodes belonging to the given zone will be marked for autostart + AutoStart bool `json:"autostart"` + + // Created timestamp + CreatedTime uint64 `json:"createdTime"` + + // Decort URL + DecortURL string `json:"decort_url"` + + // Deletable flag + Deletable bool `json:"deletable"` + + // Description + Description string `json:"description"` + + // DRS + DRS bool `json:"drs"` + + // DRS Name + DRSName string `json:"drs_name"` + + // DRS UID + DRSUID string `json:"drs_uid"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // List of associated Node IDs + NodeIDs []uint64 `json:"nodeIds"` + + // SSO URL + SSOURL string `json:"sso_url"` + + // SSO type + SSOType string `json:"sso_type"` + + // Status + Status string `json:"status"` + + // Updated timestamp + UpdatedTime uint64 `json:"updatedTime"` + + // Ping address + PingAddr string `json:"ping_addr"` + + // Broadcast address + BroadcastAddr string `json:"broadcast_addr"` + + // Skip ssl verify + SSLSkipVerify bool `json:"ssl_skip_verify"` + + // Domain + Domain string `json:"domain"` + + // CPU alignment profiles + CpuAlignmentProfiles []CpuAlignmentProfile `json:"cpu_alignment_profiles"` +} diff --git a/pkg/cloudapi/zone/serialize.go b/pkg/cloudapi/zone/serialize.go new file mode 100644 index 0000000..8215a55 --- /dev/null +++ b/pkg/cloudapi/zone/serialize.go @@ -0,0 +1,43 @@ +package zone + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 (list ListZones) Serialize(params ...string) (serialization.Serialized, error) { + if list.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(list, prefix, indent) + } + + return json.Marshal(list) +} + +// 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 (item RecordZone) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(item, prefix, indent) + } + + return json.Marshal(item) +} diff --git a/pkg/cloudapi/zone/zone.go b/pkg/cloudapi/zone/zone.go new file mode 100644 index 0000000..a15987f --- /dev/null +++ b/pkg/cloudapi/zone/zone.go @@ -0,0 +1,18 @@ +// API Actor for use zones +package zone + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to zone +type Zone struct { + client interfaces.Caller +} + +// Builder for zone endpoints +func New(client interfaces.Caller) *Zone { + return &Zone{ + client, + } +} diff --git a/pkg/cloudbroker/account.go b/pkg/cloudbroker/account.go new file mode 100644 index 0000000..d79d951 --- /dev/null +++ b/pkg/cloudbroker/account.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..0c3c214 --- /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/v15/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_storage_policy.go b/pkg/cloudbroker/account/add_storage_policy.go new file mode 100644 index 0000000..9548b8e --- /dev/null +++ b/pkg/cloudbroker/account/add_storage_policy.go @@ -0,0 +1,46 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AddStoragePolicyRequest struct for adding storage policy to the account +type AddStoragePolicyRequest struct { + // ID of account to add to + // Required: true + AccountID uint64 `url:"account_id" json:"account_id" validate:"required"` + + // ID of the storage policy to which to connect account + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // Limit storage resources GB. Or -1 unlimit + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} + +// AddStoragePolicy add storage policy to the account. +func (a Account) AddStoragePolicy(ctx context.Context, req AddStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/add_storage_policy" + + 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/add_user.go b/pkg/cloudbroker/account/add_user.go new file mode 100644 index 0000000..5fa0190 --- /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/v15/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/add_zone.go b/pkg/cloudbroker/account/add_zone.go new file mode 100644 index 0000000..c4e809e --- /dev/null +++ b/pkg/cloudbroker/account/add_zone.go @@ -0,0 +1,42 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AddZoneRequest struct for adding zone to account for a user +type AddZoneRequest struct { + // ID of account to add to + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // IDs of zones + // Required: true + ZoneIDs []uint64 `url:"zoneIds" json:"zoneIds" validate:"required"` +} + +// AddUser gives a user access rights. +func (a Account) AddZone(ctx context.Context, req AddZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/addZone" + + 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..5d45daf --- /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/v15/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..83434a9 --- /dev/null +++ b/pkg/cloudbroker/account/create.go @@ -0,0 +1,103 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct for creating account +type CreateRequest struct { + // Display name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // 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"` + + // Storage policies + // Required: false + StoragePolicies []StoragePolicy `url:"-" json:"storage_policies,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 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, dpdk, changemac, trunk + // Required: false + ComputeFeatures []string `url:"computeFeatures,omitempty" json:"computeFeatures,omitempty" validate:"omitempty,computeFeatures"` + + // Default zone ID + // Required: false + DefaultZoneID uint64 `url:"defaultZoneId,omitempty" json:"defaultZoneId,omitempty"` + + // Zones + // Required: false + ZoneIDs []uint64 `url:"zoneIds,omitempty" json:"zoneIds,omitempty"` +} + +type StoragePolicy struct { + ID uint64 `url:"id" json:"id"` + Limit int `url:"limit" json:"limit"` +} + +// 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.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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/del_storage_policy.go b/pkg/cloudbroker/account/del_storage_policy.go new file mode 100644 index 0000000..b2444d9 --- /dev/null +++ b/pkg/cloudbroker/account/del_storage_policy.go @@ -0,0 +1,42 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DelStoragePolicyRequest struct for deleting storage policy to the account +type DelStoragePolicyRequest struct { + // ID of account + // Required: true + AccountID uint64 `url:"account_id" json:"account_id" validate:"required"` + + // ID of the storage policy to which to disconnect account + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// DelStoragePolicy delete storage policy to the account. +func (a Account) DelStoragePolicy(ctx context.Context, req DelStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/del_storage_policy" + + 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/delete.go b/pkg/cloudbroker/account/delete.go new file mode 100644 index 0000000..6cc7356 --- /dev/null +++ b/pkg/cloudbroker/account/delete.go @@ -0,0 +1,40 @@ +package account + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Name of account + // Required: false + Name string `url:"name,omitempty" json:"name,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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/delete" + + result, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(result), nil +} diff --git a/pkg/cloudbroker/account/delete_accounts.go b/pkg/cloudbroker/account/delete_accounts.go new file mode 100644 index 0000000..f3b99d0 --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/deleteAccounts" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/account/delete_user.go b/pkg/cloudbroker/account/delete_user.go new file mode 100644 index 0000000..afe5fcf --- /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/v15/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..54b0a4f --- /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/v15/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..8088cc2 --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/disableAccounts" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/account/enable.go b/pkg/cloudbroker/account/enable.go new file mode 100644 index 0000000..74fde9a --- /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/v15/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..5a31418 --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/enableAccounts" + + res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), 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..c8ccae9 --- /dev/null +++ b/pkg/cloudbroker/account/filter_test.go @@ -0,0 +1,155 @@ +package account + +import ( + "testing" +) + +var accounts = ListAccounts{ + Data: []ItemAccount{ + { + InfoAccount: InfoAccount{ + CreatedTime: 1676878820, + DeletedTime: 0, + ID: 132847, + Name: "std_2", + Status: "CONFIRMED", + UpdatedTime: 1676645275, + }, + ACL: []ACL{ + { + Explicit: true, + GUID: "", + Right: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "not_really_timofey_tkachev_1@decs3o", + }, + }, + }, + { + InfoAccount: InfoAccount{ + CreatedTime: 1676645275, + DeletedTime: 1677723401, + ID: 132846, + Name: "std", + Status: "DELETED", + UpdatedTime: 1676645275, + }, + ACL: []ACL{ + { + Explicit: true, + GUID: "", + Right: "CXDRAU", + Status: "CONFIRMED", + Type: "U", + UserGroupID: "timofey_tkachev_1@decs3o", + }, + }, + }, + { + InfoAccount: InfoAccount{ + CreatedTime: 1676883850, + DeletedTime: 1676883899, + ID: 132848, + Name: "std_broker", + Status: "DELETED", + UpdatedTime: 1676878820, + }, + 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", + }, + }, + }, + }, + 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..6d37fb4 --- /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/v15/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..7fe1239 --- /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/v15/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..bfe67e2 --- /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/v15/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..b1141f1 --- /dev/null +++ b/pkg/cloudbroker/account/list.go @@ -0,0 +1,76 @@ +package account + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..26b45a3 --- /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/v15/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..e9ce1f3 --- /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/v15/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..1eb6e8b --- /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/v15/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..c80e040 --- /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/v15/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..d06001a --- /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/v15/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..7328663 --- /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/v15/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..82fc0b3 --- /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/v15/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..e748623 --- /dev/null +++ b/pkg/cloudbroker/account/models.go @@ -0,0 +1,626 @@ +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"` +} + +// Policy +type Policy struct { + // Size of the disk + DiskSize float64 `json:"disksize"` + + // Max size of the disk + DiskSizeMax float64 `json:"disksizemax"` + + // SEPs used + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +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"` + + // Number of grafic cores + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` + + // Policies + Policies map[string]Policy `json:"policies"` +} + +// 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"` +} + +// Access Control List with emails field +type ACLWithEmails struct { + // ACL + ACL + + // Emails + Emails []string `json:"emails"` +} + +// 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"` + + // GPUUnits + GPUUnits float64 `json:"gpu_units"` + + // Storage policies + StoragePolicies []StoragePolicy `json:"storage_policy"` +} + +// Main information about account +type InfoAccount struct { + // DCLocation + DCLocation string `json:"DCLocation"` + + // 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 uint64 `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"` + + // Description + Description string `json:"desc"` + + // 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"` + + // Storage policy ids + StoragePolicyIDs []uint64 `json:"storage_policy_ids"` + + // UniqPools + UniqPools []string `json:"uniqPools"` + + // Updated By + UpdatedBy string `json:"updatedBy"` + + // UpdatedTime + UpdatedTime uint64 `json:"updatedTime"` + + // Version + Version uint64 `json:"version"` + + // List of VINS IDs + VINS []uint64 `json:"vins"` + + // Default zone ID + DefaultZoneID uint64 `json:"defaultZoneId"` +} + +// Deatailed information about the account zone +type ZoneID struct { + // ID of zone + ID int64 `json:"id"` + + // Name of zone + Name string `json:"name"` +} + +// Deatailed information about account +type RecordAccount struct { + // Main information about account + InfoAccount + + // Access Control List + ACL []ACLWithEmails `json:"acl"` + + // Zones IDs + ZoneIDs []ZoneID `json:"zoneIds"` +} + +// More information about account +type ItemAccount struct { + + // Access Control List + ACL []ACL `json:"acl"` + + // Main information about account + InfoAccount + + // Zones + ZoneIDs []uint64 `json:"zoneIds"` +} + +// 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"` + + // Number of GPU + GPU int64 `json:"gpu"` + + // Number of RAM + RAM int64 `json:"ram"` + + // SEPs number + SEPs uint64 `json:"seps"` + + // Policies + Policies map[string]Policy `json:"policies"` +} + +// 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"` + + // Description + Description string `json:"desc"` + + // 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/remove_zone.go b/pkg/cloudbroker/account/remove_zone.go new file mode 100644 index 0000000..7fc0cf5 --- /dev/null +++ b/pkg/cloudbroker/account/remove_zone.go @@ -0,0 +1,42 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// RemoveZoneRequest struct for removing zone from account for a user +type RemoveZoneRequest struct { + // ID of account to add to + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // IDs of zones + // Required: true + ZoneIDs []uint64 `url:"zoneIds" json:"zoneIds" validate:"required"` +} + +// RemoveZone removes zones with ids provided from a user. +func (a Account) RemoveZone(ctx context.Context, req RemoveZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/removeZone" + + 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/restore.go b/pkg/cloudbroker/account/restore.go new file mode 100644 index 0000000..dd1631f --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/account/restore" + + result, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(result), 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..d88a6b1 --- /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/v15/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..6d8d0c2 --- /dev/null +++ b/pkg/cloudbroker/account/serialize.go @@ -0,0 +1,43 @@ +package account + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..b9bf90d --- /dev/null +++ b/pkg/cloudbroker/account/set_cpu_allocation_parameter.go @@ -0,0 +1,43 @@ +package account + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..58cb3da --- /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/v15/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 uint64 `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..2e561c6 --- /dev/null +++ b/pkg/cloudbroker/account/update.go @@ -0,0 +1,93 @@ +package account + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update account +type UpdateRequest struct { + // ID of account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Description + // Required: false + Description string `url:"desc,omitempty" json:"desc,omitempty"` + + // 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 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"` + + // Storage policies + // Required: false + StoragePolicies []StoragePolicy `url:"-" json:"storage_policies,omitempty"` + + // 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"` + + // Default zone ID + // Required: false + DefaultZoneID uint64 `url:"defaultZoneId,omitempty" json:"defaultZoneId,omitempty"` + + // CPU allocation parameter + // Required: false + CpuAllocationParameter string `url:"cpu_allocation_parameter,omitempty" json:"cpu_allocation_parameter,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 := "/cloudbroker/account/update" + + res, err := a.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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..5de7f6b --- /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/v15/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, dpdk, changemac, trunk + // 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..ab73b90 --- /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/v15/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..5ebbbda --- /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/v15/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..a9e73c5 --- /dev/null +++ b/pkg/cloudbroker/apiaccess.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..b5fb118 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/api_find.go @@ -0,0 +1,49 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// APIFindRequest struct for finding apiaccess groups. +type APIFindRequest struct { + //API group to find (cloudbroker, cloudapi, etc) + //Available values : cloudapi, cloudbroker, system + //Required: true + APIGroup string `url:"api_group" json:"api_group" validate:"required,apiGroup"` + + //API object to find (compute, vins, etc ) + //Required: true + APIObject string `url:"api_object" json:"api_object" validate:"required"` + + //API endpoint to find (delete, create, etc) + //Required: true + APIMethod string `url:"api_method" json:"api_method" 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..2f20d59 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/apiaccess.go @@ -0,0 +1,15 @@ +package apiaccess + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..cbf1435 --- /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/v15/internal/validators" +) + +type APIString string + +// APIsExcludeRequest struct for removing api from access group. +type APIsExcludeRequest struct { + // APIAccess group ID + // Required: true + APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" 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..7a2bb35 --- /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/v15/internal/validators" +) + +// APIsIncludeRequest struct for adding api to access group. +type APIsIncludeRequest struct { + // APIAccess group ID. + // Required: true + APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" 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..1ddf14b --- /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/v15/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:"apiaccess_id" json:"apiaccess_id" 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..eb7d4b7 --- /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/v15/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..3cc9816 --- /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/v15/internal/validators" +) + +// DeleteRequest struct for deleting apiaccess group. +type DeleteRequest struct { + // APIAccess group ID. + // Required: true + APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" 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..08faf2d --- /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/v15/internal/validators" +) + +// DescUpdateRequest struct for updating apiaccess group description. +type DescUpdateRequest struct { + // APIAccess group ID. + // Required: true + APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" 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..8897e53 --- /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/v15/internal/validators" +) + +// GetRequest struct to get apiaccess group. +type GetRequest struct { + // APIAccess group ID. + // Required: true + APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" 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_default.go b/pkg/cloudbroker/apiaccess/get_default.go new file mode 100644 index 0000000..88254cd --- /dev/null +++ b/pkg/cloudbroker/apiaccess/get_default.go @@ -0,0 +1,26 @@ +package apiaccess + +import ( + "context" + "encoding/json" + "net/http" +) + +// GetDefault gets default apiaccess group +func (a APIAccess) GetDefault(ctx context.Context) (*ItemAPIAccessDefault, error) { + url := "/cloudbroker/apiaccess/getDefault" + + info := ItemAPIAccessDefault{} + + 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_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/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..0f30a73 --- /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/v15/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 description + // Required: false + Desc string `url:"desc,omitempty" json:"desc,omitempty"` + + // Find by created after time (unix timestamp) + // Required: false + CreatedAfter uint64 `url:"created_after,omitempty" json:"created_after,omitempty"` + + // Find by created before time (unix timestamp) + // Required: false + CreatedBefore uint64 `url:"created_before,omitempty" json:"created_before,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size, 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/models.go b/pkg/cloudbroker/apiaccess/models.go new file mode 100644 index 0000000..61a1d84 --- /dev/null +++ b/pkg/cloudbroker/apiaccess/models.go @@ -0,0 +1,206 @@ +package apiaccess + +type ItemAPIAccess struct { + // APIs + APIs APIsEndpoints `json:"apis"` + + CreatedTime uint64 `json:"created_at"` + + // Is default + Default bool `json:"default"` + + // Deleted time + DeletedTime uint64 `json:"deleted_at"` + + // Description + Description string `json:"desc"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Is protected + Protected bool `json:"protected"` + + // Status + Status string `json:"status"` + + // Updated time + UpdatedTime uint64 `json:"updated_at"` +} + +type ItemAPIAccessDefault struct { + // APIs + APIs APIsEndpoints `json:"apis"` + + // Created at + CreatedAt uint64 `json:"created_at"` + + // Is default + Default bool `json:"default"` + + // Deleted at + DeletedAt uint64 `json:"deleted_at"` + + // Description + Description string `json:"decs"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // Protected + Protected bool `json:"protected"` + + // Status + Status string `json:"status"` + + //Updated at + UpdatedAt uint64 `json:"updated_at"` +} + +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"` + DPDK []string `json:"dpdk,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"` + DPDK []string `json:"dpdk,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"` + 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"` + Node []string `json:"node,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..f0c53b5 --- /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/v15/internal/validators" +) + +// SetDefaultRequest struct for setting default apiaccess group. +type SetDefaultRequest struct { + // APIAccess group ID + // Required: true + APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" 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..6ccff08 --- /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/v15/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:"minuend_group_id" json:"minuend_group_id" validate:"required"` + + // ID of the API access group which is subtracted. This group is unchanged. + SubtrahendID uint64 `url:"subtrahend_group_id" json:"subtrahend_group_id" 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..2fb4048 --- /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/v15/internal/validators" +) + +// UnionRequest struct for union. +type UnionRequest struct { + // Recipient apiaccess group ID + // Required: true + RecipientID uint64 `url:"recipient_group_id" json:"recipient_group_id" validate:"required"` + + // Donor apiaccess group ID + // Required: true + DonorID uint64 `url:"donor_group_id" json:"donor_group_id" 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..5974f0d --- /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/v15/internal/validators" +) + +// UpdateRequest struct for updating apis of apiaccess group. +type UpdateRequest struct { + // APIAccess group ID + // Required: true + APIAccessID uint64 `url:"apiaccess_id" json:"apiaccess_id" 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..7925f29 --- /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/v15/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:"apiaccess_id" json:"apiaccess_id" 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..bd32f04 --- /dev/null +++ b/pkg/cloudbroker/audit.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/audit" +) + +// Accessing the Audit 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..01bea61 --- /dev/null +++ b/pkg/cloudbroker/audit/audit.go @@ -0,0 +1,15 @@ +package audit + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..de9f2fa --- /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.MethodGet, url, nil) + if err != nil { + return nil, err + } + + return res, nil +} diff --git a/pkg/cloudbroker/audit/filter.go b/pkg/cloudbroker/audit/filter.go new file mode 100644 index 0000000..82f80e6 --- /dev/null +++ b/pkg/cloudbroker/audit/filter.go @@ -0,0 +1,81 @@ +package audit + +// FilterByID returns ListAudits with specified ID. +func (la ListAudits) FilterByID(guid string) ListAudits { + predicate := func(ia ItemAudit) bool { + return ia.GUID == guid + } + + return la.FilterFunc(predicate) +} + +// FilterByCall returns ListAudits with specified call. +func (la ListAudits) FilterByCall(call string) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.Call == call + } + + return la.FilterFunc(predicate) +} + +// FilterByCorrelationID returns ListAudits with specified correlation id. +func (la ListAudits) FilterByCorrelationID(correlationID string) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.CorrelationID == correlationID + } + + return la.FilterFunc(predicate) +} + +// FilterByRemoteAddr returns ListAudits with specified remote address. +func (la ListAudits) FilterByRemoteAddr(remoteAddr string) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.RemoteAddr == remoteAddr + } + + return la.FilterFunc(predicate) +} + +// FilterByUser returns ListAudits with specified user name. +func (la ListAudits) FilterByUser(user string) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.User == user + } + + return la.FilterFunc(predicate) +} + +// FilterByStatusCode return ListAudits with specified status code. +func (la ListAudits) FilterByStatusCode(statusCode uint64) ListAudits { + predicate := func(ic ItemAudit) bool { + return ic.StatusCode == statusCode + } + + return la.FilterFunc(predicate) + +} + +// FilterFunc allows filtering ListAudits based on a user-specified predicate. +func (la ListAudits) FilterFunc(predicate func(ItemAudit) bool) ListAudits { + var result ListAudits + + for _, item := range la.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemAudit +// If none was found, returns an empty struct. +func (la ListAudits) FindOne() ItemAudit { + if len(la.Data) == 0 { + return ItemAudit{} + } + + return la.Data[0] +} diff --git a/pkg/cloudbroker/audit/filter_test.go b/pkg/cloudbroker/audit/filter_test.go new file mode 100644 index 0000000..8f09a1b --- /dev/null +++ b/pkg/cloudbroker/audit/filter_test.go @@ -0,0 +1,115 @@ +package audit + +import ( + "testing" +) + +var audits = ListAudits{ + Data: []ItemAudit{ + { + Args: "[]", + Call: "/restmachine/cloudapi/audit/linkedJobs", + GUID: "550e8400-e29b-41d4-a716-446655440001", + CorrelationID: "550e8400-e29b-41d4-a716-446655440001", + Kwargs: `{\"audit_guid\":\"dd8623a1-a887-48c1-a500-c10210d404cf\"}`, + RemoteAddr: "192.168.1.100", + ResponseTime: 1, + Result: `[]`, + StatusCode: 200, + Timestamp: 1640995200, + TimestampEnd: 1640995201, + User: "test@example.com", + TTL: "2025-07-31T14:22:57.028000", + }, + { + Args: "[]", + Call: "/restmachine/cloudapi/audit/test", + GUID: "550e8400-e29b-41d4-a716-446655440002", + CorrelationID: "550e8400-e29b-41d4-a716-446655440002", + Kwargs: `{\"audit_guid\":\"dd8623a1-a887-48c1-a500-c10210d404cf\"}`, + RemoteAddr: "192.168.1.105", + ResponseTime: 5, + Result: `[]`, + StatusCode: 400, + Timestamp: 1640995200, + TimestampEnd: 1640995201, + User: "test2@example.com", + TTL: "2025-07-31T14:22:57.028000", + }, + }, + EntryCount: 2, +} + +func TestFilterByID(t *testing.T) { + actual := audits.FilterByID("550e8400-e29b-41d4-a716-446655440002").FindOne() + + if actual.GUID != "550e8400-e29b-41d4-a716-446655440002" { + t.Fatal("expected GUID 550e8400-e29b-41d4-a716-446655440002, found: ", actual.GUID) + } + + actualEmpty := audits.FilterByID("") + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByCorrelationID(t *testing.T) { + actual := audits.FilterByCorrelationID("550e8400-e29b-41d4-a716-446655440002").FindOne() + + if actual.CorrelationID != "550e8400-e29b-41d4-a716-446655440002" { + t.Fatal("expected GUID 550e8400-e29b-41d4-a716-446655440002, found: ", actual.CorrelationID) + } + + actualEmpty := audits.FilterByCorrelationID("") + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByRemoteAddr(t *testing.T) { + actual := audits.FilterByRemoteAddr("192.168.1.100").FindOne() + + if actual.RemoteAddr != "192.168.1.100" { + t.Fatal("expected remote address 192.168.1.100, found: ", actual.RemoteAddr) + } + + actualEmpty := audits.FilterByRemoteAddr("") + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByUser(t *testing.T) { + actual := audits.FilterByUser("test@example.com").FindOne() + + if actual.User != "test@example.com" { + t.Fatal("expected user test@example.com, found: ", actual.RemoteAddr) + } + + actualEmpty := audits.FilterByUser("") + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterByCall(t *testing.T) { + actual := audits.FilterByCall("/restmachine/cloudapi/audit/test").FindOne() + + if actual.Call != "/restmachine/cloudapi/audit/test" { + t.Fatal("expected call /restmachine/cloudapi/audit/test, found: ", actual.Call) + } +} + +func TestFilterByStatusCode(t *testing.T) { + actual := audits.FilterByStatusCode(200) + + for _, item := range actual.Data { + if item.StatusCode != 200 { + t.Fatal("expected 200 status code, found: ", item.StatusCode) + } + } +} diff --git a/pkg/cloudbroker/audit/get.go b/pkg/cloudbroker/audit/get.go new file mode 100644 index 0000000..fa336e7 --- /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/v15/internal/validators" +) + +// GetRequest struct to get information about account +type GetRequest struct { + // Audit GUID + // Required: true + AuditGuid string `url:"audit_guid" json:"audit_guid" 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..e8e3690 --- /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/v15/internal/validators" +) + +// LinkedJobsRequest struct to get information about jobs linked with Audit +type LinkedJobsRequest struct { + // Audit GUID + // Required: true + AuditGuid string `url:"audit_guid" json:"audit_guid" 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..aa88325 --- /dev/null +++ b/pkg/cloudbroker/audit/list.go @@ -0,0 +1,128 @@ +package audit + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to give list of account audits +type ListRequest struct { + + // Find all audits after point in time (unixtime) + // Required: false + TimestampAt uint64 `url:"timestamp_at,omitempty" json:"timestamp_at,omitempty"` + + // Find all audits before point in time (unixtime) + // Required: false + TimestampTo uint64 `url:"timestamp_to,omitempty" json:"timestamp_to,omitempty"` + + // Find by user (Mongo RegExp supported) + // Required: false + User string `url:"user,omitempty" json:"user,omitempty"` + + // Find by api endpoint (Mongo RegExp supported) + // Required: false + Call string `url:"call,omitempty" json:"call,omitempty"` + + // Find by request id + // Required: false + RequestID string `url:"request_id,omitempty" json:"request_id,omitempty"` + + // Find by HTTP min status code + // Required: false + MinStatusCode uint64 `url:"min_status_code,omitempty" json:"min_status_code,omitempty"` + + // Find by HTTP max status code + // Required: false + MaxStatusCode uint64 `url:"max_status_code,omitempty" json:"max_status_code,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Find by resource group id + // Required: false + RGID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"` + + // Find by compute id + // Required: false + ComputeID uint64 `url:"compute_id,omitempty" json:"compute_id,omitempty"` + + // Find by account id + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` + + // Find by vins id + // Required: false + VINSID uint64 `url:"vins_id,omitempty" json:"vins_id,omitempty"` + + // Find by service id + // Required: false + ServiceID uint64 `url:"service_id,omitempty" json:"service_id,omitempty"` + + // Find by k8s id + // Required: false + K8SID uint64 `url:"k8s_id,omitempty" json:"k8s_id,omitempty"` + + // Find by flipgroup id + // Required: false + FLIPGroupID uint64 `url:"flipgroup_id,omitempty" json:"flipgroup_id,omitempty"` + + // Find by load balancer id + // Required: false + LBID uint64 `url:"lb_id,omitempty" json:"lb_id,omitempty"` + + // Find by sep id + // Required: false + SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"` + + // Find by node id + // Required: false + NodeID uint64 `url:"node_id,omitempty" json:"node_id,omitempty"` + + // Exclude audit lines from response + // Required: false + ExcludeAuditLines bool `url:"exclude_audit_lines,omitempty" json:"exclude_audit_lines,omitempty"` +} + +// List gets audit records for the specified account object +func (a Audit) List(ctx context.Context, req ListRequest) (*ListAudits, error) { + + res, err := a.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListAudits{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of audit records an array of bytes +func (a Audit) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/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..f232bf5 --- /dev/null +++ b/pkg/cloudbroker/audit/models.go @@ -0,0 +1,141 @@ +package audit + +// Main info about audit +type ItemAudit struct { + // Args + Args string `json:"args"` + + // Call + Call string `json:"call"` + + // GUID + GUID string `json:"guid"` + + // Correlation ID + CorrelationID string `json:"correlation_id"` + + // Kwargs + Kwargs string `json:"kwargs"` + + // RemoteAddr + RemoteAddr string `json:"remote_addr"` + + // Response time + ResponseTime float64 `json:"responsetime"` + + // Result + Result string `json:"result"` + + // Status code + StatusCode uint64 `json:"statuscode"` + + // Timestamp + Timestamp float64 `json:"timestamp"` + + // Timestamp End + TimestampEnd float64 `json:"timestampEnd"` + + // User + User string `json:"user"` + + // TTL + TTL string `json:"_ttl"` +} + +// List of audits +type ListAudits struct { + // Data + Data []ItemAudit `json:"data"` + + // EntryCount + EntryCount uint64 `json:"entryCount"` +} + +// Main info about audit +type RecordAudit struct { + + // Arguments + Arguments string `json:"args"` + + // Call + Call string `json:"call"` + + // Correlation ID + CorrelationID string `json:"correlation_id"` + + // 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"` + + // TTL + TTL string `json:"_ttl"` + + // Resgroup ID + ResgroupID uint64 `json:"resgroup_id"` + + // Account ID + AccountID uint64 `json:"account_id"` + + // Compute ID + ComputeID uint64 `json:"compute_id"` +} + +// 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"` + + // Physical Node or not + PhysicalNode bool `json:"physicalNode"` + + // 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..228d678 --- /dev/null +++ b/pkg/cloudbroker/backup.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..17b8091 --- /dev/null +++ b/pkg/cloudbroker/backup/backup.go @@ -0,0 +1,17 @@ +package backup + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..da63829 --- /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/v15/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..ac5c6b6 --- /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/v15/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..ee67b02 --- /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/v15/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..cfffd60 --- /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/v15/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..921b612 --- /dev/null +++ b/pkg/cloudbroker/backup/restore_disk_from_backup.go @@ -0,0 +1,87 @@ +package backup + +import ( + "context" + "encoding/json" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// RestoreDiskFromBackupRequest struct for restoring disk from backup +type RestoreDiskFromBackupRequest struct { + // Compute ID + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Node ID + NodeID uint64 `url:"nodeId,omitempty" json:"nodeId,omitempty"` + + // 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..80fb87d --- /dev/null +++ b/pkg/cloudbroker/backup/restore_disks_from_backup.go @@ -0,0 +1,92 @@ +package backup + +import ( + "context" + "encoding/json" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Node ID + NodeID uint64 `url:"nodeId,omitempty" json:"nodeId,omitempty"` + + //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/bservice.go b/pkg/cloudbroker/bservice.go new file mode 100644 index 0000000..591622f --- /dev/null +++ b/pkg/cloudbroker/bservice.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/bservice" + +// Accessing the BService method group +func (ca *CloudBroker) BService() *bservice.BService { + return bservice.New(ca.client) +} diff --git a/pkg/cloudbroker/bservice/bservice.go b/pkg/cloudbroker/bservice/bservice.go new file mode 100644 index 0000000..5080012 --- /dev/null +++ b/pkg/cloudbroker/bservice/bservice.go @@ -0,0 +1,15 @@ +package bservice + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/cloudbroker/bservice/create.go b/pkg/cloudbroker/bservice/create.go new file mode 100644 index 0000000..4450816 --- /dev/null +++ b/pkg/cloudbroker/bservice/create.go @@ -0,0 +1,54 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,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 := "/cloudbroker/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/cloudbroker/bservice/delete.go b/pkg/cloudbroker/bservice/delete.go new file mode 100644 index 0000000..93c5ef0 --- /dev/null +++ b/pkg/cloudbroker/bservice/delete.go @@ -0,0 +1,43 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 + // Default: 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 := "/cloudbroker/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/cloudbroker/bservice/disable.go b/pkg/cloudbroker/bservice/disable.go new file mode 100644 index 0000000..5dd4532 --- /dev/null +++ b/pkg/cloudbroker/bservice/disable.go @@ -0,0 +1,40 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/enable.go b/pkg/cloudbroker/bservice/enable.go new file mode 100644 index 0000000..ed546fc --- /dev/null +++ b/pkg/cloudbroker/bservice/enable.go @@ -0,0 +1,41 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/filter.go b/pkg/cloudbroker/bservice/filter.go new file mode 100644 index 0000000..f503212 --- /dev/null +++ b/pkg/cloudbroker/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/cloudbroker/bservice/filter_test.go b/pkg/cloudbroker/bservice/filter_test.go new file mode 100644 index 0000000..70e2949 --- /dev/null +++ b/pkg/cloudbroker/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/cloudbroker/bservice/get.go b/pkg/cloudbroker/bservice/get.go new file mode 100644 index 0000000..ae0d2d7 --- /dev/null +++ b/pkg/cloudbroker/bservice/get.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/bservice/get" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/bservice/group_add.go b/pkg/cloudbroker/bservice/group_add.go new file mode 100644 index 0000000..654f314 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_add.go @@ -0,0 +1,111 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // 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"` + + // Chipset "i440fx" or "Q35 + // Default value : Q35 + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"chipset"` + + // ID of the chosen storage policy + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,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 := "/cloudbroker/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/cloudbroker/bservice/group_compute_remove.go b/pkg/cloudbroker/bservice/group_compute_remove.go new file mode 100644 index 0000000..240678d --- /dev/null +++ b/pkg/cloudbroker/bservice/group_compute_remove.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_get.go b/pkg/cloudbroker/bservice/group_get.go new file mode 100644 index 0000000..154cd2b --- /dev/null +++ b/pkg/cloudbroker/bservice/group_get.go @@ -0,0 +1,44 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_parent_remove.go b/pkg/cloudbroker/bservice/group_parent_remove.go new file mode 100644 index 0000000..3ac0f39 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_parent_remove.go @@ -0,0 +1,48 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_parrent_add.go b/pkg/cloudbroker/bservice/group_parrent_add.go new file mode 100644 index 0000000..57b95f7 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_parrent_add.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_remove.go b/pkg/cloudbroker/bservice/group_remove.go new file mode 100644 index 0000000..aa58045 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_remove.go @@ -0,0 +1,43 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_resize.go b/pkg/cloudbroker/bservice/group_resize.go new file mode 100644 index 0000000..db260b6 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_resize.go @@ -0,0 +1,59 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Chipset for new computes, either i440fx or Q35 (i440fx by default) + // Available values : i440fx, Q35 + // Default value : Q35 + // Required: false + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Either delta or absolute value of computes + // Should be one of: + // - ABSOLUTE + // - RELATIVE + // Required: false + Mode string `url:"mode,omitempty" json:"mode,omitempty" validate:"omitempty,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 := "/cloudbroker/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/cloudbroker/bservice/group_start.go b/pkg/cloudbroker/bservice/group_start.go new file mode 100644 index 0000000..6916b05 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_start.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_stop.go b/pkg/cloudbroker/bservice/group_stop.go new file mode 100644 index 0000000..b90eaa7 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_stop.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_update.go b/pkg/cloudbroker/bservice/group_update.go new file mode 100644 index 0000000..47c4a21 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_update.go @@ -0,0 +1,76 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_update_extnet.go b/pkg/cloudbroker/bservice/group_update_extnet.go new file mode 100644 index 0000000..35cd2e5 --- /dev/null +++ b/pkg/cloudbroker/bservice/group_update_extnet.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/group_update_vins.go b/pkg/cloudbroker/bservice/group_update_vins.go new file mode 100644 index 0000000..d860e6b --- /dev/null +++ b/pkg/cloudbroker/bservice/group_update_vins.go @@ -0,0 +1,46 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/list.go b/pkg/cloudbroker/bservice/list.go new file mode 100644 index 0000000..834839b --- /dev/null +++ b/pkg/cloudbroker/bservice/list.go @@ -0,0 +1,92 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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 := "/cloudbroker/bservice/list" + + res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/bservice/list_deleted.go b/pkg/cloudbroker/bservice/list_deleted.go new file mode 100644 index 0000000..677be6e --- /dev/null +++ b/pkg/cloudbroker/bservice/list_deleted.go @@ -0,0 +1,56 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/migrate_to_zone.go b/pkg/cloudbroker/bservice/migrate_to_zone.go new file mode 100644 index 0000000..56259cf --- /dev/null +++ b/pkg/cloudbroker/bservice/migrate_to_zone.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move basic service to another zone +type MigrateToZoneRequest struct { + // ID of the BasicService to move + // Required: true + ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zoneId" json:"zoneId" validate:"required"` +} + +// MigrateToZone moves Basic Service instance to new zone +func (b BService) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/bservice/migrateToZone" + + 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/cloudbroker/bservice/models.go b/pkg/cloudbroker/bservice/models.go new file mode 100644 index 0000000..49903b3 --- /dev/null +++ b/pkg/cloudbroker/bservice/models.go @@ -0,0 +1,407 @@ +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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// List of Groups +type ListGroups []ItemGroup + +// 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 Computes +type ListComputes []ItemCompute + +// 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"` + + // NodeID + NodeID uint64 `json:"node_id"` + + // Status + Status string `json:"status"` + + // Tech status + TechStatus string `json:"techStatus"` +} + +// List of Snapshot +type ListSnapshots []ItemSnapshot + +// 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 BasicServices +type ListBasicServices struct { + Data []ItemBasicService `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} + +// 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"` + + // Milestones + Milestones uint64 `json:"milestones"` + + // 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"` + + // List of snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // SSH key for connection + SSHKey string `json:"sshKey"` + + // 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` +} + +// List of Group Computes +type ListGroupComputes []ItemGroupCompute + +// 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"` + + //Chipset + Chipset string `json:"chipset"` +} + +// List of information about OS Users +type ListOSUsers []ItemOSUser + +// Main information about OS User +type ItemOSUser struct { + // Login + Login string `json:"login"` + + // Password + Password string `json:"password"` +} diff --git a/pkg/cloudbroker/bservice/restore.go b/pkg/cloudbroker/bservice/restore.go new file mode 100644 index 0000000..422772a --- /dev/null +++ b/pkg/cloudbroker/bservice/restore.go @@ -0,0 +1,38 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/snapshot_create.go b/pkg/cloudbroker/bservice/snapshot_create.go new file mode 100644 index 0000000..163d27b --- /dev/null +++ b/pkg/cloudbroker/bservice/snapshot_create.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/snapshot_delete.go b/pkg/cloudbroker/bservice/snapshot_delete.go new file mode 100644 index 0000000..9c561f2 --- /dev/null +++ b/pkg/cloudbroker/bservice/snapshot_delete.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/snapshot_list.go b/pkg/cloudbroker/bservice/snapshot_list.go new file mode 100644 index 0000000..20d12d7 --- /dev/null +++ b/pkg/cloudbroker/bservice/snapshot_list.go @@ -0,0 +1,40 @@ +package bservice + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/snapshot_rollback.go b/pkg/cloudbroker/bservice/snapshot_rollback.go new file mode 100644 index 0000000..176e289 --- /dev/null +++ b/pkg/cloudbroker/bservice/snapshot_rollback.go @@ -0,0 +1,42 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/sorting.go b/pkg/cloudbroker/bservice/sorting.go new file mode 100644 index 0000000..3fdaed1 --- /dev/null +++ b/pkg/cloudbroker/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/cloudbroker/bservice/start.go b/pkg/cloudbroker/bservice/start.go new file mode 100644 index 0000000..454d5e4 --- /dev/null +++ b/pkg/cloudbroker/bservice/start.go @@ -0,0 +1,40 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/bservice/stop.go b/pkg/cloudbroker/bservice/stop.go new file mode 100644 index 0000000..8fa4dad --- /dev/null +++ b/pkg/cloudbroker/bservice/stop.go @@ -0,0 +1,40 @@ +package bservice + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/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/cloudbroker/cloudbroker.go b/pkg/cloudbroker/cloudbroker.go new file mode 100644 index 0000000..3e3fd81 --- /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/v15/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..76b4617 --- /dev/null +++ b/pkg/cloudbroker/compute.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/abort_shared_snapshot_merge.go b/pkg/cloudbroker/compute/abort_shared_snapshot_merge.go new file mode 100644 index 0000000..ef881a9 --- /dev/null +++ b/pkg/cloudbroker/compute/abort_shared_snapshot_merge.go @@ -0,0 +1,68 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AbortSharedSnapshotMergeRequest struct to abort shared snapshots merge +type AbortSharedSnapshotMergeRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Label of the snapshot + // Required: true + Label string `url:"label" json:"label" validate:"required"` +} + +type wrapperAbortSharedSnapshotMergeRequest struct { + AbortSharedSnapshotMergeRequest + AsyncMode bool `url:"asyncMode"` +} + +// AbortSharedSnapshotMerge shared snapshots merge abort +func (c Compute) AbortSharedSnapshotMerge(ctx context.Context, req AbortSharedSnapshotMergeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperAbortSharedSnapshotMergeRequest{ + AbortSharedSnapshotMergeRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/abort_shared_snapshot_merge" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} + +// AbortSharedSnapshotMergeAsync shared snapshots merge abort in async mode +func (c Compute) AbortSharedSnapshotMergeAsync(ctx context.Context, req AbortSharedSnapshotMergeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperAbortSharedSnapshotMergeRequest{ + AbortSharedSnapshotMergeRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/abort_shared_snapshot_merge" + + 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/affinity_group_check_start.go b/pkg/cloudbroker/compute/affinity_group_check_start.go new file mode 100644 index 0000000..b5de728 --- /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/v15/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..517db4f --- /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/v15/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..d7297d0 --- /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/v15/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..1355cc0 --- /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/v15/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..d71d7bb --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_rule_add.go @@ -0,0 +1,67 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: true + Value string `url:"value" json:"value" validate:"required"` +} + +// 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..5cca6b7 --- /dev/null +++ b/pkg/cloudbroker/compute/affinity_rule_remove.go @@ -0,0 +1,65 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: true + Value string `url:"value" json:"value" validate:"required"` +} + +// 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..036fdfa --- /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/v15/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..4255a3e --- /dev/null +++ b/pkg/cloudbroker/compute/anti_affinity_rule_add.go @@ -0,0 +1,65 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: true + Value string `url:"value" json:"value" validate:"required"` +} + +// 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..89c2ccb --- /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/v15/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..7d659a8 --- /dev/null +++ b/pkg/cloudbroker/compute/anti_affinity_rule_remove.go @@ -0,0 +1,65 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: true + Value string `url:"value" json:"value" validate:"required"` +} + +// 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..9e2a2e7 --- /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/v15/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..76635fd --- /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/v15/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..a3e8ed1 --- /dev/null +++ b/pkg/cloudbroker/compute/audits.go @@ -0,0 +1,76 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AuditsRequest struct to get audit records +type AuditsRequest struct { + // ID of the compute + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Find all audits after point in time + // Required: false + TimestampAT uint64 `url:"timestamp_at,omitempty" json:"timestamp_at,omitempty"` + + // Find all audits before point in time + // Required: false + TimestampTO uint64 `url:"timestamp_to,omitempty" json:"timestamp_to,omitempty"` + + // Find by user + // Required: false + User string `url:"user,omitempty" json:"user,omitempty"` + + // Find by api endpoints + // Required: false + Call string `url:"call,omitempty" json:"call,omitempty"` + + // Sort by one of supported fields, format ± + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,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"` + + // Find by HTTP min status code + // Required: false + MinStatusCode uint64 `url:"min_status_code,omitempty" json:"min_status_code,omitempty"` + + // Find by HTTP max status code + // Required: false + MaxStatusCode uint64 `url:"max_status_code,omitempty" json:"max_status_code,omitempty"` +} + +// 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.MethodGet, 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..47d1f4f --- /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/v15/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..ee9c1dd --- /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/v15/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..3c49bf0 --- /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/v15/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..6454b20 --- /dev/null +++ b/pkg/cloudbroker/compute/cd_eject.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperCDEjectRequest struct { + CDEjectRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperCDEjectRequest{ + CDEjectRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/cdEject" + + res, err := c.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, err +} + +// CDEjectAsync ejects CD image to compute's CD-ROM with AsyncMode +func (c Compute) CDEjectAsync(ctx context.Context, req CDEjectRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCDEjectRequest{ + CDEjectRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/cdEject" + + 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/cd_insert.go b/pkg/cloudbroker/compute/cd_insert.go new file mode 100644 index 0000000..0dfa948 --- /dev/null +++ b/pkg/cloudbroker/compute/cd_insert.go @@ -0,0 +1,69 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperCDInsertRequest struct { + CDInsertRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperCDInsertRequest{ + CDInsertRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/cdInsert" + + _, err = c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return false, err + } + + return true, nil +} + +// CDInsertAsync inserts new CD image to compute's CD-ROM with AsyncMode +func (c Compute) CDInsertAsync(ctx context.Context, req CDInsertRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCDInsertRequest{ + CDInsertRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/cdInsert" + + 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/change_ip.go b/pkg/cloudbroker/compute/change_ip.go new file mode 100644 index 0000000..ca687fe --- /dev/null +++ b/pkg/cloudbroker/compute/change_ip.go @@ -0,0 +1,87 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeIPRequest struct to change IP for network +type ChangeIPRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Network type + // 'EXTNET' for connect to external network directly + // 'VINS' for connect to ViNS + // Required: true + NetType string `url:"net_type" json:"net_type" validate:"computeNetType"` + + // Network ID for connect to + // For EXTNET - external network ID + // For VINS - VINS ID + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // IP address to which we will change the existing one, it must be from the same subnet + // Required: true + IPAddr string `url:"ip_addr" json:"ip_addr" validate:"required"` +} + +type wrapperChangeIPRequest struct { + ChangeIPRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperChangeIPRequest{ + ChangeIPRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/changeIp" + + res, err := c.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 +} + +// ChangeIPAsync change reserved IP for compute instance with AsyncMode +func (c Compute) ChangeIPAsync(ctx context.Context, req ChangeIPRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeIPRequest{ + ChangeIPRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/changeIp" + + 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/change_link_state.go b/pkg/cloudbroker/compute/change_link_state.go new file mode 100644 index 0000000..43a1410 --- /dev/null +++ b/pkg/cloudbroker/compute/change_link_state.go @@ -0,0 +1,80 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperChangeLinkStateRequest struct { + ChangeLinkStateRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperChangeLinkStateRequest{ + ChangeLinkStateRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/changeLinkState" + + res, err := c.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 +} + +// ChangeLinkStateAsync changes the status link virtual of compute with AsyncMode +func (c Compute) ChangeLinkStateAsync(ctx context.Context, req ChangeLinkStateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeLinkStateRequest{ + ChangeLinkStateRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/changeLinkState" + + 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/change_mac.go b/pkg/cloudbroker/compute/change_mac.go new file mode 100644 index 0000000..2ce71bc --- /dev/null +++ b/pkg/cloudbroker/compute/change_mac.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeMACRequest struct to change MAC for network +type ChangeMACRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Current mac address + // Required: true + СurrentMAC string `url:"current_mac_address" json:"current_mac_address" validate:"required"` + + // the MAC address to which we will change the existing one + // Required: true + NewMAC string `url:"new_mac_address" json:"new_mac_address" validate:"required"` +} + +// ChangeMAC change MAC for compute instance +func (c Compute) ChangeMAC(ctx context.Context, req ChangeMACRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/changeMac" + + 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_mtu.go b/pkg/cloudbroker/compute/change_mtu.go new file mode 100644 index 0000000..07fc767 --- /dev/null +++ b/pkg/cloudbroker/compute/change_mtu.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeMTURequest struct to change MTU for a compute +type ChangeMTURequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Interface name or MAC address + // Required: true + Interface string `url:"interface" json:"interface" validate:"required"` + + // Maximum transmission unit + // Required: true + MTU uint64 `url:"mtu" json:"mtu" validate:"required" validate:"omitempty,mtu"` +} + +type wrapperChangeMTURequest struct { + ChangeMTURequest + + AsyncMode bool `url:"asyncMode"` +} + +// ChangeMTU change MTU for compute instance +func (c Compute) ChangeMTU(ctx context.Context, req ChangeMTURequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeMTURequest{ + ChangeMTURequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/change_mtu" + + res, err := c.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 +} + +// ChangeMTUAsync change MTU for compute instance with AsyncMode +func (c Compute) ChangeMTUAsync(ctx context.Context, req ChangeMTURequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeMTURequest{ + ChangeMTURequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/change_mtu" + + 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/change_read_only.go b/pkg/cloudbroker/compute/change_read_only.go new file mode 100644 index 0000000..262cdae --- /dev/null +++ b/pkg/cloudbroker/compute/change_read_only.go @@ -0,0 +1,41 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeReadOnlyRequest defines parameters for toggling read-only mode. +type ChangeReadOnlyRequest struct { + // Compute ID + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // ReadOnly indicates whether the read-only mode is enabled + // Required: true + ReadOnly bool `url:"read_only" json:"read_only" validate:"required"` +} + +// ChangeReadOnly toggles compute read-only mode. +func (c Compute) ChangeReadOnly(ctx context.Context, req ChangeReadOnlyRequest) (bool, error) { + if err := validators.ValidateRequest(req); err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/change_read_only" + + 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_secutity_group.go b/pkg/cloudbroker/compute/change_secutity_group.go new file mode 100644 index 0000000..8fed54e --- /dev/null +++ b/pkg/cloudbroker/compute/change_secutity_group.go @@ -0,0 +1,83 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeSecGroupsRequest struct to change security groups for compute +type ChangeSecGroupsRequest struct { + // Identifier compute + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Interface name or MAC address + // Required: true + Interface string `url:"interface" json:"interface" validate:"required"` + + // List of security group IDs to assign to this interface + // Required: false + SecGroups []uint64 `url:"security_groups,omitempty" json:"security_groups,omitempty"` + + // Flag indicating whether security groups are enabled for this interface + // Required: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +type wrapperChangeSecGroupsRequest struct { + ChangeSecGroupsRequest + + AsyncMode bool `url:"asyncMode"` +} + +// ChangeSecGroups changes security groups for compute +func (c Compute) ChangeSecGroups(ctx context.Context, req ChangeSecGroupsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeSecGroupsRequest{ + ChangeSecGroupsRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/change_security_groups" + + res, err := c.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 +} + +// ChangeSecGroupsAsync changes security groups for compute with AsyncMode +func (c Compute) ChangeSecGroupsAsync(ctx context.Context, req ChangeSecGroupsRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperChangeSecGroupsRequest{ + ChangeSecGroupsRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/change_security_groups" + + 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/check_compute_placement.go b/pkg/cloudbroker/compute/check_compute_placement.go new file mode 100644 index 0000000..98aecdb --- /dev/null +++ b/pkg/cloudbroker/compute/check_compute_placement.go @@ -0,0 +1,47 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CheckComputePlacementRequest struct for check compute placement +type CheckComputePlacementRequest struct { + // IDs of compute instances to check + // Required: true + ComputeIDs []uint64 `url:"compute_ids" json:"compute_ids" validate:"required"` + + // Filter by CPU and RAM when checking placement + // Required: false + // Default: true + FilterByCPURAM interface{} `url:"filter_by_cpu_ram,omitempty" json:"filter_by_cpu_ram,omitempty" validate:"omitempty,isBool"` +} + +// CheckComputePlacement checks compute placement and returns structured result +func (c Compute) CheckComputePlacement(ctx context.Context, req CheckComputePlacementRequest) (CheckComputePlacementResult, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/check_compute_placement" + + if req.FilterByCPURAM == nil { + req.FilterByCPURAM = true + } + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + result := make(CheckComputePlacementResult) + if err := json.Unmarshal(res, &result); err != nil { + return nil, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/clone.go b/pkg/cloudbroker/compute/clone.go new file mode 100644 index 0000000..eeb47ea --- /dev/null +++ b/pkg/cloudbroker/compute/clone.go @@ -0,0 +1,95 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // ID of the Storage Policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // The name of the pool to migrate disks to + // Required: false + PoolName string `url:"pool_name" json:"pool_name"` + + // The ID of the SEP to migrate disks to + // Required: false + SEPID uint64 `url:"sep_id" json:"sep_id"` +} + +type wrapperCloneRequest struct { + CloneRequest + + AsyncMode bool `url:"asyncMode" json:"asyncMode"` +} + +// Clone clones compute instance +func (c Compute) Clone(ctx context.Context, req CloneRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCloneRequest{ + CloneRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/clone" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} + +// CloneAsync clones compute instance with AsyncMode +func (c Compute) CloneAsync(ctx context.Context, req CloneRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperCloneRequest{ + CloneRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/clone" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, reqWrapped) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/clone_abort.go b/pkg/cloudbroker/compute/clone_abort.go new file mode 100644 index 0000000..de7ef29 --- /dev/null +++ b/pkg/cloudbroker/compute/clone_abort.go @@ -0,0 +1,41 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CloneAbortRequest struct to abort a compute clone +type CloneAbortRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// CloneAbort aborts a compute clone +func (c Compute) CloneAbort(ctx context.Context, req CloneAbortRequest) ([]RecordCloneAbort, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/clone_abort" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + result := make([]RecordCloneAbort, 0) + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/compute/clone_status.go b/pkg/cloudbroker/compute/clone_status.go new file mode 100644 index 0000000..6e13912 --- /dev/null +++ b/pkg/cloudbroker/compute/clone_status.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetCloneStatusRequest struct to get information about compute clone status +type GetCloneStatusRequest struct { + // ID of compute instance + // Required: true + ComputeID string `url:"compute_id" json:"compute_id" validate:"required"` +} + +// GetCloneStatus gets information about compute clone status as a RecordCloneStatus struct +func (c Compute) GetCloneStatus(ctx context.Context, req GetCloneStatusRequest) ([]RecordCloneStatus, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/clone_status" + + res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return nil, err + } + + cloneStatus := make([]RecordCloneStatus, 0) + + err = json.Unmarshal(res, &cloneStatus) + if err != nil { + return nil, err + } + + return cloneStatus, nil +} diff --git a/pkg/cloudbroker/compute/compute.go b/pkg/cloudbroker/compute/compute.go new file mode 100644 index 0000000..4e5a211 --- /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/v15/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..7465922 --- /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/v15/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..319f77a --- /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/v15/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..9749181 --- /dev/null +++ b/pkg/cloudbroker/compute/create_template.go @@ -0,0 +1,72 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", 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 "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} + +// CreateTemplateAsync create template from compute instance with AsyncMode +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 + } + + return string(res), 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..1c40cbe --- /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/v15/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"` + + // Storage policy id of compute. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // 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"` + + // 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..01afb29 --- /dev/null +++ b/pkg/cloudbroker/compute/delete.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDeleteRequest struct { + DeleteRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDeleteRequest{ + DeleteRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/delete" + + res, err := c.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 +} + +// DeleteAsync deletes compute with AsyncMode +func (c Compute) DeleteAsync(ctx context.Context, req DeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDeleteRequest{ + DeleteRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/delete" + + 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/delete_cpu_alignment_profile.go b/pkg/cloudbroker/compute/delete_cpu_alignment_profile.go new file mode 100644 index 0000000..83c94b7 --- /dev/null +++ b/pkg/cloudbroker/compute/delete_cpu_alignment_profile.go @@ -0,0 +1,39 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteCPUAlignmentProfileRequest struct to delete CPU alignment profile for computes +type DeleteCPUAlignmentProfileRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []uint64 `url:"compute_ids" json:"compute_ids" validate:"min=1"` +} + +// DeleteCPUAlignmentProfile deletes CPU alignment profile for computes +func (c Compute) DeleteCPUAlignmentProfile(ctx context.Context, req DeleteCPUAlignmentProfileRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/delete_cpu_alignment_profile" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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..2dae444 --- /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/v15/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..4a3ef44 --- /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/v15/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..b3aa9ad --- /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/v15/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..afd62fa --- /dev/null +++ b/pkg/cloudbroker/compute/disable.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DisableRequest struct to disable compute +type DisableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperDisableRequest struct { + DisableRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDisableRequest{ + DisableRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/disable" + + res, err := c.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 +} + +// DisableAsync disables compute with AsyncMode +func (c Compute) DisableAsync(ctx context.Context, req DisableRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDisableRequest{ + DisableRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/disable" + + 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/disk_add.go b/pkg/cloudbroker/compute/disk_add.go new file mode 100644 index 0000000..58c5481 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_add.go @@ -0,0 +1,121 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Storage policy id of compute. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // 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"` + + // Desired PCI slot (hex string, e.g. "0x1A") + // Required: false + PCISlot string `url:"pci_slot,omitempty" json:"pci_slot,omitempty"` + + // Desired bus number (hex string, e.g. "0x03") + // Required: false + BusNumber string `url:"bus_number,omitempty" json:"bus_number,omitempty"` + + // Disk cache mode + // Required: false + Cache string `url:"cache,omitempty" json:"cache,omitempty"` + + // Discard + // Required: false + Discard string `url:"discard,omitempty" json:"discard,omitempty"` + + // Mount disk in read-only mode + // Required: false + ReadOnly bool `url:"read_only,omitempty" json:"read_only,omitempty"` +} + +type wrapperDiskAddRequest struct { + DiskAddRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskAddRequest{ + DiskAddRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/diskAdd" + + 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 +} + +// DiskAddAsync creates new disk and attach to compute with AsyncMode +func (c Compute) DiskAddAsync(ctx context.Context, req DiskAddRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskAddRequest{ + DiskAddRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/diskAdd" + + 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/disk_attach.go b/pkg/cloudbroker/compute/disk_attach.go new file mode 100644 index 0000000..f32dac7 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_attach.go @@ -0,0 +1,87 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Desired PCI slot (hex string, e.g. "0x1A") + // Required: false + PCISlot string `url:"pci_slot,omitempty" json:"pci_slot,omitempty"` + + // Desired bus number (hex string, e.g. "0x03") + // Required: false + BusNumber string `url:"bus_number,omitempty" json:"bus_number,omitempty"` + + // Mount disk in read-only mode + // Required: false + ReadOnly bool `url:"read_only,omitempty" json:"read_only,omitempty"` +} + +type wrapperDiskAttachRequest struct { + DiskAttachRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskAttachRequest{ + DiskAttachRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/diskAttach" + + res, err := c.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 +} + +// DiskAttachAsync attach disk to compute with AsyncMode +func (c Compute) DiskAttachAsync(ctx context.Context, req DiskAttachRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskAttachRequest{ + DiskAttachRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/diskAttach" + + 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/disk_del.go b/pkg/cloudbroker/compute/disk_del.go new file mode 100644 index 0000000..b4717cb --- /dev/null +++ b/pkg/cloudbroker/compute/disk_del.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskDelRequest struct { + DiskDelRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskDelRequest{ + DiskDelRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/diskDel" + + res, err := c.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 +} + +// DiskDelAsync deletes disk and detaches it from compute with AsyncMode +func (c Compute) DiskDelAsync(ctx context.Context, req DiskDelRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskDelRequest{ + DiskDelRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/diskDel" + + 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/disk_detach.go b/pkg/cloudbroker/compute/disk_detach.go new file mode 100644 index 0000000..238354e --- /dev/null +++ b/pkg/cloudbroker/compute/disk_detach.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskDetachRequest struct { + DiskDetachRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskDetachRequest{ + DiskDetachRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/diskDetach" + + res, err := c.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 +} + +// DiskDetachAsync detaches disk from compute with AsyncMode +func (c Compute) DiskDetachAsync(ctx context.Context, req DiskDetachRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskDetachRequest{ + DiskDetachRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/diskDetach" + + 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/disk_migrate.go b/pkg/cloudbroker/compute/disk_migrate.go new file mode 100644 index 0000000..5700bce --- /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/v15/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..346688e --- /dev/null +++ b/pkg/cloudbroker/compute/disk_qos.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskQOSRequest struct { + DiskQOSRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskQOSRequest{ + DiskQOSRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/diskQos" + + res, err := c.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 +} + +// DiskQOSAsync changes QOS of the disk with AsyncMode +func (c Compute) DiskQOSAsync(ctx context.Context, req DiskQOSRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskQOSRequest{ + DiskQOSRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/diskQos" + + 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/disk_resize.go b/pkg/cloudbroker/compute/disk_resize.go new file mode 100644 index 0000000..6315d29 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_resize.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskResizeRequest struct { + DiskResizeRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskResizeRequest{ + DiskResizeRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/diskResize" + + res, err := c.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 +} + +// DiskResizeAsync changes disk size with AsyncMode +func (c Compute) DiskResizeAsync(ctx context.Context, req DiskResizeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskResizeRequest{ + DiskResizeRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/diskResize" + + 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/disk_switch_to_replication.go b/pkg/cloudbroker/compute/disk_switch_to_replication.go new file mode 100644 index 0000000..5c9a794 --- /dev/null +++ b/pkg/cloudbroker/compute/disk_switch_to_replication.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperDiskSwitchToReplicationRequest struct { + DiskSwitchToReplicationRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperDiskSwitchToReplicationRequest{ + DiskSwitchToReplicationRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/diskSwitchToReplication" + + res, err := c.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 +} + +// DiskSwitchToReplicationAsync switches disk to it's replication with AsyncMode +func (c Compute) DiskSwitchToReplicationAsync(ctx context.Context, req DiskSwitchToReplicationRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperDiskSwitchToReplicationRequest{ + DiskSwitchToReplicationRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/diskSwitchToReplication" + + 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/enable.go b/pkg/cloudbroker/compute/enable.go new file mode 100644 index 0000000..b581308 --- /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/v15/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..440dc08 --- /dev/null +++ b/pkg/cloudbroker/compute/filter.go @@ -0,0 +1,331 @@ +package compute + +import ( + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/k8s" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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] +} + +// FilterByID returns ListDeletedComputes with specified ID. +func (lc ListDeletedComputes) FilterByID(id uint64) ListDeletedComputes { + predicate := func(ic ItemDeletedCompute) bool { + return ic.ID == id + } + + return lc.FilterFunc(predicate) +} + +// FilterByName returns ListDeletedComputes with specified Name. +func (lc ListDeletedComputes) FilterByName(name string) ListDeletedComputes { + predicate := func(ic ItemDeletedCompute) bool { + return ic.Name == name + } + + return lc.FilterFunc(predicate) +} + +// FilterByStatus returns ListDeletedComputes with specified Status. +func (lc ListDeletedComputes) FilterByStatus(status string) ListDeletedComputes { + predicate := func(ic ItemDeletedCompute) bool { + return ic.Status == status + } + + return lc.FilterFunc(predicate) +} + +// FilterByTechStatus returns ListDeletedComputes with specified TechStatus. +func (lc ListDeletedComputes) FilterByTechStatus(techStatus string) ListDeletedComputes { + predicate := func(ic ItemDeletedCompute) bool { + return ic.TechStatus == techStatus + } + + return lc.FilterFunc(predicate) +} + +// FilterByDiskID return ListDeletedComputes with specified DiskID. +func (lc ListDeletedComputes) FilterByDiskID(diskID uint64) ListDeletedComputes { + predicate := func(ic ItemDeletedCompute) bool { + for _, disk := range ic.Disks { + if disk.ID == diskID { + return true + } + } + return false + } + + return lc.FilterFunc(predicate) +} + +// FilterByK8SID returns master and worker nodes (ListDeletedComputes) inside specified K8S cluster. +func (lc ListDeletedComputes) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListDeletedComputes, 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 ItemDeletedCompute) 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 ListDeletedComputes) FilterByK8SMasters() ListDeletedComputes { + predicate := func(ic ItemDeletedCompute) 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 ListDeletedComputes) FilterByK8SWorkers() ListDeletedComputes { + predicate := func(ic ItemDeletedCompute) bool { + for _, rule := range ic.AntiAffinityRules { + if rule.Value == "worker" { + return true + } + } + return false + } + + return lc.FilterFunc(predicate) +} + +// FilterByLBID is used to filter ListDeletedComputes used by specified Load Balancer. +func (lc ListDeletedComputes) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (*ListDeletedComputes, 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 ItemDeletedCompute) bool { + return ic.ID == foundLB.PrimaryNode.ComputeID || ic.ID == foundLB.SecondaryNode.ComputeID + } + + result := lc.FilterFunc(predicate) + + return &result, nil +} + +// FilterFunc allows filtering ListDeletedComputes based on a user-specified predicate. +func (lc ListDeletedComputes) FilterFunc(predicate func(ItemDeletedCompute) bool) ListDeletedComputes { + var result ListDeletedComputes + + 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 ItemDeletedCompute +// If none was found, returns an empty struct. +func (lc ListDeletedComputes) FindOne() ItemDeletedCompute { + if len(lc.Data) == 0 { + return ItemDeletedCompute{} + } + + 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..7116ef6 --- /dev/null +++ b/pkg/cloudbroker/compute/filter_test.go @@ -0,0 +1,481 @@ +package compute + +import ( + "testing" +) + +var computes = ListComputes{ + Data: []ItemCompute{ + { + Disks: []InfoDisk{ + { + ID: 65191, + }, + }, + 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, + Interfaces: ListInterfaces{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363500, + Name: "test", + PinnedToNode: 1, + 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, + }, + }, + { + Disks: []InfoDisk{ + { + ID: 65248, + }, + }, + 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, + Interfaces: ListInterfaces{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363853, + Name: "compute_2", + PinnedToNode: 1, + 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, + }, + }, + }, + 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) + } +} + +var deleteComputes = ListDeletedComputes{ + Data: []ItemDeletedCompute{ + { + Disks: []InfoDisk{ + { + ID: 65191, + }, + }, + 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, + Interfaces: ListInterfaces{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363500, + Name: "test", + 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, + }, + }, + { + Disks: []InfoDisk{ + { + ID: 65248, + }, + }, + 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, + Interfaces: ListInterfaces{}, + LockStatus: "UNLOCKED", + ManagerID: 0, + ManagerType: "", + MigrationJob: 0, + Milestones: 363853, + Name: "compute_2", + 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, + }, + }, + }, + EntryCount: 2, +} + +func TestFilterDeleteByID(t *testing.T) { + actual := deleteComputes.FilterByID(48500).FindOne() + + if actual.ID != 48500 { + t.Fatal("expected ID 48500, found: ", actual.ID) + } + + actualEmpty := deleteComputes.FilterByID(0) + + if len(actualEmpty.Data) != 0 { + t.Fatal("expected empty, actual: ", len(actualEmpty.Data)) + } +} + +func TestFilterDeleteByName(t *testing.T) { + actual := deleteComputes.FilterByName("compute_2").FindOne() + + if actual.Name != "compute_2" { + t.Fatal("expected compute with name 'test', found: ", actual.Name) + } +} + +func TestFilterDeleteByStatus(t *testing.T) { + actual := deleteComputes.FilterByStatus("ENABLED") + + for _, item := range actual.Data { + if item.Status != "ENABLED" { + t.Fatal("expected ENABLED status, found: ", item.Status) + } + } +} + +func TestFilterDeleteByTechStatus(t *testing.T) { + actual := deleteComputes.FilterByTechStatus("STARTED").FindOne() + + if actual.ID != 48556 { + t.Fatal("expected 48556 with STARTED techStatus, found: ", actual.ID) + } +} + +func TestFilterDeleteByDiskID(t *testing.T) { + actual := deleteComputes.FilterByDiskID(65248).FindOne() + + if actual.ID != 48556 { + t.Fatal("expected 48556 with DiskID 65248, found: ", actual.ID) + } +} + +func TestFilterDeleteFunc(t *testing.T) { + actual := deleteComputes.FilterFunc(func(ic ItemDeletedCompute) 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 TestDeleteSortingByCreatedTime(t *testing.T) { + actual := deleteComputes.SortByCreatedTime(false) + + if actual.Data[0].Name != "test" { + t.Fatal("expected 'test', found: ", actual.Data[0].Name) + } + + actual = deleteComputes.SortByCreatedTime(true) + if actual.Data[0].Name != "compute_2" { + t.Fatal("expected 'compute_2', found: ", actual.Data[0].Name) + } +} + +func TestDeleteSortingByCPU(t *testing.T) { + actual := deleteComputes.SortByCPU(false) + + if actual.Data[0].CPUs != 4 { + t.Fatal("expected 4 CPU cores, found: ", actual.Data[0].CPUs) + } + + actual = deleteComputes.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..c003a53 --- /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/v15/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..dd8024e --- /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/v15/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..1db27e2 --- /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/v15/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_cpu_alignment_profile.go b/pkg/cloudbroker/compute/get_cpu_alignment_profile.go new file mode 100644 index 0000000..538b327 --- /dev/null +++ b/pkg/cloudbroker/compute/get_cpu_alignment_profile.go @@ -0,0 +1,46 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetCPUAlignmentProfileRequest struct to get CPU alignment profile for compute +type GetCPUAlignmentProfileRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// GetCPUAlignmentProfile gets CPU alignment profile for compute +func (c Compute) GetCPUAlignmentProfile(ctx context.Context, req GetCPUAlignmentProfileRequest) (*CPUAlignmentProfile, error) { + res, err := c.GetCPUAlignmentProfileRaw(ctx, req) + if err != nil { + return nil, err + } + + info := CPUAlignmentProfile{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetCPUAlignmentProfileRaw gets CPU alignment profile for compute as an array of bytes +func (c Compute) GetCPUAlignmentProfileRaw(ctx context.Context, req GetCPUAlignmentProfileRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/get_cpu_alignment_profile" + + res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/compute/get_custom_fields.go b/pkg/cloudbroker/compute/get_custom_fields.go new file mode 100644 index 0000000..30574d7 --- /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/v15/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..d7a2093 --- /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/v15/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.MethodGet, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/guest_agent_disable.go b/pkg/cloudbroker/compute/guest_agent_disable.go new file mode 100644 index 0000000..3c04f7f --- /dev/null +++ b/pkg/cloudbroker/compute/guest_agent_disable.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentDisableRequest struct to disable guest agent +type GuestAgentDisableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +type wrapperGuestAgentDisableRequest struct { + GuestAgentDisableRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Disable guest agent at a specific compute +func (c Compute) GuestAgentDisable(ctx context.Context, req GuestAgentDisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentDisableRequest{ + GuestAgentDisableRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/guest_agent_disable" + + res, err := c.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 +} + +// GuestAgentDisableAsync disables guest agent at a specific compute with AsyncMode +func (c Compute) GuestAgentDisableAsync(ctx context.Context, req GuestAgentDisableRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentDisableRequest{ + GuestAgentDisableRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/guest_agent_disable" + + 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/guest_agent_enable.go b/pkg/cloudbroker/compute/guest_agent_enable.go new file mode 100644 index 0000000..7ed7e54 --- /dev/null +++ b/pkg/cloudbroker/compute/guest_agent_enable.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentEnableRequest struct to enable guest agent +type GuestAgentEnableRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +type wrapperGuestAgentEnableRequest struct { + GuestAgentEnableRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Enable guest agent at a specific compute +func (c Compute) GuestAgentEnable(ctx context.Context, req GuestAgentEnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentEnableRequest{ + GuestAgentEnableRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/guest_agent_enable" + + res, err := c.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 +} + +// GuestAgentEnableAsync enables guest agent at a specific compute with AsyncMode +func (c Compute) GuestAgentEnableAsync(ctx context.Context, req GuestAgentEnableRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentEnableRequest{ + GuestAgentEnableRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/guest_agent_enable" + + 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/guest_agent_execute.go b/pkg/cloudbroker/compute/guest_agent_execute.go new file mode 100644 index 0000000..8bf2806 --- /dev/null +++ b/pkg/cloudbroker/compute/guest_agent_execute.go @@ -0,0 +1,81 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentExecuteRequest struct to execute command from user to agent +type GuestAgentExecuteRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Custom command from user to agent + // Required: true + Command string `url:"command" json:"command" validate:"required"` + + // Arguments to command + // Required: true + Arguments string `url:"arguments" json:"arguments" validate:"required"` +} + +type wrapperGuestAgentExecuteRequest struct { + GuestAgentExecuteRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Execute guest agent command +func (c Compute) GuestAgentExecuteRequest(ctx context.Context, req GuestAgentExecuteRequest) (map[string]interface{}, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentExecuteRequest{ + GuestAgentExecuteRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/guest_agent_execute" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + var result map[string]interface{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// GuestAgentExecuteRequestAsync executes guest agent command with AsyncMode +func (c Compute) GuestAgentExecuteRequestAsync(ctx context.Context, req GuestAgentExecuteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentExecuteRequest{ + GuestAgentExecuteRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/guest_agent_execute" + + 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/guest_agent_feature_get.go b/pkg/cloudbroker/compute/guest_agent_feature_get.go new file mode 100644 index 0000000..9b89281 --- /dev/null +++ b/pkg/cloudbroker/compute/guest_agent_feature_get.go @@ -0,0 +1,40 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentFeatureGetRequest struct to feature get guest agent +type GuestAgentFeatureGetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// List of features +func (c Compute) GuestAgentFeatureGet(ctx context.Context, req GuestAgentFeatureGetRequest) ([]string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/guest_agent_feature_get" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + features := make([]string, 0) + + err = json.Unmarshal(res, &features) + if err != nil { + return nil, err + } + + return features, nil +} diff --git a/pkg/cloudbroker/compute/guest_agent_feature_update.go b/pkg/cloudbroker/compute/guest_agent_feature_update.go new file mode 100644 index 0000000..670bc43 --- /dev/null +++ b/pkg/cloudbroker/compute/guest_agent_feature_update.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GuestAgentFeatureUpdateRequest struct to feature update guest agent +type GuestAgentFeatureUpdateRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +type wrapperGuestAgentFeatureUpdateRequest struct { + GuestAgentFeatureUpdateRequest + + AsyncMode bool `url:"asyncMode"` +} + +// Feature update guest agent +func (c Compute) GuestAgentFeatureUpdate(ctx context.Context, req GuestAgentFeatureUpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentFeatureUpdateRequest{ + GuestAgentFeatureUpdateRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/guest_agent_feature_update" + + res, err := c.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 +} + +// GuestAgentFeatureUpdateAsync feature updates guest agent with AsyncMode +func (c Compute) GuestAgentFeatureUpdateAsync(ctx context.Context, req GuestAgentFeatureUpdateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperGuestAgentFeatureUpdateRequest{ + GuestAgentFeatureUpdateRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/guest_agent_feature_update" + + 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/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..b270cd3 --- /dev/null +++ b/pkg/cloudbroker/compute/list.go @@ -0,0 +1,117 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 node ID + // Required: false + NodeID uint64 `url:"node_id,omitempty" json:"node_id,omitempty"` + + // Find by CD image ID + // Required: false + CDImageID uint64 `url:"cdImageId,omitempty" json:"cdImageId,omitempty"` + + // Find by node name + // Required: false + NodeName string `url:"nodeName,omitempty" json:"nodeName,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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..638ac97 --- /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/v15/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) (*ListDeletedComputes, 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 := ListDeletedComputes{} + + 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..31a5f2b --- /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/v15/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..9095a7b --- /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/v15/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..3ea1ff4 --- /dev/null +++ b/pkg/cloudbroker/compute/mass_delete.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperMassDeleteRequest struct { + MassDeleteRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperMassDeleteRequest{ + MassDeleteRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/massDelete" + + res, err := c.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 +} + +// MassDeleteAsync starts jobs to delete several computes with AsyncMode +func (c Compute) MassDeleteAsync(ctx context.Context, req MassDeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMassDeleteRequest{ + MassDeleteRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/massDelete" + + 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/mass_reboot.go b/pkg/cloudbroker/compute/mass_reboot.go new file mode 100644 index 0000000..dcbfa95 --- /dev/null +++ b/pkg/cloudbroker/compute/mass_reboot.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperMassRebootRequest struct { + MassRebootRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperMassRebootRequest{ + MassRebootRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/massReboot" + + res, err := c.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 +} + +// MassRebootAsync starts jobs to reboot several computes with AsyncMode +func (c Compute) MassRebootAsync(ctx context.Context, req MassRebootRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMassRebootRequest{ + MassRebootRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/massReboot" + + 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/mass_start.go b/pkg/cloudbroker/compute/mass_start.go new file mode 100644 index 0000000..ea5bbba --- /dev/null +++ b/pkg/cloudbroker/compute/mass_start.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperMassStartRequest struct { + MassStartRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperMassStartRequest{ + MassStartRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/massStart" + + res, err := c.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 +} + +// MassStartAsync starts jobs to start several computes with AsyncMode +func (c Compute) MassStartAsync(ctx context.Context, req MassStartRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMassStartRequest{ + MassStartRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/massStart" + + 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/mass_stop.go b/pkg/cloudbroker/compute/mass_stop.go new file mode 100644 index 0000000..6ca40da --- /dev/null +++ b/pkg/cloudbroker/compute/mass_stop.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperMassStopRequest struct { + MassStopRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperMassStopRequest{ + MassStopRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/massStop" + + res, err := c.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 +} + +// MassStopAsync starts jobs to stop several computes with AsyncMode +func (c Compute) MassStopAsync(ctx context.Context, req MassStopRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMassStopRequest{ + MassStopRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/massStop" + + 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/migrate.go b/pkg/cloudbroker/compute/migrate.go new file mode 100644 index 0000000..8b6477b --- /dev/null +++ b/pkg/cloudbroker/compute/migrate.go @@ -0,0 +1,76 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 Node ID to migrate this compute to + // Required: false + TargetNodeID uint64 `url:"targetNodeId,omitempty" json:"targetNodeId,omitempty"` +} + +type AsyncWrapperMigrateRequest struct { + MigrateRequest + SyncMode bool `url:"sync"` +} + +// Migrate migrates compute to another node +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" + + syncReq := AsyncWrapperMigrateRequest{MigrateRequest: req, SyncMode: true} + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, syncReq) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} + +// AsyncMigrate migrates compute to another node in async mode +func (c Compute) AsyncMigrate(ctx context.Context, req MigrateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/migrate" + + asyncReq := AsyncWrapperMigrateRequest{MigrateRequest: req, SyncMode: false} + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, asyncReq) + if err != nil { + return " ", err + } + + var taskID string + + err = json.Unmarshal(res, &taskID) + if err != nil { + return "", err + } + + return taskID, nil +} diff --git a/pkg/cloudbroker/compute/migrate_abort.go b/pkg/cloudbroker/compute/migrate_abort.go new file mode 100644 index 0000000..c1e527e --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_abort.go @@ -0,0 +1,68 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateAbortRequest struct to abort migration +type MigrateAbortRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +type wrapperMigrateAbortRequest struct { + MigrateAbortRequest + + AsyncMode bool `url:"asyncMode"` +} + +// MigrateAbort aborts compute migration +func (c Compute) MigrateAbort(ctx context.Context, req MigrateAbortRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateAbortRequest{ + MigrateAbortRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/migrate_abort" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} + +// MigrateAbortAsync aborts compute migration with AsyncMode +func (c Compute) MigrateAbortAsync(ctx context.Context, req MigrateAbortRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateAbortRequest{ + MigrateAbortRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/migrate_abort" + + 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/migrate_storage.go b/pkg/cloudbroker/compute/migrate_storage.go new file mode 100644 index 0000000..cc0a92d --- /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/v15/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 node ID + // Required: true + NodeID uint64 `url:"node_id" json:"node_id" 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 node, 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..d99f73a --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_storage_abort.go @@ -0,0 +1,76 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperMigrateStorageAbortRequest struct { + MigrateStorageAbortRequest + + AsyncMode bool `url:"asyncMode"` +} + +// MigrateStorageAbort aborts complex compute migration job +func (c Compute) MigrateStorageAbort(ctx context.Context, req MigrateStorageAbortRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateStorageAbortRequest{ + MigrateStorageAbortRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/migrateStorageAbort" + + res, err := c.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 +} + +// MigrateStorageAbortAsync aborts complex compute migration job with AsyncMode +func (c Compute) MigrateStorageAbortAsync(ctx context.Context, req MigrateStorageAbortRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateStorageAbortRequest{ + MigrateStorageAbortRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/migrateStorageAbort" + + 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/migrate_storage_clean_up.go b/pkg/cloudbroker/compute/migrate_storage_clean_up.go new file mode 100644 index 0000000..6b27565 --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_storage_clean_up.go @@ -0,0 +1,73 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperMigrateStorageCleanUpRequest struct { + MigrateStorageCleanUpRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateStorageCleanUpRequest{ + MigrateStorageCleanUpRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/migrateStorageCleanup" + + res, err := c.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 +} + +// MigrateStorageCleanUpAsync cleanup resources after finished migration with AsyncMode +func (c Compute) MigrateStorageCleanUpAsync(ctx context.Context, req MigrateStorageCleanUpRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateStorageCleanUpRequest{ + MigrateStorageCleanUpRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/migrateStorageCleanup" + + 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/migrate_storage_info.go b/pkg/cloudbroker/compute/migrate_storage_info.go new file mode 100644 index 0000000..cace350 --- /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/v15/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/migrate_storage_list.go b/pkg/cloudbroker/compute/migrate_storage_list.go new file mode 100644 index 0000000..f993bec --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_storage_list.go @@ -0,0 +1,55 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateStorageListRequest struct to get list of jobs +type MigrateStorageListRequest struct { + // Find by job ID + // Required: false + MigrationJobID uint64 `url:"migration_job_id,omitempty" json:"migration_job_id,omitempty"` + + // If True then return completed jobs + // 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:"sort_by,omitempty" json:"sort_by,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// MigrateStorageList gets list of the jobs. +func (c Compute) MigrateStorageList(ctx context.Context, req MigrateStorageListRequest) (*ListMigrateStorage, error) { + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/migrate_storage_list" + + res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return nil, err + } + + list := ListMigrateStorage{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} diff --git a/pkg/cloudbroker/compute/migrate_to_zone.go b/pkg/cloudbroker/compute/migrate_to_zone.go new file mode 100644 index 0000000..2c899c8 --- /dev/null +++ b/pkg/cloudbroker/compute/migrate_to_zone.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move compute to another zone +type MigrateToZoneRequest struct { + // ID of the compute instance to move + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zoneId" json:"zoneId" validate:"required"` +} + +type wrapperMigrateToZoneRequest struct { + MigrateToZoneRequest + + AsyncMode bool `url:"asyncMode"` +} + +// MigrateToZone moves compute to new zone +func (c Compute) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateToZoneRequest{ + MigrateToZoneRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/migrateToZone" + + res, err := c.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 +} + +// MigrateToZoneAsync moves compute to new zone with AsyncMode +func (c Compute) MigrateToZoneAsync(ctx context.Context, req MigrateToZoneRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMigrateToZoneRequest{ + MigrateToZoneRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/migrateToZone" + + 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/models.go b/pkg/cloudbroker/compute/models.go new file mode 100644 index 0000000..2d489d7 --- /dev/null +++ b/pkg/cloudbroker/compute/models.go @@ -0,0 +1,1568 @@ +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 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 + +// Main information about snapshot +type ItemSnapshot struct { + // Compute info + Compute RecordCompute `json:"compute"` + + // List of disk IDs + Disks []uint64 `json:"disks"` + + // GUID + GUID string `json:"guid"` + + // Label + Label string `json:"label"` + + // Memory dump image ID + MemoryDumpImage uint64 `json:"memory_dump_image"` + + // 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 { + // 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"` + + // Enable security groups + EnableSecGroups bool `json:"enable_secgroups"` + + // FLIPGroup ID + FLIPGroupID uint64 `json:"flipgroupId"` + + // GUID + GUID string `json:"guid"` + + // IP address + IPAddress string `json:"ipAddress"` + + // Libvirt Settings + LibvirtSettings LibvirtSettings `json:"libvirtSettings"` + + // Listen SSH + ListenSSH bool `json:"listenSsh"` + + // 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"` + + // Node id + NodeID int `json:"nodeId"` + + // PCI slot + PCISlot int64 `json:"pciSlot"` + + // SDN interface ID + SDNInterfaceID string `json:"sdn_interface_id"` + + // List of security groups + SecurityGroups []uint64 `json:"security_groups"` + + // QOS + QOS QOS `json:"qos"` + + // Target + Target string `json:"target"` + + // Type + Type string `json:"type"` + + // List of trunk tags + TrunkTags []uint64 `json:"trunk_tags"` + + // 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 Detailed audits +type ListDetailedAudits struct { + // Data + Data []ItemDetailedAudit `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 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"` + + // Discard + Discard string `json:"discard"` + + // Block Size + BlockSize string `json:"block_size"` + + // Boot partition + BootPartition uint64 `json:"bootPartition"` + + // Bus number + BusNumber uint64 `json:"bus_number"` + + // Chache + Cache string `json:"cache"` + + // Created by + CreatedBy string `json:"createdBy"` + + // Created time + CreatedTime uint64 `json:"createdTime"` + + // Device name + DeviceName string `json:"devicename"` + + // Deleted by + DeletedBy string `json:"deletedBy"` + + // Deleted time + DeletedTime uint64 `json:"deletedTime"` + + // 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"` + + // Independent + Independent bool `json:"independent"` + + // 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"` + + // Params + Params string `json:"params"` + + // Parent ID + ParentID uint64 `json:"parentId"` + + // Password + Password string `json:"passwd"` + + // PCI slot + PCISlot int64 `json:"pci_slot"` + + // Pool + Pool string `json:"pool"` + + // Present to + PresentTo map[string]uint64 `json:"presentTo"` + + // Provision + Provision string `json:"provision"` + + // 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"` + + // Size available + SizeAvailable float64 `json:"sizeAvailable"` + + // List detailed snapshots + Snapshots ListDetailedSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Storage policy id of disk. + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // Type + Type string `json:"type"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // UpdatedTime + UpdatedTime uint64 `json:"updatedTime"` + + // Read-only + ReadOnly bool `json:"read_only"` +} + +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"` + + // Enable security groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // List of security groups + SecGroups []uint64 `json:"security_groups"` + + // SDN interface ID + SDNInterfaceID string `json:"sdn_interface_id"` + + // Target + Target string `json:"target"` + + // Trunk tags + TrunkTags string `json:"trunk_tags"` + + // 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"` + + // Auto start when node restarted + AutoStart bool `json:"autoStart"` + + // Architecture + Arch string `json:"arch"` + + // Boot image ID + BootImageID uint64 `json:"boot_image_id"` + + // Boot order + BootOrder []string `json:"bootOrder"` + + // Boot type + BootType string `json:"bootType"` + + // 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"` + + // Clock + Clock string `json:"clock"` + + // 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"` + + // Hot resize + HotResize bool `json:"hotResize"` + + // ID + ID uint64 `json:"id"` + + // List interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Live migration job ID + LiveMigrationJobID uint64 `json:"live_migration_job_id"` + + // Loader type + LoaderType string `json:"loaderType"` + + // 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"` + + // network interface naming + NetworkInterfaceNaming string `json:"networkInterfaceNaming"` + + // Numa Affinity + NumaAffinity string `json:"numaAffinity"` + + //NumaNodeId + NumaNodeId int64 `json:"numaNodeId"` + + // List OS users + OSUsers ListOSUsers `json:"osUsers"` + + // Name of OS + OSVersion string `json:"os_version"` + + // Pinned to node + PinnedToNode int64 `json:"pinnedToNode"` + + // PreferredCPU + PreferredCPU []int64 `json:"preferredCpu"` + + // CPU alignment profile + CPUAlignmentProfile CPUAlignmentProfile `json:"cpu_alignment_profile"` + + // Qemu_quest + QemuQuest QemuQuest `json:"qemu_guest"` + + // ReadOnly indicates read-only mode state + ReadOnly bool `json:"read_only"` + + // 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"` + + // Node ID + NodeID uint64 `json:"node_id"` + + // Node name + NodeName string `json:"nodeName"` + + // 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"` + + // Weight + Weight uint64 `json:"weight"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +type QemuQuest struct { + Enabled bool `json:"enabled"` + EnabledAgentFeatures []string `json:"enabled_agent_features"` + GUID string `json:"guid"` + LastUpdate uint64 `json:"last_update"` + User string `json:"user"` +} + +type CPUAlignmentProfile struct { + Model string `json:"model"` + Name string `json:"name"` + Vendor string `json:"vendor"` +} + +// 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"` + + // Auto start when node restarted + AutoStart bool `json:"autoStart"` + + // Architecture + Arch string `json:"arch"` + + // Boot image ID + BootImageID uint64 `json:"boot_image_id"` + + // Boot order + BootOrder []string `json:"bootOrder"` + + // Boot type + BootType string `json:"bootType"` + + // 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"` + + // Clock + Clock string `json:"clock"` + + // 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"` + + // Hot resize + HotResize bool `json:"hotResize"` + + // ID + ID uint64 `json:"id"` + + // Image ID + ImageID uint64 `json:"imageId"` + + // Image name + ImageName string `json:"imageName"` + + // List interfaces + Interfaces ListInterfaces `json:"interfaces"` + + // Loader meta iso information + LoaderMetaIso LoaderMetaIso `json:"loaderMetaIso"` + + // Live migration job ID + LiveMigrationJobID uint64 `json:"live_migration_job_id"` + + // Loader type + LoaderType string `json:"loaderType"` + + // 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"` + + // 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"` + + // network interface naming + NetworkInterfaceNaming string `json:"networkInterfaceNaming"` + + // NumaAffinity + NumaAffinity string `json:"numaAffinity"` + + //NumaNodeId + NumaNodeId int64 `json:"numaNodeId"` + + // List OS users + OSUsers ListOSUsers `json:"osUsers"` + + // Name of OS + OSVersion string `json:"os_version"` + + // Pinned to node + PinnedToNode int64 `json:"pinnedToNode"` + + // PreferredCPU + PreferredCPU []int64 `json:"preferredCpu"` + + // CPU alignment profile + CPUAlignmentProfile CPUAlignmentProfile `json:"cpu_alignment_profile"` + + // Qemu_quest + QemuQuest QemuQuest `json:"qemu_guest"` + + // ReadOnly indicates read-only mode state + ReadOnly bool `json:"read_only"` + + // 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"` + + // Node ID + NodeID uint64 `json:"node_id"` + + // Node name + NodeName string `json:"nodeName"` + + // 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 + VGPUs []VGPUItem `json:"vgpus"` + + // VNC password + VNCPassword string `json:"vncPasswd"` + + // Weight + Weight uint64 `json:"weight"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` + + //todo + _ uint64 `json:"nodeId"` +} + +type LoaderMetaIso struct { + // Name + DeviceName string `json:"devicename"` + + // Path + Path string `json:"path"` +} + +type VGPUItem struct { + // ID + ID uint64 `json:"id"` + + // GID + GID uint64 `json:"gid"` + + // Type + Type string `json:"type"` + + // Mode + Mode string `json:"mode"` + + // Status + Status string `json:"status"` + + // ProfileID + ProfileID uint64 `json:"profileId"` + + // RAM + RAM uint64 `json:"ram"` + + // LastUpdateTime + LastUpdateTime uint64 `json:"lastUpdateTime"` + + // CreatedTime + CreatedTime uint64 `json:"createdTime"` + + // DeletedTime + DeletedTime uint64 `json:"deletedTime"` + + // VMID + VMID uint64 `json:"vmid"` + + // PGPuid + PGPuid uint64 `json:"pgpuid"` + + // ReferenceID + ReferenceID string `json:"referenceId"` + + // AccountID + AccountID uint64 `json:"accountId"` + + // RgID + RgID uint64 `json:"rgId"` + + // LastClaimedBy + LastClaimedBy uint64 `json:"lastClaimedBy"` + + // PCISlot + PCISlot uint64 `json:"pciSlot"` + + // BusNumber + BusNumber uint64 `json:"bus_number"` + + // GUID + GUID uint64 `json:"guid"` +} + +// 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 + + // NID + NID uint64 `json:"nid"` + + // Total disk size + TotalDiskSize uint64 `json:"totalDisksSize"` + + // VINS connected + VINSConnected uint64 `json:"vinsConnected"` +} + +// Main information about compute for list +type ItemDeletedCompute 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 { + // ID + ID uint64 `json:"id"` + + // SEP ID + SepID int64 `json:"sepId"` + + // Read-only + ReadOnly bool `json:"read_only"` +} + +// List computes +type ListComputes struct { + // Data + Data []ItemCompute `json:"data"` + + // EntryCount + EntryCount uint64 `json:"entryCount"` +} + +// List computes +type ListDeletedComputes struct { + // Data + Data []ItemDeletedCompute `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"` + + // Node ID + NodeID uint64 `json:"nodeId"` + + // 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"` +} + +type ListMigrateStorage struct { + EntryCount uint64 `json:"entryCount"` + Data []MigrateStorageItem `json:"data"` +} + +type MigrateStorageItem struct { + // Migration completion status + Completed bool `json:"completed"` + + // Domain name + DomainName string `json:"domainName"` + + // Migration job ID + ID uint64 `json:"id"` + + // Migration process log + Log []string `json:"log"` + + // Source node ID + SourceNodeID uint64 `json:"sourceNodeId"` + + // Migration status + Status string `json:"status"` + + // Target node ID + TargetNodeID uint64 `json:"targetNodeId"` +} + +type RecordCloneAbort struct { + // Disk ID + DiskID uint64 `json:"disk_id"` + + // Aborted + Aborted bool `json:"aborted"` + + // Blockcopy abort job ID + BlockcopyAbortJobID string `json:"blockcopy_abort_job_id"` +} + +type RecordCloneStatus struct { + // Disk ID + DiskID int `json:"disk_id"` + + // Clone Status + Status CloneStatus `json:"status"` +} + +type CloneStatus struct { + // Type + Type int `json:"type"` + + // Copy speed + Bandwidth int `json:"bandwidth"` + + // Current progress + Cur int `json:"cur"` + + // Total size + End int `json:"end"` + + // Operation status + Ready bool `json:"ready"` + + // Progress percent + ProgressPercent int `json:"progress_percent"` +} + +type CheckComputePlacementError struct { + // Code + Code int64 `json:"code"` + + // Message + Message string `json:"message"` +} + +type CheckComputePlacementItem struct { + // Nide IDs + NodeIDs []uint64 `json:"node_ids"` + // error + Error CheckComputePlacementError `json:"error,omitempty"` +} + +type CheckComputePlacementResult map[uint64]CheckComputePlacementItem diff --git a/pkg/cloudbroker/compute/move_to_rg.go b/pkg/cloudbroker/compute/move_to_rg.go new file mode 100644 index 0000000..ab618b3 --- /dev/null +++ b/pkg/cloudbroker/compute/move_to_rg.go @@ -0,0 +1,90 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperMoveToRGRequest struct { + MoveToRGRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperMoveToRGRequest{ + MoveToRGRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/moveToRg" + + res, err := c.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 +} + +// MoveToRGAsync moves compute instance to new resource group with AsyncMode +func (c Compute) MoveToRGAsync(ctx context.Context, req MoveToRGRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperMoveToRGRequest{ + MoveToRGRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/moveToRg" + + 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/net_attach.go b/pkg/cloudbroker/compute/net_attach.go new file mode 100644 index 0000000..42ce426 --- /dev/null +++ b/pkg/cloudbroker/compute/net_attach.go @@ -0,0 +1,141 @@ +package compute + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 + // `EMPTY` for connect empty network + // `SDN` for connect to SDN + // `TRUNK` for connect to TRUNK + // 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"` + + // MAC address + // Required: false + MACAddr string `url:"mac_addr,omitempty" json:"mac_addr,omitempty"` + + // Used for EXTNET, TRUNK and DPDK + // Must be 1500-9216 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` + + // Net mask + // Used only to DPDK or VFNIC net type + // Required: false + NetMask uint64 `url:"netMask,omitempty" json:"netMask,omitempty"` + + // SDN Segment ID + // Required: false + SDNSegmentID string `url:"sdn_segment_id,omitempty" json:"sdn_segment_id,omitempty"` + + // SDN Object Group IDs + // Required: false + SDNObjectGroupIDs []string `url:"sdn_object_group_ids,omitempty" json:"sdn_object_group_ids,omitempty"` + + // SDN Logical Port Display Name + // Required: false + SDNLogicalPortDisplayName string `url:"sdn_logical_port_display_name,omitempty" json:"sdn_logical_port_display_name,omitempty"` + + // SDN Logical Port Description + // Required: false + SDNLogicalPortDescription string `url:"sdn_logical_port_description,omitempty" json:"sdn_logical_port_description,omitempty"` + + // Unique identifier of logical port on SDN side + // Required: false + SDNInterfaceID string `url:"sdn_interface_id,omitempty" json:"sdn_interface_id,omitempty" validate:"omitempty"` + + // List of security group IDs to assign to this interface + // Required: false + SecGroups []uint64 `url:"security_groups,omitempty" json:"security_groups,omitempty"` + + // Flag indicating whether security groups are enabled for this interface + // Not applicable to netType VFNIC, TRUNK, or SDN + // Required: false + EnableSecGroups bool `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty"` + + // Flag indicating whether this interface is enabled (only for VINS, EXTNET, DPDK, SDN, TRUNK) + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` +} + +type wrapperNetAttachRequest struct { + NetAttachRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperNetAttachRequest{ + NetAttachRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/netAttach" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return nil, err + } + + info := RecordNetAttach{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// NetAttachAsync attaches network to compute with AsyncMode +func (c Compute) NetAttachAsync(ctx context.Context, req NetAttachRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperNetAttachRequest{ + NetAttachRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/netAttach" + + 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/net_detach.go b/pkg/cloudbroker/compute/net_detach.go new file mode 100644 index 0000000..d06acec --- /dev/null +++ b/pkg/cloudbroker/compute/net_detach.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperNetDetachRequest struct { + NetDetachRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperNetDetachRequest{ + NetDetachRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/netDetach" + + res, err := c.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 +} + +// NetDetachAsync detaches network from compute with AsyncMode +func (c Compute) NetDetachAsync(ctx context.Context, req NetDetachRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperNetDetachRequest{ + NetDetachRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/netDetach" + + 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/net_qos.go b/pkg/cloudbroker/compute/net_qos.go new file mode 100644 index 0000000..854ac5d --- /dev/null +++ b/pkg/cloudbroker/compute/net_qos.go @@ -0,0 +1,94 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperNetQOSRequest struct { + NetQOSRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperNetQOSRequest{ + NetQOSRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/netQos" + + res, err := c.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 +} + +// NetQOSAsync updates compute interfaces QOS with AsyncMode +func (c Compute) NetQOSAsync(ctx context.Context, req NetQOSRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperNetQOSRequest{ + NetQOSRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/netQos" + + 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/pause.go b/pkg/cloudbroker/compute/pause.go new file mode 100644 index 0000000..71d594b --- /dev/null +++ b/pkg/cloudbroker/compute/pause.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// PauseRequest struct to pause compute +type PauseRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperPauseRequest struct { + PauseRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperPauseRequest{ + PauseRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/pause" + + res, err := c.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 +} + +// PauseAsync pauses compute with AsyncMode +func (c Compute) PauseAsync(ctx context.Context, req PauseRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPauseRequest{ + PauseRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/pause" + + 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/pfw_add.go b/pkg/cloudbroker/compute/pfw_add.go new file mode 100644 index 0000000..0939e9b --- /dev/null +++ b/pkg/cloudbroker/compute/pfw_add.go @@ -0,0 +1,91 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperPFWAddRequest struct { + PFWAddRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperPFWAddRequest{ + PFWAddRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/pfwAdd" + + 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 +} + +// PFWAddAsync adds port forward rule with AsyncMode +func (c Compute) PFWAddAsync(ctx context.Context, req PFWAddRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPFWAddRequest{ + PFWAddRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/pfwAdd" + + 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/pfw_del.go b/pkg/cloudbroker/compute/pfw_del.go new file mode 100644 index 0000000..8578f77 --- /dev/null +++ b/pkg/cloudbroker/compute/pfw_del.go @@ -0,0 +1,94 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperPFWDelRequest struct { + PFWDelRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperPFWDelRequest{ + PFWDelRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/pfwDel" + + res, err := c.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 +} + +// PFWDelAsync deletes port forward rule with AsyncMode +func (c Compute) PFWDelAsync(ctx context.Context, req PFWDelRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPFWDelRequest{ + PFWDelRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/pfwDel" + + 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/pfw_list.go b/pkg/cloudbroker/compute/pfw_list.go new file mode 100644 index 0000000..997d5a7 --- /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/v15/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_node.go b/pkg/cloudbroker/compute/pin_to_node.go new file mode 100644 index 0000000..2afc6c2 --- /dev/null +++ b/pkg/cloudbroker/compute/pin_to_node.go @@ -0,0 +1,84 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// PinToNodeRequest struct to pin compute to node +type PinToNodeRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Node ID to pin to + // Required: false + TargetNodeID uint64 `url:"targetNodeId" json:"targetNodeId"` + + // Try to migrate or not if compute in running states + // Required: false + Force bool `url:"force" json:"force"` + + // Auto start when node restarted + // Required: false + // Default: false + AutoStart bool `url:"autoStart" json:"autoStart"` +} + +type wrapperPinToNodeRequest struct { + PinToNodeRequest + + AsyncMode bool `url:"asyncMode"` +} + +// PinToNode pins compute to current node +func (c Compute) PinToNode(ctx context.Context, req PinToNodeRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPinToNodeRequest{ + PinToNodeRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/pin_to_node" + + 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 +} + +// PinToNodeAsync pins compute to current node with AsyncMode +func (c Compute) PinToNodeAsync(ctx context.Context, req PinToNodeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPinToNodeRequest{ + PinToNodeRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/pin_to_node" + + 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/power_cycle.go b/pkg/cloudbroker/compute/power_cycle.go new file mode 100644 index 0000000..3d1a105 --- /dev/null +++ b/pkg/cloudbroker/compute/power_cycle.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperPowerCycleRequest struct { + PowerCycleRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperPowerCycleRequest{ + PowerCycleRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/powerCycle" + + res, err := c.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 +} + +// PowerCycleAsync makes force stop and start compute with AsyncMode +func (c Compute) PowerCycleAsync(ctx context.Context, req PowerCycleRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperPowerCycleRequest{ + PowerCycleRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/powerCycle" + + 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/raise_down.go b/pkg/cloudbroker/compute/raise_down.go new file mode 100644 index 0000000..fa300c7 --- /dev/null +++ b/pkg/cloudbroker/compute/raise_down.go @@ -0,0 +1,44 @@ +package compute + +import ( + "context" + "net/http" + "strconv" +) + +type wrapperRaiseDownRequest struct { + AsyncMode bool `url:"asyncMode"` +} + +// RaiseDown starting all computes in "DOWN" tech status +func (c Compute) RaiseDown(ctx context.Context) (bool, error) { + reqWrapped := wrapperRaiseDownRequest{AsyncMode: false} + + url := "/cloudbroker/compute/raiseDown" + + res, err := c.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 +} + +// RaiseDownAsync starting all computes in "DOWN" tech status with AsyncMode +func (c Compute) RaiseDownAsync(ctx context.Context) (string, error) { + reqWrapped := wrapperRaiseDownRequest{AsyncMode: true} + + url := "/cloudbroker/compute/raiseDown" + + 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/reboot.go b/pkg/cloudbroker/compute/reboot.go new file mode 100644 index 0000000..a06ca0b --- /dev/null +++ b/pkg/cloudbroker/compute/reboot.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// RebootRequest struct to reboot compute +type RebootRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperRebootRequest struct { + RebootRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperRebootRequest{ + RebootRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/reboot" + + res, err := c.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 +} + +// RebootAsync reboots compute with AsyncMode +func (c Compute) RebootAsync(ctx context.Context, req RebootRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRebootRequest{ + RebootRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/reboot" + + 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/redeploy.go b/pkg/cloudbroker/compute/redeploy.go new file mode 100644 index 0000000..3953eac --- /dev/null +++ b/pkg/cloudbroker/compute/redeploy.go @@ -0,0 +1,103 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,omitempty"` + + // Storage policy id of compute. The rules of the specified storage policy will be used. + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,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"` +} + +type wrapperRedeployRequest struct { + RedeployRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperRedeployRequest{ + RedeployRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/redeploy" + + res, err := c.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 +} + +// RedeployAsync redeploys compute with AsyncMode +func (c Compute) RedeployAsync(ctx context.Context, req RedeployRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRedeployRequest{ + RedeployRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/redeploy" + + 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/reset.go b/pkg/cloudbroker/compute/reset.go new file mode 100644 index 0000000..305916f --- /dev/null +++ b/pkg/cloudbroker/compute/reset.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ResetRequest struct to reset compute +type ResetRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperResetRequest struct { + ResetRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperResetRequest{ + ResetRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/reset" + + res, err := c.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 +} + +// ResetAsync resets compute with AsyncMode +func (c Compute) ResetAsync(ctx context.Context, req ResetRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperResetRequest{ + ResetRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/reset" + + 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/resize.go b/pkg/cloudbroker/compute/resize.go new file mode 100644 index 0000000..1a5fa47 --- /dev/null +++ b/pkg/cloudbroker/compute/resize.go @@ -0,0 +1,99 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False + // Required: false + PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` +} + +// GetRAM returns RAM field values +func (r ResizeRequest) GetRAM() map[string]uint64 { + + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +type wrapperResizeRequest struct { + ResizeRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperResizeRequest{ + ResizeRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/resize" + + res, err := c.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 +} + +// ResizeAsync resizes compute instance with AsyncMode +func (c Compute) ResizeAsync(ctx context.Context, req ResizeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperResizeRequest{ + ResizeRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/resize" + + 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/restore.go b/pkg/cloudbroker/compute/restore.go new file mode 100644 index 0000000..461eaca --- /dev/null +++ b/pkg/cloudbroker/compute/restore.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// RestoreRequest struct to restore compute +type RestoreRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperRestoreRequest struct { + RestoreRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperRestoreRequest{ + RestoreRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/restore" + + res, err := c.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 +} + +// RestoreAsync restores compute from recycle bin with AsyncMode +func (c Compute) RestoreAsync(ctx context.Context, req RestoreRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperRestoreRequest{ + RestoreRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/restore" + + 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/resume.go b/pkg/cloudbroker/compute/resume.go new file mode 100644 index 0000000..194994c --- /dev/null +++ b/pkg/cloudbroker/compute/resume.go @@ -0,0 +1,71 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ResumeRequest struct to resume compute +type ResumeRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +type wrapperResumeRequest struct { + ResumeRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperResumeRequest{ + ResumeRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/resume" + + res, err := c.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 +} + +// ResumeAsync resumes Compute from paused state with AsyncMode +func (c Compute) ResumeAsync(ctx context.Context, req ResumeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperResumeRequest{ + ResumeRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/resume" + + 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/serialize.go b/pkg/cloudbroker/compute/serialize.go new file mode 100644 index 0000000..2eb4c22 --- /dev/null +++ b/pkg/cloudbroker/compute/serialize.go @@ -0,0 +1,43 @@ +package compute + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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_cpu_alignment_profile.go b/pkg/cloudbroker/compute/set_cpu_alignment_profile.go new file mode 100644 index 0000000..36b0545 --- /dev/null +++ b/pkg/cloudbroker/compute/set_cpu_alignment_profile.go @@ -0,0 +1,43 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// SetCPUAlignmentProfileRequest struct to set CPU alignment profile for computes +type SetCPUAlignmentProfileRequest struct { + // IDs of the compute instances + // Required: true + ComputeIDs []int64 `url:"compute_ids" json:"compute_ids" validate:"min=1"` + + // CPU alignment profile name + // Required: true + CPUAlignmentProfile string `url:"cpu_alignment_profile" json:"cpu_alignment_profile" validate:"required"` +} + +// SetCPUAlignmentProfile sets CPU alignment profile for computes +func (c Compute) SetCPUAlignmentProfile(ctx context.Context, req SetCPUAlignmentProfileRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/set_cpu_alignment_profile" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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_custom_fields.go b/pkg/cloudbroker/compute/set_custom_fields.go new file mode 100644 index 0000000..f930744 --- /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/v15/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..f4e542a --- /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/v15/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/shared_snapshot_merge_status.go b/pkg/cloudbroker/compute/shared_snapshot_merge_status.go new file mode 100644 index 0000000..31d443b --- /dev/null +++ b/pkg/cloudbroker/compute/shared_snapshot_merge_status.go @@ -0,0 +1,33 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// SharedSnapshotMergeStatusRequest struct to get shared snapshot merge status +type SharedSnapshotMergeStatusRequest struct { + // ID of compute instance to get log for + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` +} + +// SharedSnapshotMergeStatus shared snapshots merge status +// returns a string representing either the current status or the progress percentage +func (c Compute) SharedSnapshotMergeStatus(ctx context.Context, req SharedSnapshotMergeStatusRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/shared_snapshot_merge_status" + + res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/snapshot_create.go b/pkg/cloudbroker/compute/snapshot_create.go new file mode 100644 index 0000000..ff00895 --- /dev/null +++ b/pkg/cloudbroker/compute/snapshot_create.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 + // Allowed characters: a-z, 0-9, spaces, punctuation except '<' and '>' + // Maximum length: 36 characters + // Required: true + Label string `url:"label" json:"label" validate:"required,max=36,excludesall=<>"` + + // Create snapshot with memory dump + // Required: false + // Default: false + WithMemory bool `url:"with_memory" json:"with_memory"` +} + +type wrapperSnapshotCreateRequest struct { + SnapshotCreateRequest + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperSnapshotCreateRequest{ + SnapshotCreateRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/snapshotCreate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped) + if err != nil { + return "", err + } + + result := strings.ReplaceAll(string(res), "\"", "") + + return result, nil +} + +// SnapshotCreateAsync creates compute snapshot in async mode +func (c Compute) SnapshotCreateAsync(ctx context.Context, req SnapshotCreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSnapshotCreateRequest{ + SnapshotCreateRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/snapshotCreate" + + 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_delete.go b/pkg/cloudbroker/compute/snapshot_delete.go new file mode 100644 index 0000000..4a39402 --- /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/v15/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..ebb4155 --- /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/v15/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..10d030e --- /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/v15/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..cea878c --- /dev/null +++ b/pkg/cloudbroker/compute/snapshot_rollback.go @@ -0,0 +1,83 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Rollback with memory dump restore + // Required: false + // Default: true + WithMemory interface{} `url:"with_memory,omitempty" json:"with_memory,omitempty" validate:"omitempty,isBool"` + + // ID of the node to rollback on + // Required: false + NodeID uint64 `url:"node_id,omitempty" json:"node_id,omitempty"` +} + +type wrapperSnapshotRollbackRequest struct { + SnapshotRollbackRequest + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperSnapshotRollbackRequest{ + SnapshotRollbackRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/snapshotRollback" + + res, err := c.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 +} + +// SnapshotRollbackAsync rollbacks specified compute snapshot in async mode +func (c Compute) SnapshotRollbackAsync(ctx context.Context, req SnapshotRollbackRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperSnapshotRollbackRequest{ + SnapshotRollbackRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/snapshotRollback" + + 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_usage.go b/pkg/cloudbroker/compute/snapshot_usage.go new file mode 100644 index 0000000..6a735ba --- /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/v15/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..e78e578 --- /dev/null +++ b/pkg/cloudbroker/compute/sorting.go @@ -0,0 +1,193 @@ +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 +} + +// SortByCPU sorts ListDeletedComputes by the CPU core amount in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListDeletedComputes) SortByCPU(inverse bool) ListDeletedComputes { + 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 ListDeletedComputes by the RAM amount in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListDeletedComputes) SortByRAM(inverse bool) ListDeletedComputes { + 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 ListDeletedComputes by the CreatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListDeletedComputes) SortByCreatedTime(inverse bool) ListDeletedComputes { + 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 ListDeletedComputes by the UpdatedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListDeletedComputes) SortByUpdatedTime(inverse bool) ListDeletedComputes { + 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 ListDeletedComputes by the DeletedTime field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lc ListDeletedComputes) SortByDeletedTime(inverse bool) ListDeletedComputes { + 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..f1d38df --- /dev/null +++ b/pkg/cloudbroker/compute/start.go @@ -0,0 +1,79 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 node to start compute + // Required: false + NodeID uint64 `url:"node_id,omitempty" json:"node_id,omitempty"` +} + +type wrapperStartRequest struct { + StartRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperStartRequest{ + StartRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/start" + + res, err := c.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 +} + +// StartAsync starts compute with AsyncMode +func (c Compute) StartAsync(ctx context.Context, req StartRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperStartRequest{ + StartRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/start" + + 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/start_migration_in.go b/pkg/cloudbroker/compute/start_migration_in.go new file mode 100644 index 0000000..7b6e26f --- /dev/null +++ b/pkg/cloudbroker/compute/start_migration_in.go @@ -0,0 +1,36 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// StartMigrationINRequest struct to start compute for external migration in +type StartMigrationINRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // ID of the node where the compute will be staged for migration-in + // Required: false + NodeID uint64 `url:"node_id,omitempty" json:"node_id,omitempty"` +} + +// StartMigrationIN starts compute for external migration in +func (c Compute) StartMigrationIN(ctx context.Context, req StartRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/start_migration_in" + + 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/start_migration_out.go b/pkg/cloudbroker/compute/start_migration_out.go new file mode 100644 index 0000000..7b0e54e --- /dev/null +++ b/pkg/cloudbroker/compute/start_migration_out.go @@ -0,0 +1,62 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// StartMigrationOutRequest struct to start compute for external migration out +type StartMigrationOutRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"compute_id" json:"compute_id" validate:"required"` + + // Remote libvirt host to connect to + // Required: true + Target string `url:"target" json:"target" validate:"required"` + + // Graphics handling on the destination + // Required: true + Graphics string `url:"graphics" json:"graphics" validate:"required"` + + // Optional new domain name on the destination + // Required: false + NewName string `url:"new_name,omitempty" json:"new_name,omitempty"` + + // When true, adds libvirt's UNSAFE flag to force migration even if libvirt marks it unsafe. + // Default: false + // Required: false + UseUnsafe bool `url:"use_unsafe,omitempty" json:"use_unsafe,omitempty"` + + // Mapping of guest disk target names to absolute paths on the destination host. + // Required: false + DiskMap map[string]string `url:"diskmap,omitempty" json:"diskmap,omitempty"` + + // Mapping of network interfaces + // Required: false + NetMap map[string]string `url:"netmap,omitempty" json:"netmap,omitempty"` + + // Mapping for CD/DVD devices or their source paths to new ISO/device paths on the destination + // Required: false + CDROMMap map[string]string `url:"cdrommap,omitempty" json:"cdrommap,omitempty"` +} + +// StartMigrationOut starts compute for external migration out +func (c Compute) StartMigrationOut(ctx context.Context, req StartMigrationOutRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/start_migration_out" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/stop.go b/pkg/cloudbroker/compute/stop.go new file mode 100644 index 0000000..377457c --- /dev/null +++ b/pkg/cloudbroker/compute/stop.go @@ -0,0 +1,75 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +type wrapperStopRequest struct { + StopRequest + + AsyncMode bool `url:"asyncMode"` +} + +// 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)) + } + + reqWrapped := wrapperStopRequest{ + StopRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/compute/stop" + + res, err := c.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 +} + +// StopAsync stops compute with AsyncMode +func (c Compute) StopAsync(ctx context.Context, req StopRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperStopRequest{ + StopRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/compute/stop" + + 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/stop_migration_in.go b/pkg/cloudbroker/compute/stop_migration_in.go new file mode 100644 index 0000000..29bcdb1 --- /dev/null +++ b/pkg/cloudbroker/compute/stop_migration_in.go @@ -0,0 +1,61 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// OSUsers struct contains OS user data for Guest OS. +// Must be provided if NewVMUUID is provided. +type OSUser struct { + // Login of a user + // Required: true + Login string `url:"login" json:"login" validate:"required"` + + // Password of a user + // Required: true + Password string `url:"password" json:"password" validate:"required"` + + // GUID + // Required: false + GUID string `url:"guid,omitempty" json:"guid,omitempty"` + + // Pubkey + // Required: false + Pubkey string `url:"pubkey,omitempty" json:"pubkey,omitempty"` +} + +// StopMigrationINRequest struct to stop compute for external migration in +type StopMigrationINRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // If provided, indicates the UUID of the VM on the target host and that migration completed. + // Required: false + NewVMUUID string `url:"new_vm_uuid,omitempty" json:"new_vm_uuid,omitempty"` + + // OS user data for Guest OS + // Required: false + OSUsers []OSUser `url:"os_users,omitempty" json:"os_users,omitempty" validate:"omitempty,dive"` +} + +// StopMigrationIN stops compute for external migration in +func (c Compute) StopMigrationIN(ctx context.Context, req StopMigrationINRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/stop_migration_in" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/stop_migration_out.go b/pkg/cloudbroker/compute/stop_migration_out.go new file mode 100644 index 0000000..4e16a8e --- /dev/null +++ b/pkg/cloudbroker/compute/stop_migration_out.go @@ -0,0 +1,37 @@ +package compute + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// StopMigrationOutRequest struct to stop compute for external migration out +type StopMigrationOutRequest struct { + // ID of compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` + + // Identifier of the agent-side external migration (LibvirtMigrateToURI3Request) job + // Required: false + MigrateToURI3JobID string `url:"migrate_to_uri3_job_id,omitempty" json:"migrate_to_uri3_job_id,omitempty"` +} + +// StopMigrationOut stops compute for external migration out +func (c Compute) StopMigrationOut(ctx context.Context, req StopMigrationOutRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/stop_migration_out" + + res, err := c.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/compute/tag_add.go b/pkg/cloudbroker/compute/tag_add.go new file mode 100644 index 0000000..f896c51 --- /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/v15/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..c2c0f23 --- /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/v15/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_node.go b/pkg/cloudbroker/compute/unpin_from_node.go new file mode 100644 index 0000000..0f02806 --- /dev/null +++ b/pkg/cloudbroker/compute/unpin_from_node.go @@ -0,0 +1,38 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UnpinFromNodeRequest struct to unpin from node +type UnpinFromNodeRequest struct { + // ID of the compute instance + // Required: true + ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` +} + +// UnpinFromNode unpins compute from current node +func (c Compute) UnpinFromNode(ctx context.Context, req UnpinFromNodeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/compute/unpin_from_node" + + 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..1d64c8a --- /dev/null +++ b/pkg/cloudbroker/compute/update.go @@ -0,0 +1,103 @@ +package compute + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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, true or false + // Required: false + CPUPin interface{} `url:"cpupin,omitempty" json:"cpupin,omitempty" validate:"omitempty,isBool"` + + // 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, true or false + // Required: false + HPBacked interface{} `url:"hpBacked,omitempty" json:"hpBacked,omitempty" validate:"omitempty,isBool"` + + // Auto start when node restarted, true or false + // Required: false + AutoStart interface{} `url:"autoStart,omitempty" json:"autoStart,omitempty" validate:"omitempty,isBool"` + + // Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False + // Required: false + PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` + + // VM type linux, windows or unknown + // Required: false + LoaderType string `url:"loaderType,omitempty" json:"loaderType,omitempty" validate:"omitempty,loaderType"` + + // Boot type of image bios or uefi + // Required: false + BootType string `url:"bootType,omitempty" json:"bootType,omitempty" validate:"omitempty,imageBootType"` + + // Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming. + // Required: false + NetworkInterfaceNaming string `url:"networkInterfaceNaming,omitempty" json:"networkInterfaceNaming,omitempty" validate:"omitempty,networkInterfaceNaming"` + + // Does this machine supports hot resize, true or false + // Required: false + HotResize interface{} `url:"hotResize,omitempty" json:"hotResize,omitempty" validate:"omitempty,isBool"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,omitempty"` + + // Priority weight of the compute: higher value means higher priority and later migration + // Required: false + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` + + // Clock type for the VM + // Required: false + // Default: null + Clock string `url:"clock,omitempty" json:"clock,omitempty"` +} + +// 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..4afa965 --- /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/v15/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..4570f23 --- /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/v15/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..6d86bf0 --- /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/v15/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..130d234 --- /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/v15/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..ad4014b --- /dev/null +++ b/pkg/cloudbroker/disks.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/change_disk_storage_policy.go b/pkg/cloudbroker/disks/change_disk_storage_policy.go new file mode 100644 index 0000000..b231577 --- /dev/null +++ b/pkg/cloudbroker/disks/change_disk_storage_policy.go @@ -0,0 +1,42 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ChangeDiskStoragePolicyRequest struct to change storage policy for disk +type ChangeDiskStoragePolicyRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"disk_id" json:"disk_id" validate:"required"` + + // ID of the storage policy to which to connect account + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// ChangeDiskStoragePolicy changes storage policy for disk +func (d Disks) ChangeDiskStoragePolicy(ctx context.Context, req ChangeDiskStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/change_disk_storage_policy" + + 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/create.go b/pkg/cloudbroker/disks/create.go new file mode 100644 index 0000000..29bca57 --- /dev/null +++ b/pkg/cloudbroker/disks/create.go @@ -0,0 +1,70 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create disk +type CreateRequest struct { + // ID of the account + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Name of disk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // ID of the storage policy under the disk will be created + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // 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"` + + // Cache mode of disk + // Required: false + Cache string `url:"cache,omitempty" json:"cache,omitempty"` + + // Discard + // Required: false + Discard string `url:"discard,omitempty" json:"discard,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..f5edad4 --- /dev/null +++ b/pkg/cloudbroker/disks/delete.go @@ -0,0 +1,50 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Name of disk to delete + // Required: false + Name string `url:"name,omitempty" json:"name,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..32d2e1d --- /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/v15/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..511d595 --- /dev/null +++ b/pkg/cloudbroker/disks/depresent.go @@ -0,0 +1,41 @@ +package disks + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// DepresentRequest struct to depresent disk from node +type DepresentRequest struct { + // ID of the disk to depresent + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID of the node to depresent disk from + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// Depresent depresents disk from node +func (d Disks) Depresent(ctx context.Context, req DepresentRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/depresent" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/disks.go b/pkg/cloudbroker/disks/disks.go new file mode 100644 index 0000000..d7c3a50 --- /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/v15/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..817dc1a --- /dev/null +++ b/pkg/cloudbroker/disks/filter.go @@ -0,0 +1,143 @@ +package disks + +import ( + "context" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/k8s" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..eef62a1 --- /dev/null +++ b/pkg/cloudbroker/disks/filter_test.go @@ -0,0 +1,224 @@ +package disks + +import "testing" + +var disks = ListDisks{ + Data: []ItemDisk{ + { + + RecordDisk: RecordDisk{ + MachineID: 0, + MachineName: "", + 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", + VMID: 48500, + }, + }, + }, + { + + RecordDisk: RecordDisk{ + MachineID: 0, + MachineName: "", + 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", + 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..1826b95 --- /dev/null +++ b/pkg/cloudbroker/disks/from_platform_disk.go @@ -0,0 +1,111 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // 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"` + + // Pool for image create + // Required: false + PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"` + + // 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..f3a8515 --- /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/v15/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..3b13961 --- /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/v15/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..e55deb7 --- /dev/null +++ b/pkg/cloudbroker/disks/list.go @@ -0,0 +1,102 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Find by sep ID + // Required: false + SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"` + + // Find by storage policy id + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,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"` + + // ID of the resource group + // Required: false + RGID uint64 `url:"rg_id,omitempty" json:"rg_id,omitempty"` + + // ID of the compute + // Required: false + ComputeID uint64 `url:"compute_id,omitempty" json:"compute_id,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..858e5df --- /dev/null +++ b/pkg/cloudbroker/disks/list_deleted.go @@ -0,0 +1,72 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // 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_unattached.go b/pkg/cloudbroker/disks/list_unattached.go new file mode 100644 index 0000000..d49fd98 --- /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/v15/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"` + + // 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 storage policy id + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,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/migrate.go b/pkg/cloudbroker/disks/migrate.go new file mode 100644 index 0000000..ad3aeea --- /dev/null +++ b/pkg/cloudbroker/disks/migrate.go @@ -0,0 +1,46 @@ +package disks + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// Migrate struct to move disk to another sep, pool and storage policy +type MigrateRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"disk_id" json:"disk_id" validate:"required"` + + // ID of the new SEP + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // New pool name + // Required: true + PoolName string `url:"pool_name" json:"pool_name" validate:"required"` + + // ID of the storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// Move moves disk to another sep, pool and storage policy +func (c Disks) Migrate(ctx context.Context, req MigrateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/migrate" + + res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + result := string(res) + + return result, nil +} diff --git a/pkg/cloudbroker/disks/migrate_abort.go b/pkg/cloudbroker/disks/migrate_abort.go new file mode 100644 index 0000000..af30e44 --- /dev/null +++ b/pkg/cloudbroker/disks/migrate_abort.go @@ -0,0 +1,38 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateAbortRequest struct to abort migration +type MigrateAbortRequest struct { + // ID of the disk + // Required: true + DiskID uint64 `url:"disk_id" json:"disk_id" validate:"required"` +} + +// MigrateAbort aborts disk migration +func (c Disks) MigrateAbort(ctx context.Context, req MigrateAbortRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/migrate_abort" + + 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/migrate_status.go b/pkg/cloudbroker/disks/migrate_status.go new file mode 100644 index 0000000..c17bc5b --- /dev/null +++ b/pkg/cloudbroker/disks/migrate_status.go @@ -0,0 +1,40 @@ +package disks + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetMigrateStatusRequest struct to get information about disk migrate status +type GetMigrateStatusRequest struct { + // ID of disk + // Required: true + DiskID uint64 `url:"disk_id" json:"disk_id" validate:"required"` +} + +// GetMigrateStatus gets information about disk migrate status +func (c Disks) GetMigrateStatus(ctx context.Context, req GetMigrateStatusRequest) (*MigrateStatus, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/migrate_status" + + res, err := c.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return nil, err + } + + status := MigrateStatus{} + + err = json.Unmarshal(res, &status) + if err != nil { + return nil, err + } + + return &status, nil +} diff --git a/pkg/cloudbroker/disks/models.go b/pkg/cloudbroker/disks/models.go new file mode 100644 index 0000000..1a4c973 --- /dev/null +++ b/pkg/cloudbroker/disks/models.go @@ -0,0 +1,346 @@ +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"` + + // Discard + Discard string `json:"discard"` + + // Block size of disk + BlockSize string `json:"block_size"` + + // Boot partition + BootPartition uint64 `json:"bootPartition"` + + // Computes + Computes map[string]string `json:"computes"` + + // Computes read only + ComputesReadOnly map[string]bool `json:"computes_read_only"` + + // 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 map[string]uint64 `json:"presentTo"` + + // Provision + Provision string `json:"provision"` + + // 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 available + SizeAvailable float64 `json:"sizeAvailable"` + + // Size max + SizeMax int64 `json:"sizeMax"` + + // Size used + SizeUsed float64 `json:"sizeUsed"` + + // List snapshots + Snapshots ListSnapshots `json:"snapshots"` + + // Status + Status string `json:"status"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // Virtual machine ID + VMID uint64 `json:"vmid"` + + // Updated by + UpdatedBy string `json:"updatedBy"` + + // Cache mode of disk + Cache string `json:"cache"` +} + +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 { + + // Block size of disk + BlockSize string `json:"block_size"` + + //Created by + CreatedBy string `json:"createdBy"` + + //Deleted by + DeletedBy string `json:"deletedBy"` + + // Device name + DeviceName string `json:"devicename"` + + // SEP type + SEPType string `json:"sepType"` + + // Machine ID + MachineID uint64 `json:"machineId"` + + // Machine name + MachineName string `json:"machineName"` + + // Main information about disk + InfoDisk + + // Independent + Independent bool `json:"independent"` + + //Updated by + UpdatedBy string `json:"updatedBy"` + + // Update time + UpdatedTime uint64 `json:"updatedTime"` +} + +// Main information for list disks +type ItemDisk struct { + + // 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 { + // 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 MigrateStatus struct { + // Type + Type int `json:"type"` + + // Copy speed + Bandwidth int `json:"bandwidth"` + + // Current progress + Cur interface{} `json:"cur"` + + // Total size + End interface{} `json:"end"` + + // Progress percent + ProgressPercent int `json:"progress_percent"` +} diff --git a/pkg/cloudbroker/disks/present.go b/pkg/cloudbroker/disks/present.go new file mode 100644 index 0000000..fe2149e --- /dev/null +++ b/pkg/cloudbroker/disks/present.go @@ -0,0 +1,41 @@ +package disks + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// PresentRequest struct to present disk to node +type PresentRequest struct { + // ID of the disk to present + // Required: true + DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` + + // ID of the node to present disk to + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` +} + +// Present presents disk to node +func (d Disks) Present(ctx context.Context, req PresentRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/present" + + res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return false, err + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/cloudbroker/disks/rename.go b/pkg/cloudbroker/disks/rename.go new file mode 100644 index 0000000..4685192 --- /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/v15/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..ff4182a --- /dev/null +++ b/pkg/cloudbroker/disks/replicate.go @@ -0,0 +1,56 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // ID of the storage policy under the disk will be created + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// Replicate 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..b321284 --- /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/v15/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..7217ca1 --- /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/v15/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..2bd1b7d --- /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/v15/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..e403722 --- /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/v15/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..ce80a4b --- /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/v15/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..0af9e72 --- /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/v15/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..7f745ae --- /dev/null +++ b/pkg/cloudbroker/disks/resize.go @@ -0,0 +1,45 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +// 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..10f1e5e --- /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/v15/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..3b5b569 --- /dev/null +++ b/pkg/cloudbroker/disks/serialize.go @@ -0,0 +1,43 @@ +package disks + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..39eedb3 --- /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/v15/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..d41e263 --- /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/v15/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..4f6ef73 --- /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/v15/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..63f918c --- /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/v15/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/disks/update.go b/pkg/cloudbroker/disks/update.go new file mode 100644 index 0000000..996a2b2 --- /dev/null +++ b/pkg/cloudbroker/disks/update.go @@ -0,0 +1,50 @@ +package disks + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update disk +type UpdateRequest struct { + // ID of the disk to update + // Required: true + DiskID uint64 `url:"disk_id" json:"disk_id" validate:"required"` + + // Cache mode of disk + // Required: false + Cache string `url:"cache,omitempty" json:"cache,omitempty"` + + // Discard + // Required: false + Discard string `url:"discard,omitempty" json:"discard,omitempty"` + + // Block size of disk + // Required: false + BlockSize string `url:"block_size,omitempty" json:"block_size,omitempty"` +} + +// Update updates disk +func (d Disks) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/disks/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/dpdk.go b/pkg/cloudbroker/dpdk.go new file mode 100644 index 0000000..e5f5682 --- /dev/null +++ b/pkg/cloudbroker/dpdk.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..666fb8c --- /dev/null +++ b/pkg/cloudbroker/dpdknet/create.go @@ -0,0 +1,67 @@ +package dpdknet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Enable security groups for DPDK network + // Required: false + // Default: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +// 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..b63fa20 --- /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/v15/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..b9d5633 --- /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/v15/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..84a4c3d --- /dev/null +++ b/pkg/cloudbroker/dpdknet/dpdk.go @@ -0,0 +1,15 @@ +package dpdknet + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..d5fccbc --- /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/v15/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..ba7e17b --- /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/v15/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..186730f --- /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/v15/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..ebcca93 --- /dev/null +++ b/pkg/cloudbroker/dpdknet/models.go @@ -0,0 +1,98 @@ +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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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..a44aebb --- /dev/null +++ b/pkg/cloudbroker/dpdknet/update.go @@ -0,0 +1,66 @@ +package dpdknet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Flag indicating whether security groups are enabled for this network + // Required: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +// 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..a271d79 --- /dev/null +++ b/pkg/cloudbroker/extnet.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..653e670 --- /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/v15/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..c3445ee --- /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/v15/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/add_reserved_ip.go b/pkg/cloudbroker/extnet/add_reserved_ip.go new file mode 100644 index 0000000..fe1d3b3 --- /dev/null +++ b/pkg/cloudbroker/extnet/add_reserved_ip.go @@ -0,0 +1,50 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AddReserveIPRequest struct to reserved address or address poll +type AddReserveIPRequest struct { + // AccountID to which a reserved address or address pool is added + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // ExtNetID from which the address or address pool is reserved + // Required: true + ExtNetID uint64 `url:"extnetId" json:"extnetId" validate:"required"` + + // Field for specifying the number of reserved addresses + // Required: false + IPCount uint64 `url:"ipCount,omitempty" json:"ipCount,omitempty"` + + // List of IPs for specifying the desired address + // Required: false + IPs []string `url:"ips,omitempty" json:"ips,omitempty"` +} + +// AddReserveIP reserves address or address poll to external network for account ID +func (e ExtNet) AddReserveIP(ctx context.Context, req AddReserveIPRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/addReservedIp" + + 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/create.go b/pkg/cloudbroker/extnet/create.go new file mode 100644 index 0000000..e830233 --- /dev/null +++ b/pkg/cloudbroker/extnet/create.go @@ -0,0 +1,161 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Maximum transmission unit + // Default: 1500 + // Required: false + MTU uint `url:"mtu,omitempty" json:"mtu,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 primary vnfdev with + // Required: false + PriVNFDevIP string `url:"priVnfdevIP,omitempty" json:"priVnfdevIP,omitempty"` + + // IP to create secondary vnfdev with + // Required: false + SecVNFDevIP string `url:"secVnfdevIP,omitempty" json:"secVnfdevIP,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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // High Availability mode is enabled, default False + // Required: false + // Default: false + HAMode bool `url:"highlyAvailable,omitempty" json:"highlyAvailable,omitempty"` + + // Enable security groups for external network + // Required: false + // Default: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +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..aca2942 --- /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/v15/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/del_reserved_ip.go b/pkg/cloudbroker/extnet/del_reserved_ip.go new file mode 100644 index 0000000..82009a2 --- /dev/null +++ b/pkg/cloudbroker/extnet/del_reserved_ip.go @@ -0,0 +1,50 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DelReserveIPRequest struct to delete reserved address or address poll +type DelReserveIPRequest struct { + // AccountID to which a reserved address or address pool is added + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // ExtNetID from which the address or address pool is reserved + // Required: true + ExtNetID uint64 `url:"extnetId" json:"extnetId" validate:"required"` + + // Field for specifying the number of reserved addresses + // Required: false + IPCount uint64 `url:"ipCount,omitempty" json:"ipCount,omitempty"` + + // List of IPs for specifying the desired address + // Required: false + IPs []string `url:"ips,omitempty" json:"ips,omitempty"` +} + +// DelReserveIP deletes reserved address or address poll to external network for account ID +func (e ExtNet) DelReserveIP(ctx context.Context, req DelReserveIPRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/delReservedIp" + + 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..8d4bb89 --- /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/v15/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..348048f --- /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/v15/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..2e93cba --- /dev/null +++ b/pkg/cloudbroker/extnet/device_migrate.go @@ -0,0 +1,47 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 node ID to migrate to + // Required: false + NodeID uint64 `url:"node_id,omitempty" json:"node_id,omitempty"` + + // Target device to migrate + // Required: false + // Default: primary + Device string `url:"device,omitempty" json:"device,omitempty" validate:"omitempty,device"` +} + +// 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..60d1741 --- /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/v15/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..886490d --- /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/v15/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..5e1ee87 --- /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/v15/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..736d5e8 --- /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/v15/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..9062cc5 --- /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/v15/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..bdf6a73 --- /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/v15/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..4dcb212 --- /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", + NetworkIDs: NetworkIDs{Primary: 10, Secondary: 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", + NetworkIDs: NetworkIDs{Primary: 10, Secondary: 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", + NetworkIDs: NetworkIDs{Primary: 10, Secondary: 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..e68395e --- /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/v15/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/get_reserved_ip.go b/pkg/cloudbroker/extnet/get_reserved_ip.go new file mode 100644 index 0000000..d0f5cc7 --- /dev/null +++ b/pkg/cloudbroker/extnet/get_reserved_ip.go @@ -0,0 +1,50 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about reserved address or address poll +type GetReservedIP struct { + // AccountID of the account whose reservation information we want to receive + // Required: true + AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` + + // Field for specifying the ID of extnet whose reservation information we want to receive + // Required: false + ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"` +} + +// GetReservedIP gets information about reserved address or address poll as a slice of RecordReservedIP struct +func (e ExtNet) GetReservedIP(ctx context.Context, req GetReservedIP) ([]RecordReservedIP, error) { + res, err := e.GetReservedIPRaw(ctx, req) + if err != nil { + return nil, err + } + + reservedIP := make([]RecordReservedIP, 0) + + err = json.Unmarshal(res, &reservedIP) + if err != nil { + return nil, err + } + + return reservedIP, nil +} + +// GetRaw gets detailed information about external network as an array of bytes +func (e ExtNet) GetReservedIPRaw(ctx context.Context, req GetReservedIP) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/getReservedIp" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} 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..02863a9 --- /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/v15/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..f271b76 --- /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/v15/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..d46d28a --- /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/v15/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..c489db3 --- /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/v15/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..5075ccc --- /dev/null +++ b/pkg/cloudbroker/extnet/list.go @@ -0,0 +1,88 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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/migrate_to_zone.go b/pkg/cloudbroker/extnet/migrate_to_zone.go new file mode 100644 index 0000000..91810f4 --- /dev/null +++ b/pkg/cloudbroker/extnet/migrate_to_zone.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move extnet to another zone +type MigrateToZoneRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zone_id" json:"zone_id" validate:"required"` +} + +// MoveToZone moves extnet to new zone +func (e ExtNet) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/migrateToZone" + + 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/models.go b/pkg/cloudbroker/extnet/models.go new file mode 100644 index 0000000..a84c7f6 --- /dev/null +++ b/pkg/cloudbroker/extnet/models.go @@ -0,0 +1,304 @@ +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 { + // Account ID + AccountID uint64 `json:"account_id"` + + // 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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // MTU + MTU uint64 `json:"mtu"` + + // Name + Name string `json:"name"` + + // Network IDs + NetworkIDs NetworkIDs `json:"networkIds"` + + // OVSBridge + OVSBridge string `json:"ovsBridge"` + + // PreReservationsNum + PreReservationsNum uint64 `json:"preReservationsNum"` + + // PriVNFDevID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // Redundant + Redundant bool `json:"redundant"` + + // SecVnfDevId + SecVNFDevID uint64 `json:"secVnfDevId"` + + // List of shared with + SharedWith []interface{} `json:"sharedWith"` + + // Status + Status string `json:"status"` + + // VLAN ID + VLANID uint64 `json:"vlanId"` + + // VNFs + VNFs VNFs `json:"vnfs"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // MTU + MTU uint64 `json:"mtu"` + + // Name + Name string `json:"name"` + + // Network + Network string `json:"network"` + + // Network IDs + NetworkIDs NetworkIDs `json:"networkIds"` + + // NTP + NTP []string `json:"ntp"` + + // OVSBridge + OVSBridge string `json:"ovsBridge"` + + // PreReservationsNum + PreReservationsNum uint64 `json:"preReservationsNum"` + + // Prefix + Prefix uint64 `json:"prefix"` + + // PriVNFDevID + PriVNFDevID uint64 `json:"priVnfDevId"` + + // Redundant + Redundant bool `json:"redundant"` + + // SecVnfDevId + SecVNFDevID uint64 `json:"secVnfDevId"` + + // List reservations + Reservations ListReservations `json:"reservations"` + + // List pre-reservations + PreReservations ListReservations `json:"pre-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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +type NetworkIDs struct { + // Primary + Primary uint64 `json:"primary"` + + // Secondary + Secondary uint64 `json:"secondary"` +} + +// 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"` +} + +// Detailed information about reserved address or address pool +type RecordReservedIP struct { + ExtnetID int `json:"extnet_id"` + Reservations []Reservations `json:"reservations"` +} + +type Reservations struct { + AccountID int `json:"account_id"` + ClientType string `json:"clientType"` + DomainName string `json:"domainname"` + Hostname string `json:"hostname"` + IP string `json:"ip"` + Mac string `json:"mac"` + Type string `json:"type"` + VMID int `json:"vmId"` +} diff --git a/pkg/cloudbroker/extnet/ntp_apply.go b/pkg/cloudbroker/extnet/ntp_apply.go new file mode 100644 index 0000000..d2d5f82 --- /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/v15/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..764caa5 --- /dev/null +++ b/pkg/cloudbroker/extnet/serialize.go @@ -0,0 +1,43 @@ +package extnet + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..6b9cda0 --- /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/v15/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/set_highly_available.go b/pkg/cloudbroker/extnet/set_highly_available.go new file mode 100644 index 0000000..2f48def --- /dev/null +++ b/pkg/cloudbroker/extnet/set_highly_available.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// SetHAModeRequest struct to set HA mode for external network +type SetHAModeRequest struct { + // ID of external network + // Required: true + NetID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // HA Mode + // Required: true + HAMode bool `url:"highly_available" json:"highly_available" validate:"required"` +} + +// SetHAMode set HA mode for external network +func (e ExtNet) SetHAMode(ctx context.Context, req SetHAModeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/extnet/set_highly_available" + + 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..145979d --- /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/v15/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..5b367d5 --- /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/v15/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..c59ea40 --- /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/v15/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..c432600 --- /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/v15/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..41764db --- /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/v15/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..ec9d10f --- /dev/null +++ b/pkg/cloudbroker/extnet/update.go @@ -0,0 +1,55 @@ +package extnet + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Maximum transmission unit + // Default: 1500 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty"` + + // Flag indicating whether security groups are enabled for this network + // Required: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +// 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..e5aba73 --- /dev/null +++ b/pkg/cloudbroker/flipgoup.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..89aa3e4 --- /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/v15/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..d8b7168 --- /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/v15/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..1e7cc9e --- /dev/null +++ b/pkg/cloudbroker/flipgroup/create.go @@ -0,0 +1,70 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + // Default: "compute" + ClientType string `url:"clientType,omitempty" json:"clientType,omitempty"` + + // 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..88f847c --- /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/v15/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..d65886b --- /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/v15/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..a5d8141 --- /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/v15/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..b831436 --- /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/v15/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..067475d --- /dev/null +++ b/pkg/cloudbroker/flipgroup/list.go @@ -0,0 +1,99 @@ +package flipgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 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..c7f130d --- /dev/null +++ b/pkg/cloudbroker/flipgroup/models.go @@ -0,0 +1,163 @@ +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"` + + // 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..9f322bd --- /dev/null +++ b/pkg/cloudbroker/flipgroup/serialize.go @@ -0,0 +1,43 @@ +package flipgroup + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..b63b38b --- /dev/null +++ b/pkg/cloudbroker/grid.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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_custom_backup_path.go b/pkg/cloudbroker/grid/add_custom_backup_path.go new file mode 100644 index 0000000..1c23aa0 --- /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/v15/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..a294f9e --- /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/v15/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..3a2e17c --- /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/v15/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..f9f9007 --- /dev/null +++ b/pkg/cloudbroker/grid/filter_test.go @@ -0,0 +1,136 @@ +package grid + +import "testing" + +var grids = ListGrids{ + Data: []ItemGridList{ + { + Resources: Resources{ + Current: RecordResource{ + CPU: 84, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + GPU: 79500, + RAM: 0, + SEPs: map[string]map[string]DiskUsage{}, + }, + Reserved: RecordResource{ + CPU: 123, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + 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, + GPU: 79500, + RAM: 0, + SEPs: map[string]map[string]DiskUsage{}, + }, + Reserved: RecordResource{ + CPU: 123, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + 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, + GPU: 79500, + RAM: 0, + SEPs: map[string]map[string]DiskUsage{}, + }, + Reserved: RecordResource{ + CPU: 123, + DiskSize: 976, + DiskSizeMax: 1200, + ExtIPs: 132, + 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..6013ec3 --- /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/v15/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..aac7dd5 --- /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/v15/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..267ba57 --- /dev/null +++ b/pkg/cloudbroker/grid/get_diagnosis.go @@ -0,0 +1,49 @@ +package grid + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 := "/cloudbroker/grid/getDiagnosis" + + 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..8a4094c --- /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/v15/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..1c7e3dc --- /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/v15/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..5b40e19 --- /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/v15/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..d3b7fbc --- /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/v15/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..2c8642d --- /dev/null +++ b/pkg/cloudbroker/grid/models.go @@ -0,0 +1,287 @@ +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"` + + // Number of GPU + GPU uint64 `json:"gpu"` + + // Policies + Policies map[string]PolicyUsage `json:"policies"` + + // 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"` +} + +// Policy usage details +type PolicyUsage struct { + // Disk usage + DiskUsage + + // SEPs + SEPs map[string]map[string]DiskUsage `json:"seps"` +} + +// Detailed information about grid +type RecordGrid struct { + // 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"` + + // Network modes + NetworkModes []string `json:"network_modes"` + + // SDN support + SDNSupport bool `json:"sdn_support"` + + // Is Zero Access enabled + ZeroAccessEnabled bool `json:"zeroaccess_enabled"` + + // Is BRO enabled + BROEnabled bool `json:"bro_enabled"` +} + +// 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"` + + // Network modes + NetworkModes []string `json:"network_modes"` + + // SDN support + SDNSupport bool `json:"sdn_support"` + + // Is Zero Access enabled + ZeroAccessEnabled bool `json:"zeroaccess_enabled"` + + // Is BRO enabled + BROEnabled bool `json:"bro_enabled"` +} + +// 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"` + + // CPU allocation ratio + CPUAllocationRatio uint64 `json:"cpu_allocation_ratio"` + + // CPU allocation ratio for VMs + CPUAllocationRatioVM uint64 `json:"cpu_allocation_ratio_vm"` + + // Custom backup path + CustomBackupPath []string `json:"custom_backup_path"` + + //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"` + + // Interface generation scheme + InterfaceGenerationScheme string `json:"interface_generation_scheme"` + + //k8s cleanup enabled + K8sCleanupEnabled bool `json:"k8s_cleanup_enabled"` + + //Limits + Limits interface{} `json:"limits"` + + //Location url + LocationURL string `json:"location_url"` + + // MAC address prefix + MACAddressPrefix string `json:"mac_address_prefix"` + + //Net QOS + NetQOS NetQOS `json:"net_qos"` + + //Networks + Networks string `json:"networks"` + + // Node self stop timer uptime monitor + NodeSelfStopTimerUptimeMonitor uint64 `json:"nodeSelfStopTimerUptimeMonitor"` + + // Node self stop uptime monitor + NodeSelfStopUptimeMonitor bool `json:"nodeSelfStopUptimeMonitor"` + + //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..9824ea9 --- /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/v15/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: false + Age string `url:"age,omitempty" json:"age,omitempty"` +} + +// 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..abc63b3 --- /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/v15/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..c5a2413 --- /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/v15/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..d43c7dc --- /dev/null +++ b/pkg/cloudbroker/grid/serialize.go @@ -0,0 +1,43 @@ +package grid + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..03d46ed --- /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/v15/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..d035608 --- /dev/null +++ b/pkg/cloudbroker/grid/set_cpu_allocation_parameter.go @@ -0,0 +1,43 @@ +package grid + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..97cf75b --- /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/v15/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 uint64 `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..9196f22 --- /dev/null +++ b/pkg/cloudbroker/grid/set_cpu_allocation_ratio_for_vm.go @@ -0,0 +1,41 @@ +package grid + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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 uint64 `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..744cd03 --- /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/v15/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 uint64 `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..929b9a7 --- /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/v15/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..0c94473 --- /dev/null +++ b/pkg/cloudbroker/group.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..167ddfc --- /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/v15/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..f2e91d6 --- /dev/null +++ b/pkg/cloudbroker/group/group.go @@ -0,0 +1,15 @@ +package group + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..9ca5429 --- /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/v15/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..e65531f --- /dev/null +++ b/pkg/cloudbroker/image.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/change_storage_policy.go b/pkg/cloudbroker/image/change_storage_policy.go new file mode 100644 index 0000000..eb585e3 --- /dev/null +++ b/pkg/cloudbroker/image/change_storage_policy.go @@ -0,0 +1,41 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type ChangeStoragePolicyRequest struct { + // ID of the image to change the storage policy + // Required: true + ImageID uint64 `url:"image_id" json:"image_id" validate:"required"` + + // ID of the storage policy to move the image to + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// ChangeStoragePolicy changes the storage policy of the image chosen +func (i Image) ChangeStoragePolicy(ctx context.Context, req ChangeStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/change_storage_policy" + + 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_set.go b/pkg/cloudbroker/image/computeci_set.go new file mode 100644 index 0000000..bd968a8 --- /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/v15/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..eb2d648 --- /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/v15/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..cd7a099 --- /dev/null +++ b/pkg/cloudbroker/image/create_cdrom_image.go @@ -0,0 +1,100 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // ID of the chosen storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` +} + +type asyncWrapperCreateCDROMImageRequest struct { + CreateCDROMImageRequest + AsyncMode bool `url:"asyncMode"` +} + +// 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" + + syncReq := asyncWrapperCreateCDROMImageRequest{CreateCDROMImageRequest: req, AsyncMode: false} + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, syncReq) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} + +// AsyncCreateCDROMImage creates CD-ROM image from an ISO identified by URL in async mode +func (i Image) AsyncCreateCDROMImage(ctx context.Context, req CreateCDROMImageRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/createCDROMImage" + + asyncReq := asyncWrapperCreateCDROMImageRequest{CreateCDROMImageRequest: req, AsyncMode: true} + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, asyncReq) + if err != nil { + return " ", err + } + + var taskID string + + err = json.Unmarshal(res, &taskID) + if err != nil { + return "", err + } + + return taskID, nil +} diff --git a/pkg/cloudbroker/image/create_image.go b/pkg/cloudbroker/image/create_image.go new file mode 100644 index 0000000..7c60599 --- /dev/null +++ b/pkg/cloudbroker/image/create_image.go @@ -0,0 +1,138 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create image +type CreateRequest struct { + // Name of the image + // 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 + // 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"` + + // ID of the chosen storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // 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"` + + // Bootable image or not + // Required: false + Bootable bool `url:"bootable,omitempty" json:"bootable,omitempty"` +} + +type asyncWrapperCreateRequest struct { + CreateRequest + AsyncMode bool `url:"asyncMode"` +} + +// 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" + + syncReq := asyncWrapperCreateRequest{CreateRequest: req, AsyncMode: false} + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, syncReq) + if err != nil { + return 0, err + } + + result, err := strconv.ParseUint(string(res), 10, 64) + if err != nil { + return 0, err + } + + return result, nil +} + +// AsyncCreate creates image from a media identified by URL in async mode +func (i Image) AsyncCreateImage(ctx context.Context, req CreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/createImage" + + asyncReq := asyncWrapperCreateRequest{CreateRequest: req, AsyncMode: true} + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, asyncReq) + if err != nil { + return " ", err + } + + var taskID string + + err = json.Unmarshal(res, &taskID) + if err != nil { + return "", err + } + + return taskID, nil +} diff --git a/pkg/cloudbroker/image/create_multi_image.go b/pkg/cloudbroker/image/create_multi_image.go new file mode 100644 index 0000000..9ca92af --- /dev/null +++ b/pkg/cloudbroker/image/create_multi_image.go @@ -0,0 +1,47 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateMultiImageRequest struct to create multi image +type CreateMultiImageRequest struct { + // Name of the multi image + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // IDs of real images + // Required: true + TargetIDs []uint64 `url:"target_ids" json:"target_ids" validate:"required"` + + // Account ID + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` +} + +// CreateMultiImage creates multi image +func (i Image) CreateMultiImage(ctx context.Context, req CreateMultiImageRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/create_multi_image" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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..62b94c0 --- /dev/null +++ b/pkg/cloudbroker/image/create_virtual.go @@ -0,0 +1,47 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // AccountID to make the virtual image exclusive + // Required: false + // Default: 0 + AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` +} + +// 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..ed79ef6 --- /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/v15/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..f3d0455 --- /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/v15/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..cd569ee --- /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/v15/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..aff8625 --- /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/v15/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..359637a --- /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/v15/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 interface{} `url:"hotresize,omitempty" json:"hotresize,omitempty" validate:"omitempty,isBool"` + + // Does this image boot OS + // Required: false + Bootable interface{} `url:"bootable,omitempty" json:"bootable,omitempty" validate:"omitempty,isBool"` +} + +// 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..28f170e --- /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/v15/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..d02bcbc --- /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: map[string]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: map[string]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: map[string]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..056fda9 --- /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/v15/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..783175c --- /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/v15/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 []int64 `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..0d2b91d --- /dev/null +++ b/pkg/cloudbroker/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 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..6de0a48 --- /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/v15/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..107764c --- /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/v15/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..4aeb13c --- /dev/null +++ b/pkg/cloudbroker/image/list.go @@ -0,0 +1,108 @@ +package image + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 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"` + + // Find by storage policy id + // Required: false + StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,omitempty"` +} + +// 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.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + return res, err +} diff --git a/pkg/cloudbroker/image/models.go b/pkg/cloudbroker/image/models.go new file mode 100644 index 0000000..e2a1109 --- /dev/null +++ b/pkg/cloudbroker/image/models.go @@ -0,0 +1,428 @@ +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"` + + // Independent + Independent bool `json:"independent"` + + // Last modified + LastModified uint64 `json:"lastModified"` + + // Link to + LinkTo uint64 `json:"linkTo"` + + // Links to + LinksTo []uint64 `json:"linksTo"` + + // 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 map[string]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"` + + // SnapshotId + SnapshotID string `json:"snapshotId"` + + // Status + Status string `json:"status"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // 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"` + + // Independent + Independent bool `json:"independent"` + + // Last modified + LastModified uint64 `json:"lastModified"` + + // Link to + LinkTo uint64 `json:"linkTo"` + + // Links to + LinksTo []uint64 `json:"linksTo"` + + // 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 map[string]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"` + + // SnapshotId + SnapshotID string `json:"snapshotId"` + + // Status + Status string `json:"status"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Tech status + TechStatus string `json:"techStatus"` + + // Need to clean before destroy + ToClean bool `json:"to_clean"` + + // 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 + +// 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/multi_image_add_links.go b/pkg/cloudbroker/image/multi_image_add_links.go new file mode 100644 index 0000000..f12a967 --- /dev/null +++ b/pkg/cloudbroker/image/multi_image_add_links.go @@ -0,0 +1,43 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MultiImageAddLinksRequest struct to add links to multi image +type MultiImageAddLinksRequest struct { + // ID of the multi image + // Required: true + ImageID uint64 `url:"image_id" json:"image_id" validate:"required"` + + // IDs of real images + // Required: true + TargetIDs []uint64 `url:"target_ids" json:"target_ids" validate:"required"` +} + +// MultiImageAddLinks adds image links to multi image +func (i Image) MultiImageAddLinks(ctx context.Context, req MultiImageAddLinksRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/multi_image_add_links" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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/multi_image_del_links.go b/pkg/cloudbroker/image/multi_image_del_links.go new file mode 100644 index 0000000..9aef980 --- /dev/null +++ b/pkg/cloudbroker/image/multi_image_del_links.go @@ -0,0 +1,43 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MultiImageDelLinksRequest struct to delete links from multi image +type MultiImageDelLinksRequest struct { + // ID of the multi image + // Required: true + ImageID uint64 `url:"image_id" json:"image_id" validate:"required"` + + // IDs of real images + // Required: true + TargetIDs []uint64 `url:"target_ids" json:"target_ids" validate:"required"` +} + +// MultiImageDelLinks removes image links from multi image +func (i Image) MultiImageDelLinks(ctx context.Context, req MultiImageDelLinksRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/multi_image_del_links" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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/multi_image_export.go b/pkg/cloudbroker/image/multi_image_export.go new file mode 100644 index 0000000..2af7ec6 --- /dev/null +++ b/pkg/cloudbroker/image/multi_image_export.go @@ -0,0 +1,46 @@ +package image + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MultiImageExportRequest struct to export multi image to a pool +type MultiImageExportRequest struct { + // ID of the multi image + // Required: true + MultiImageID uint64 `url:"multi_image_id" json:"multi_image_id" validate:"required"` + + // Name of the target pool + // Required: true + PoolName string `url:"pool_name" json:"pool_name" validate:"required"` + + // Target SEP ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` +} + +// MultiImageExport copies a physical image from multi image to the specified pool +func (i Image) MultiImageExport(ctx context.Context, req MultiImageExportRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/image/multi_image_export" + + 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/rename.go b/pkg/cloudbroker/image/rename.go new file mode 100644 index 0000000..a02e9d4 --- /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/v15/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..0f79dd6 --- /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/v15/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 []int64 `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..dc4710a --- /dev/null +++ b/pkg/cloudbroker/image/serialize.go @@ -0,0 +1,43 @@ +package image + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..5eb49ae --- /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/v15/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/k8ci.go b/pkg/cloudbroker/k8ci.go new file mode 100644 index 0000000..86b1e1b --- /dev/null +++ b/pkg/cloudbroker/k8ci.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..85704db --- /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/v15/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..bf3782a --- /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/v15/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..2872001 --- /dev/null +++ b/pkg/cloudbroker/k8ci/create.go @@ -0,0 +1,72 @@ +package k8ci + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Image ID for worker K8S node + // Required: true + WorkerImageID uint64 `url:"workerImageId" json:"workerImageId" validate:"required"` + + // 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..ea4cc45 --- /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/v15/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..f0f197d --- /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/v15/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..1af5e2d --- /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/v15/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..5df0376 --- /dev/null +++ b/pkg/cloudbroker/k8ci/filter_test.go @@ -0,0 +1,141 @@ +package k8ci + +import "testing" + +var k8ciItems = ListK8CI{ + Data: []ItemK8CI{ + { + CreatedTime: 123902139, + RecordK8CIList: RecordK8CIList{ + Description: "", + GID: 0, + GUID: 1, + ID: 1, + LBImageID: 5, + MasterImageID: 120, + MaxMasterCount: 2, + MaxWorkerCount: 3, + Name: "purple_snake", + SharedWith: []uint64{}, + Status: "ENABLED", + Version: "1", + WorkerImageID: 120, + }, + }, + { + CreatedTime: 123902232, + RecordK8CIList: RecordK8CIList{ + Description: "", + GID: 0, + GUID: 2, + ID: 2, + LBImageID: 10, + MasterImageID: 121, + MaxMasterCount: 3, + MaxWorkerCount: 5, + Name: "green_giant", + SharedWith: []uint64{}, + Status: "DISABLED", + Version: "2", + WorkerImageID: 121, + }, + }, + { + CreatedTime: 123902335, + RecordK8CIList: RecordK8CIList{ + Description: "", + GID: 0, + GUID: 3, + ID: 3, + LBImageID: 12, + MasterImageID: 98, + MaxMasterCount: 5, + MaxWorkerCount: 9, + Name: "magenta_cloud", + SharedWith: []uint64{}, + Status: "ENABLED", + Version: "3", + 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..108c91a --- /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/v15/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..81504d1 --- /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/v15/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..53b36a8 --- /dev/null +++ b/pkg/cloudbroker/k8ci/list.go @@ -0,0 +1,76 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 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..a8c79be --- /dev/null +++ b/pkg/cloudbroker/k8ci/list_deleted.go @@ -0,0 +1,61 @@ +package k8ci + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 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..cdeb03b --- /dev/null +++ b/pkg/cloudbroker/k8ci/models.go @@ -0,0 +1,114 @@ +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 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 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 image ID + MasterImageID uint64 `json:"masterImageId"` + + // Master driver + MasterDriver string `json:"masterDriver"` + + // 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 image ID + WorkerImageID uint64 `json:"workerImageId"` + + // Worker driver + WorkerDriver string `json:"workerDriver"` +} diff --git a/pkg/cloudbroker/k8ci/restore.go b/pkg/cloudbroker/k8ci/restore.go new file mode 100644 index 0000000..f14c105 --- /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/v15/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..be1196a --- /dev/null +++ b/pkg/cloudbroker/k8ci/serialize.go @@ -0,0 +1,43 @@ +package k8ci + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..6153510 --- /dev/null +++ b/pkg/cloudbroker/k8s.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..e15f26d --- /dev/null +++ b/pkg/cloudbroker/k8s/create.go @@ -0,0 +1,211 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 the chosen storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // 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 + // Default: Q35 + Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"omitempty,chipset"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` +} + +// 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..c5a635c --- /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/v15/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..8ab0ffd --- /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/v15/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..f094932 --- /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/v15/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..61db8b9 --- /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/v15/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..ec95a82 --- /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/v15/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..a187114 --- /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/v15/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..1b66fd6 --- /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/v15/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..70f35aa --- /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/v15/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..85c34e8 --- /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/v15/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..10cfc17 --- /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/v15/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..6b403c2 --- /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/v15/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..10fae0f --- /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/v15/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..d07d885 --- /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/v15/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..8747767 --- /dev/null +++ b/pkg/cloudbroker/k8s/list.go @@ -0,0 +1,97 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..07dfa70 --- /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/v15/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/migrate_to_zone.go b/pkg/cloudbroker/k8s/migrate_to_zone.go new file mode 100644 index 0000000..5d96d97 --- /dev/null +++ b/pkg/cloudbroker/k8s/migrate_to_zone.go @@ -0,0 +1,42 @@ +package k8s + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move k8s cluster to another zone +type MigrateToZoneRequest struct { + // Kubernetes cluster ID to move + // Required: true + K8SID uint64 `url:"k8sId" json:"k8sId" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zoneId" json:"zoneId" validate:"required"` +} + +// MigrateToZone moves k8s cluster instance to new zone +func (k8s K8S) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/k8s/migrateToZone" + + 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/models.go b/pkg/cloudbroker/k8s/models.go new file mode 100644 index 0000000..f3d5fbe --- /dev/null +++ b/pkg/cloudbroker/k8s/models.go @@ -0,0 +1,337 @@ +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"` + + // Description + Description string `json:"desc"` + + // 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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..0e1e26f --- /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/v15/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..129ae47 --- /dev/null +++ b/pkg/cloudbroker/k8s/serialize.go @@ -0,0 +1,43 @@ +package k8s + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..a84da73 --- /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/v15/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..8a4d3da --- /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/v15/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..a1bccb1 --- /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/v15/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..51afa15 --- /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/v15/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..5fbd7b4 --- /dev/null +++ b/pkg/cloudbroker/k8s/worker_add.go @@ -0,0 +1,53 @@ +package k8s + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 + // Default: Q35 + 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..c02db8f --- /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/v15/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..774b285 --- /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/v15/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..da4df0d --- /dev/null +++ b/pkg/cloudbroker/k8s/workers_group_add.go @@ -0,0 +1,103 @@ +package k8s + +import ( + "context" + "net/http" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 the chosen storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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 + // Default: Q35 + 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..690c1cf --- /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/v15/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..7a62236 --- /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/v15/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..a8d6fa8 --- /dev/null +++ b/pkg/cloudbroker/kvmx86.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..8791cf6 --- /dev/null +++ b/pkg/cloudbroker/kvmx86/create.go @@ -0,0 +1,279 @@ +package kvmx86 + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type Interface struct { + // Network type + // Should be one of: + // - VINS + // - EXTNET + // - VFNIC + // - DPDK + // - EMPTY + // - SDN + // - TRUNK + NetType string `url:"netType" json:"netType" validate:"required,kvmx86NetType"` + + // Network ID for connect + 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"` + + // Used for EXTNET, TRUNK and DPDK + // Must be 1500-9216 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` + + // MAC address to assign to this VM when connecting to the specified network + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty" validate:"omitempty"` + + // Net mask + // Used only to DPDK or VFNIC net type + // Required: false + NetMask uint64 `url:"netMask,omitempty" json:"netMask,omitempty"` + + // SDN interface id + // Required: false + SDNInterfaceID string `url:"sdn_interface_id,omitempty" json:"sdn_interface_id,omitempty"` + + // List of security group IDs to assign to this interface + // Required: false + SecGroups []uint64 `url:"security_groups,omitempty" json:"security_groups,omitempty"` + + // Flag indicating whether security groups are enabled for this interface + // Not applicable to netType VFNIC, TRUNK, or SDN + // Required: false + EnableSecGroups bool `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty"` + + // Flag indicating whether this interface is enabled (only for VINS, EXTNET, DPDK, SDN, TRUNK) + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // SDN Segment ID + // Required: false + SDNSegmentID string `url:"sdn_segment_id,omitempty" json:"sdn_segment_id,omitempty"` + + // SDN Object Group IDs + // Required: false + SDNObjectGroupIDs []string `url:"sdn_object_group_ids,omitempty" json:"sdn_object_group_ids,omitempty"` + + // SDN Logical Port Display Name + // Required: false + SDNLogicalPortDisplayName string `url:"sdn_logical_port_display_name,omitempty" json:"sdn_logical_port_display_name,omitempty"` + + // SDN Logical Port Description + // Required: false + SDNLogicalPortDescription string `url:"sdn_logical_port_description,omitempty" json:"sdn_logical_port_description,omitempty"` +} + +// 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 policy id of disk. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // Cache mode for disk + // Required: false + Cache string `url:"cache,omitempty" json:"cache,omitempty"` + + // Discard + // Required: false + Discard string `url:"discard,omitempty" json:"discard,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"` + + // Storage policy id of сompute. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,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"` + + // Node ID + // Required: false + NodeID uint64 `url:"node_id,omitempty" json:"node_id,omitempty"` + + // Custom fields for Compute. Must be dict + // Required: false + CustomField string `url:"customFields,omitempty" json:"customFields,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 + // Default: Q35 + 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"` + + // Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False + // Required: false + PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // Cache mode for boot disk + // Required: false + BootDiskCache string `url:"boot_disk_cache,omitempty" json:"boot_disk_cache,omitempty"` + + // Boot Disk Discard + // Required: false + BootDiskDiscard string `url:"boot_disk_discard,omitempty" json:"boot_disk_discard,omitempty"` + + // Priority weight of the VM: higher value means higher priority and later migration + // Required: false + // Default: 1 + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` + + // CPU alignment profile name + // Required: false + CPUAlignmentProfile string `url:"cpu_alignment_profile,omitempty" json:"cpu_alignment_profile,omitempty"` + + // Clock type for the VM + // Required: false + // Default: default + Clock string `url:"clock,omitempty" json:"clock,omitempty"` +} + +// GetRAM returns RAM field values +func (r CreateRequest) GetRAM() map[string]uint64 { + res := make(map[string]uint64, 1) + + res["RAM"] = r.RAM + + return res +} + +// 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)) + } + + url := "/cloudbroker/kvmx86/create" + + res, err := k.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return 0, err + } + + return strconv.ParseUint(string(res), 10, 64) +} diff --git a/pkg/cloudbroker/kvmx86/create_blank.go b/pkg/cloudbroker/kvmx86/create_blank.go new file mode 100644 index 0000000..6d2d33d --- /dev/null +++ b/pkg/cloudbroker/kvmx86/create_blank.go @@ -0,0 +1,167 @@ +package kvmx86 + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Storage policy id of сompute. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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 the emulated system, Q35 or i440fx + // Required: false + // Default: Q35 + 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:"hp_backed" json:"hp_backed"` + + // 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:"cpu_pin" json:"cpu_pin"` + + // 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:"numa_affinity,omitempty" json:"numa_affinity,omitempty" validate:"omitempty,numaAffinity"` + + // Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False + // Required: false + PreferredCPU []int64 `url:"preferredCpu,omitempty" json:"preferredCpu,omitempty" validate:"omitempty,preferredCPU"` + + // VM type linux, windows or unknown + // Required: false + LoaderType string `url:"loaderType,omitempty" json:"loaderType,omitempty" validate:"omitempty,loaderType"` + + // Boot type of image bios or uefi + // Required: false + BootType string `url:"bootType,omitempty" json:"bootType,omitempty" validate:"omitempty,imageBootType"` + + // Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming. + // 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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,omitempty"` + + // Cache mode for boot disk + // Required: false + BootDiskCache string `url:"boot_disk_cache,omitempty" json:"boot_disk_cache,omitempty"` + + // Boot Disk Discard + // Required: false + BootDiskDiscard string `url:"boot_disk_discard,omitempty" json:"boot_disk_discard,omitempty"` + + // Priority weight of the VM: higher value means higher priority and later migration + // Required: false + // Default: 1 + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` + + // CPU alignment profile name + // Required: false + CPUAlignmentProfile string `url:"cpu_alignment_profile,omitempty" json:"cpu_alignment_profile,omitempty"` + + // Clock type for the VM + // Required: false + // Default: default + Clock string `url:"clock,omitempty" json:"clock,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 +} + +// 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)) + } + + url := "/cloudbroker/kvmx86/createBlank" + + res, err := k.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return 0, err + } + + return strconv.ParseUint(string(res), 10, 64) +} diff --git a/pkg/cloudbroker/kvmx86/kvmx86.go b/pkg/cloudbroker/kvmx86/kvmx86.go new file mode 100644 index 0000000..a96df84 --- /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/v15/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..f380717 --- /dev/null +++ b/pkg/cloudbroker/kvmx86/mass_create.go @@ -0,0 +1,269 @@ +package kvmx86 + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type InterfaceMassCreate struct { + // Network type + // Should be one of: + // - VINS + // - EXTNET + // - TRUNK + // - SDN + // For SDN, one of the fields sdn_interface_id or sdn_segment_id is specified, but not both at the same time + // Required: false + NetType string `url:"netType" json:"netType" validate:"required,massCreateNetType"` + + // Network ID for connect + // Required: false + 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"` + + // Used for EXTNET, TRUNK and DPDK + // Must be 1500-9216 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"` + + // MAC address to assign to this VM when connecting to the specified network + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty" validate:"omitempty"` + + // SDN interface id + // Required: false + SDNInterfaceID string `url:"sdn_interface_id,omitempty" json:"sdn_interface_id,omitempty"` + + // List of security group IDs to assign to this interface + // Required: false + SecGroups []uint64 `url:"security_groups,omitempty" json:"security_groups,omitempty"` + + // Flag indicating whether security groups are enabled for this interface + // Not applicable to netType VFNIC, TRUNK, or SDN + // Required: false + EnableSecGroups bool `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty"` + + // Flag indicating whether this interface is enabled (only for VINS, EXTNET, DPDK, SDN, TRUNK) + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // SDN Segment ID + // Required: false + SDNSegmentID string `url:"sdn_segment_id,omitempty" json:"sdn_segment_id,omitempty"` + + // SDN Object Group IDs + // Required: false + SDNObjectGroupIDs []string `url:"sdn_object_group_ids,omitempty" json:"sdn_object_group_ids,omitempty"` + + // SDN Logical Port Display Name + // Required: false + SDNLogicalPortDisplayName string `url:"sdn_logical_port_display_name,omitempty" json:"sdn_logical_port_display_name,omitempty"` + + // SDN Logical Port Description + // Required: false + SDNLogicalPortDescription string `url:"sdn_logical_port_description,omitempty" json:"sdn_logical_port_description,omitempty"` +} + +// 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"` + + // Storage policy id of сompute. The rules of the specified storage policy will be used. + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" 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 []InterfaceMassCreate `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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // The OS version that will be installed on the virtual machine + // Required: false + OSVersion string `url:"os_version,omitempty" json:"os_version,omitempty"` + + // Cache mode for boot disk + // Required: false + BootDiskCache string `url:"boot_disk_cache,omitempty" json:"boot_disk_cache,omitempty"` + + // Boot Disk Discard + // Required: false + BootDiskDiscard string `url:"boot_disk_discard,omitempty" json:"boot_disk_discard,omitempty"` + + // Priority weight of the VM: higher value means higher priority and later migration + // Required: false + // Default: 1 + Weight uint64 `url:"weight,omitempty" json:"weight,omitempty"` + + // CPU alignment profile name + // Required: false + CPUAlignmentProfile string `url:"cpu_alignment_profile,omitempty" json:"cpu_alignment_profile,omitempty"` + + // Clock type for the VM + // Required: false + // Default: default + Clock string `url:"clock,omitempty" json:"clock,omitempty"` +} + +// 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 + AsyncMode bool `json:"asyncMode"` +} + +type massCreateResponse struct { + Created []uint64 `json:"created"` + Errors map[string]string `json:"errors"` +} + +type MassCreateError struct { + Errors map[string]string +} + +func (e *MassCreateError) Error() string { + errs := make([]error, 0, len(e.Errors)) + for k, v := range e.Errors { + errs = append(errs, fmt.Errorf("%s: %s", k, v)) + } + return errors.Join(errs...).Error() +} + +// 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)) + } + + url := "/cloudbroker/kvmx86/massCreate" + + res, err := k.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, wrapperMassCreateRequest{ + MassCreateRequest: req, + AsyncMode: false, + }) + if err != nil { + return nil, err + } + + var result massCreateResponse + + if err = json.Unmarshal(res, &result); err != nil { + return nil, err + } + + if len(result.Errors) > 0 { + return result.Created, &MassCreateError{Errors: result.Errors} + } + + return result.Created, nil +} + +// MassCreateAsync 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)) + } + + url := "/cloudbroker/kvmx86/massCreate" + + res, err := k.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, wrapperMassCreateRequest{ + MassCreateRequest: req, + AsyncMode: true, + }) + 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..d430f73 --- /dev/null +++ b/pkg/cloudbroker/lb.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..8281e54 --- /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/v15/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..39a286c --- /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/v15/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..d424a9f --- /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/v15/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..e433855 --- /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/v15/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..1b8037e --- /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/v15/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..bbb7964 --- /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/v15/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..bf60ff9 --- /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/v15/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..061d688 --- /dev/null +++ b/pkg/cloudbroker/lb/create.go @@ -0,0 +1,102 @@ +package lb + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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: false + Start interface{} `url:"start,omitempty" json:"start,omitempty" validate:"omitempty,isBool"` + + // 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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,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..2a1d1e2 --- /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/v15/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..db3c429 --- /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/v15/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..77ff85c --- /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/v15/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..9daab30 --- /dev/null +++ b/pkg/cloudbroker/lb/filter.go @@ -0,0 +1,82 @@ +package lb + +import ( + "context" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..4bae9f2 --- /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/v15/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..a7e8a68 --- /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/v15/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..53539f1 --- /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/v15/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..21e0c3a --- /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/v15/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..804e6d0 --- /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/v15/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..b2e2ede --- /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/v15/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..450217c --- /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/v15/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..273ed99 --- /dev/null +++ b/pkg/cloudbroker/lb/list.go @@ -0,0 +1,97 @@ +package lb + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..0570f25 --- /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/v15/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..d9a1b90 --- /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/v15/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/migrate_to_zone.go b/pkg/cloudbroker/lb/migrate_to_zone.go new file mode 100644 index 0000000..959fae7 --- /dev/null +++ b/pkg/cloudbroker/lb/migrate_to_zone.go @@ -0,0 +1,42 @@ +package lb + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move lb to another zone +type MigrateToZoneRequest struct { + // ID of the load balancer instance to move + // Required: true + LBID uint64 `url:"lbId" json:"lbId" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zoneId" json:"zoneId" validate:"required"` +} + +// MigrateToZone moves lb instance to new zone +func (l LB) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/lb/migrateToZone" + + 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/models.go b/pkg/cloudbroker/lb/models.go new file mode 100644 index 0000000..c4d41f1 --- /dev/null +++ b/pkg/cloudbroker/lb/models.go @@ -0,0 +1,344 @@ +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"` + + //Account ID + AccountID uint64 `json:"accountId"` + + // 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 map[string]string `json:"sysctlParams"` + + // Tech status + TechStatus string `json:"techStatus"` + + // User Managed flag + UserManaged bool `json:"userManaged"` + + // VINS ID + VINSID uint64 `json:"vinsId"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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 map[string]string `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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} diff --git a/pkg/cloudbroker/lb/restart.go b/pkg/cloudbroker/lb/restart.go new file mode 100644 index 0000000..9f7e85b --- /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/v15/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..e109c3f --- /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/v15/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..b121cc2 --- /dev/null +++ b/pkg/cloudbroker/lb/serialize.go @@ -0,0 +1,43 @@ +package lb + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..79fe11a --- /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/v15/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..e6721d4 --- /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/v15/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..52f4ddc --- /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/v15/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..b5a8d17 --- /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/v15/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..90109ac --- /dev/null +++ b/pkg/cloudbroker/node.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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/add_ssh_identity.go b/pkg/cloudbroker/node/add_ssh_identity.go new file mode 100644 index 0000000..4af56d5 --- /dev/null +++ b/pkg/cloudbroker/node/add_ssh_identity.go @@ -0,0 +1,54 @@ +package node + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AddSSHIdentityRequest struct to add node ssh information +type AddSSHIdentityRequest struct { + // Node ID + // Required: true + NID uint64 `url:"node_id" json:"node_id" validate:"required"` + + // Host name of the client + // Required: true + ClientHostName string `url:"client_host_name" json:"client_host_name" validate:"required"` + + // SSH host key of the client + // Required: true + ClientHostKey string `url:"client_host_key" json:"client_host_key" validate:"required" ` + + // SSH public key of the client + // Required: true + ClientPublicKey string `url:"client_public_key" json:"client_public_key" validate:"required"` + + // IPv4 address + // Required: true + ClientIPAddress string `url:"client_ip_address" json:"client_ip_address" validate:"required" ` +} + +// AddSSHIdentity adds node ssh information +func (n Node) AddSSHIdentity(ctx context.Context, req AddSSHIdentityRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/add_ssh_identity" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/apply_ipmi_action.go b/pkg/cloudbroker/node/apply_ipmi_action.go new file mode 100644 index 0000000..99a829d --- /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/v15/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 + // Available values : is_powered, 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/autostart.go b/pkg/cloudbroker/node/autostart.go new file mode 100644 index 0000000..ab86971 --- /dev/null +++ b/pkg/cloudbroker/node/autostart.go @@ -0,0 +1,42 @@ +package node + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AutoStartRequest struct to set node autostart +type AutoStartRequest struct { + // Node ID + // Required: true + NodeID uint64 `url:"node_id" json:"node_id" validate:"required"` + + // Auto start + // Required: true + AutoStart bool `url:"autostart" json:"autostart" validate:"required"` +} + +// AutoStart sets node autostart +func (n Node) AutoStart(ctx context.Context, req AutoStartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/autostart" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/consumption.go b/pkg/cloudbroker/node/consumption.go new file mode 100644 index 0000000..cfe4a61 --- /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/v15/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..6302ba3 --- /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/v15/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..9f084be --- /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/v15/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: false + // 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 = false + } + + 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 = false + } + + 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..b917e5d --- /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/v15/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"` + + // Reason + // Required: false + Reason string `url:"reason,omitempty" json:"reason,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..a6c8ff5 --- /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/v15/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/get_logical_cores_count.go b/pkg/cloudbroker/node/get_logical_cores_count.go new file mode 100644 index 0000000..41c6816 --- /dev/null +++ b/pkg/cloudbroker/node/get_logical_cores_count.go @@ -0,0 +1,42 @@ +package node + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetLogicalCoresCountRequest struct to get logical cores count by node +type GetLogicalCoresCountRequest struct { + // Node ID + // Required: true + NodeId uint64 `url:"node_id" json:"node_id" validate:"required"` + + // Target + // Required: false + Target string `url:"target,omitempty" json:"target,omitempty" validate:"omitempty,oneof=core node"` +} + +// GetLogicalCoresCount get logical cores count by node +func (i Node) GetLogicalCoresCount(ctx context.Context, req GetLogicalCoresCountRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/get_logical_cores_count" + + res, err := i.client.DecortApiCall(ctx, http.MethodGet, 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/node/get_network_info.go b/pkg/cloudbroker/node/get_network_info.go new file mode 100644 index 0000000..a150f3e --- /dev/null +++ b/pkg/cloudbroker/node/get_network_info.go @@ -0,0 +1,46 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetNetworkInfoRequest struct to get network information of a node +type GetNetworkInfoRequest struct { + // Node ID + // Required: true + NodeID uint64 `url:"node_id" json:"node_id" validate:"required"` +} + +// GetNetworkInfo gets network information of a node as a RecordNodeNetworkInfo struct +func (n Node) GetNetworkInfo(ctx context.Context, req GetNetworkInfoRequest) (*RecordNodeNetworkInfo, error) { + res, err := n.GetNetworkInfoRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordNodeNetworkInfo{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetNetworkInfoRaw gets network information of a node as an array of bytes +func (n Node) GetNetworkInfoRaw(ctx context.Context, req GetNetworkInfoRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/get_network_info" + + res, err := n.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/node/get_pci_devices.go b/pkg/cloudbroker/node/get_pci_devices.go new file mode 100644 index 0000000..5f17172 --- /dev/null +++ b/pkg/cloudbroker/node/get_pci_devices.go @@ -0,0 +1,62 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetPCIDevicesRequest struct to get list of PCI devices on a node +type GetPCIDevicesRequest struct { + // Node ID + // Required: true + NodeID uint64 `url:"nid" json:"nid" validate:"required"` + + // Search string + // Required: false + Search string `url:"search,omitempty" json:"search,omitempty"` + + // Sort by one of supported fields, format +|-(field) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty" validate:"omitempty,sortBy"` + + // Page number + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + // Required: false + Size uint64 `url:"size,omitempty" json:"size,omitempty"` +} + +// GetPCIDevices gets list of PCI devices on a node as a ListPCIDevices struct +func (n Node) GetPCIDevices(ctx context.Context, req GetPCIDevicesRequest) (*ListPCIDevices, error) { + res, err := n.GetPCIDevicesRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListPCIDevices{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// GetPCIDevicesRaw gets list of PCI devices on a node as an array of bytes +func (n Node) GetPCIDevicesRaw(ctx context.Context, req GetPCIDevicesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/get_pci_devices" + + res, err := n.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/node/get_ssh_identity.go b/pkg/cloudbroker/node/get_ssh_identity.go new file mode 100644 index 0000000..27e00b5 --- /dev/null +++ b/pkg/cloudbroker/node/get_ssh_identity.go @@ -0,0 +1,39 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetSSHIdentityRequest struct to get node ssh information +type GetSSHIdentityRequest struct { + // Node ID + // Required: true + NID uint64 `url:"node_id" json:"node_id" validate:"required"` +} + +// GetSSHIdentity gets node ssh information +func (n Node) GetSSHIdentity(ctx context.Context, req GetSSHIdentityRequest) (*SSHIdentity, error) { + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/get_ssh_identity" + + res, err := n.client.DecortApiCall(ctx, http.MethodGet, url, req) + if err != nil { + return nil, err + } + + info := SSHIdentity{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} 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/install.go b/pkg/cloudbroker/node/install.go new file mode 100644 index 0000000..d82bce7 --- /dev/null +++ b/pkg/cloudbroker/node/install.go @@ -0,0 +1,76 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// InstallRequest struct to install a node +type InstallRequest struct { + // Node name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Node roles + // Required: true + Roles []string `url:"roles" json:"roles" validate:"required"` + + // OS version + // Required: true + OSVersion string `url:"os_version" json:"os_version" validate:"required"` + + // OS user + // Required: false + OSUser string `url:"os_user,omitempty" json:"os_user,omitempty"` + + // OS user password + // Required: true + OSUserPassword string `url:"os_user_password" json:"os_user_password" validate:"required"` + + // Backplane IP address + // Required: true + BackplaneIP string `url:"backplane_ip" json:"backplane_ip" validate:"required"` + + // Management IP address + // Required: true + ManagementIP string `url:"management_ip" json:"management_ip" validate:"required"` + + // VX backend IP address + // Required: true + VXBackendIP string `url:"vxbackend_ip" json:"vxbackend_ip" validate:"required"` + + // Gateway management IP address + // Required: true + GWMgmtIP string `url:"gw_mgmt_ip" json:"gw_mgmt_ip" validate:"required"` + + // IPMI address + // Required: true + IPMIAddress string `url:"ipmi_address" json:"ipmi_address" validate:"required"` + + // IPMI user + // Required: true + IPMIUser string `url:"ipmi_user" json:"ipmi_user" validate:"required"` + + // IPMI password + // Required: true + IPMIPassword string `url:"ipmi_password" json:"ipmi_password" validate:"required"` +} + +// Install installs a node +func (n Node) Install(ctx context.Context, req InstallRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/install" + + 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/list.go b/pkg/cloudbroker/node/list.go new file mode 100644 index 0000000..7b4d5f4 --- /dev/null +++ b/pkg/cloudbroker/node/list.go @@ -0,0 +1,88 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Find by zone ID + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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..86b9f8a --- /dev/null +++ b/pkg/cloudbroker/node/maintenance.go @@ -0,0 +1,51 @@ +package node + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Reason + // Required: false + Reason string `url:"reason,omitempty" json:"reason,omitempty"` + + // Allow node auto-enable + // Default: false + // Required: false + AutoStart bool `url:"autostart" json:"autostart"` +} + +// 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..51901d6 --- /dev/null +++ b/pkg/cloudbroker/node/models.go @@ -0,0 +1,776 @@ +package node + +// Node summary +type RecordNode struct { + // Consumption + Consumption ConsumptionInfo `json:"consumption"` + + // CPU Info + CpuInfo CpuInfo `json:"cpuInfo"` + + // CPU Allocation Ratio + CPUAllocationRatio uint64 `json:"cpu_allocation_ratio"` + + // DPDK info + DPDK DPDK `json:"dpdk"` + + // GID + GID uint64 `json:"gid"` + + // Node ID + ID uint64 `json:"id"` + + // IPAddr + IPAddr []string `json:"ipaddr"` + + // Isolated Cpus + IsolatedCpus []interface{} `json:"isolatedCpus"` + + // MemAllocationRatio + MemAllocationRatio uint64 `json:"mem_allocation_ratio"` + + // Name + Name string `json:"name"` + + // NeedReboot + NeedReboot bool `json:"needReboot"` + + // Netaddr + NetAddr NetAddr `json:"netaddr"` + + // Network mode + NetworkMode string `json:"networkmode"` + + // 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"` + + // Status + Status string `json:"status"` + + // To active + ToActive Role `json:"to_active"` + + // To installing + ToInstalling Role `json:"to_installing"` + + // To maintenance + ToMaintenance Role `json:"to_maintenance"` + + // To restricted + ToRestricted Role `json:"to_restricted"` + + // Version + Version string `json:"version"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` + + // OpenvSwitch Bridges + OpenvSwitchBridges []string `json:"openvswitch_bridges"` + + // Description + Description string `json:"description"` + + // SDN Hypervisor Name + SDNHypervisorName string `json:"sdn_hypervisor_name"` + + // CPU used by the node + UsableCPUs []string `json:"usable_cpus"` + + // AutoStart + AutoStart bool `json:"autostart"` + + // AutoStart Count + AutoStartCount uint64 `json:"autostart_count"` +} + +// 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"` + + // VCPU + VCPU uint64 `json:"vCPUs"` +} + +// 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"` + + // Thread count + ThreadCount uint64 `json:"threadCount"` + + // Flags + Flags []string `json:"flags"` + + // Model name + ModelName string `json:"model_name"` + + // Max supported generation + MaxSupportedGeneration string `json:"max_supported_generation"` +} + +// Main information about node +type ItemNode struct { + // Additional packages + AdditionalPkgs []interface{} `json:"additionalpkgs"` + + // CPU Info + CpuInfo CpuInfo `json:"cpuInfo"` + + // Description + Description string `json:"description"` + + // DPDK + DPDK DPDK `json:"dpdk"` + + // 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"` + + // SDN Hypervisor Name + SDNHypervisorName string `json:"sdn_hypervisor_name"` + + // Seps + Seps []uint64 `json:"seps"` + + // SerialNum + SerialNum string `json:"serialNum"` + + // SriovEnabled + SriovEnabled bool `json:"sriovEnabled"` + + // Status + Status string `json:"status"` + + // Tags + Tags []string `json:"tags"` + + // Type + Type string `json:"type"` + + //UEFI Firmware File + UEFIFirmwareFile string `json:"uefiFirmwareFile"` + + // Version + Version string `json:"version"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` + + // OpenvSwitch Bridges + OpenvSwitchBridges []string `json:"openvswitch_bridges"` + + // APIUrl + APIUrl string `json:"apiUrl"` + + // Drivers + Drivers []string `json:"drivers"` + + // Old Compat LVM ID + OldCompatLVMID uint64 `json:"old_compat_lvm_id"` + + // CPU Allocation ratio + CPUAllocationRatio uint64 `json:"cpu_allocation_ratio"` + + // MemAllocationRatio + MemAllocationRatio uint64 `json:"mem_allocation_ratio"` + + // Packages + Packages map[string]PackageInfo `json:"packages"` + + // CPU used by the node + UsableCPUs []string `json:"usable_cpus"` + + // AutoStart + AutoStart bool `json:"autostart"` + + // AutoStart Count + AutoStartCount uint64 `json:"autostart_count"` + + // PCI devices attached to the node + PCIDevices []ItemPCIDevice `json:"pci_devices"` +} + +type PackageInfo struct { + // Installed size + InstalledSize string `json:"installed_size"` + // Version + Ver string `json:"ver"` +} + +// 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"` + + // 1G available + OneGAvailable uint64 `json:"1G_available"` + + // 1G free + OneGFree uint64 `json:"1G_free"` + + // 1G reserved + OneGReserved uint64 `json:"1G_reserved"` + + // 1G used + OneGUsed uint64 `json:"1G_used"` + + // 1G DPDK reserved + OneGDPDKReserved uint64 `json:"1G_dpdk_reserved"` + + // 2M + TwoM uint64 `json:"2M"` + + // 2M available + TwoMAvailable uint64 `json:"2M_available"` + + // 2M free + TwoMFree uint64 `json:"2M_free"` + + // 2M reserved + TwoMReserved uint64 `json:"2M_reserved"` + + // 2M used + TwoMUsed uint64 `json:"2M_used"` + + // 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"` +} + +// Net address +type NetAddr struct { + // Name + Name string `json:"name"` + + // IP list backplane1 node + IP []string `json:"ip"` +} + +// DPDK info +type DPDK struct { + // Bridges + Bridges Bridges `json:"bridges"` + + // hp memory + HPMemory map[string]uint64 `json:"hp_memory"` + + // pmd cpu + PMDCPU []uint64 `json:"pmd_cpu"` +} + +// Bridges +type Bridges struct { + Backplane1 Backplane1 `json:"backplane1"` +} + +// Backplane1 +type Backplane1 struct { + Interfaces []string `json:"interfaces"` + NumaNode int64 `json:"numa_node"` +} + +// Role +type Role struct { + Actor string `json:"actor"` + Reason string `json:"reason"` + Time uint64 `json:"time"` +} + +// PCI device info +type ItemPCIDevice struct { + // Hardware path + HWPath string `json:"hw_path"` + + // Current driver + CurrentDriver string `json:"current_driver"` + + // NUMA node + NUMANode uint64 `json:"numa_node"` + + // Product ID + ProductID string `json:"product_id"` + + // Product name + ProductName string `json:"product_name"` + + // Vendor ID + VendorID string `json:"vendor_id"` + + // Vendor name + VendorName string `json:"vendor_name"` + + // IOMMU group + IOMMUGroup uint64 `json:"iommu_group"` +} + +// List of PCI devices +type ListPCIDevices struct { + // Data + Data []ItemPCIDevice `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Response for PCI device driver binding actions +type RecordPCIDeviceDriver struct { + // Success + Success bool `json:"success"` + + // Message + Message string `json:"message"` + + // Result + Result interface{} `json:"result"` +} + +// Information about SSH Identity +type SSHIdentity struct { + //Host name of the client + HostName string `json:"host_name"` + + //SSH host key of the client + HostKey string `json:"host_key"` + + //Array of SSH public keys of the client + PublicKeys []string `json:"public_keys"` +} + +// Full network configuration of a node +type RecordNodeNetworkInfo struct { + // System-level information about all network interfaces on the node + SystemNetworksInfo map[string]SystemNetworkInfo `json:"system_networks_info"` + + // Raw OVS ports data + OVSNetworksInfo []OVSNetworkInfo `json:"ovs_networks_info"` + + // VM network interface connections + LibvirtNetworksInfo []LibvirtNetworkInfo `json:"libvirt_networks_info"` + + // Assembled network topology of the node + Topology NetworkTopology `json:"topology"` +} + +// System network interface +type SystemNetworkInfo struct { + // MTU value + MTU uint64 `json:"mtu"` + + // Interface link speed + Speed int64 `json:"speed"` + + // Linux bridge ID + BridgeID string `json:"bridge_id"` + + // Bridge port ID + BPortID string `json:"bport_id"` + + // MAC address + MAC string `json:"mac"` +} + +// OVS port information +type OVSNetworkInfo struct { + // Bridge name + BridgeName string `json:"bridge_name"` + + // Bridge tag + BridgeTag string `json:"bridge_tag"` + + // Interface UUID + InterfaceUUID string `json:"interface_uuid"` + + // Interface type + InterfaceType string `json:"interface_type"` + + // Interface name + InterfaceName string `json:"interface_name"` + + // Interface MTU value + InterfaceMTU uint64 `json:"interface_mtu"` + + // Interface IP address + InterfaceIP string `json:"interface_ip"` + + // Interface MAC address + InterfaceMAC string `json:"interface_mac"` + + // Interface peer name + InterfacePeer string `json:"interface_peer"` +} + +// VM network interface connection +type LibvirtNetworkInfo struct { + // VM name + VMName string `json:"vm_name"` + + // Host-side interface name used by the VM + Interface string `json:"interface"` + + // Interface type + InterfaceType string `json:"interface_type"` + + // Name of the bridge the VM interface is attached to + InterfaceSource string `json:"interface_source"` +} + +// Assembled network topology of the node +type NetworkTopology struct { + // Map of all network interfaces by name with their topology details and connections + Interfaces map[string]TopologyInterface `json:"interfaces"` +} + +// Interface and its role in the network topology +type TopologyInterface struct { + // Interface type + Type string `json:"type"` + + // Interface MTU value + MTU uint64 `json:"mtu"` + + // Interface link speed + Speed int64 `json:"speed"` + + // Connections to VMs and to bridges + Connections TopologyInterfaceConnections `json:"connections"` + + // VLANs associated with the interface + VLANs []string `json:"vlans"` + + // Linux bridge ID + BridgeID string `json:"bridge_id"` + + // Port configuration of this bridge + BridgeInfo *TopologyBridgeInfo `json:"bridge_info"` + + // Peer bridges connected to this bridge via patch ports + BridgeConnections []TopologyBridgeRef `json:"bridge_connections"` + + // Names of interfaces attached to this bridge as ports + ConnectedInterfaces []string `json:"connected_interfaces"` + + // Peer interface name + Peer string `json:"peer"` + + // OVS UUID + UUID string `json:"uuid"` +} + +// Lists what is connected to this interface +type TopologyInterfaceConnections struct { + // VMs connected to this interface + VMs []TopologyVMConnection `json:"vms"` + + // Bridges this interface is attached to + Bridges []TopologyBridgeAttachment `json:"bridges"` +} + +// VM connected to a bridge or interface +type TopologyVMConnection struct { + // VM name + Name string `json:"name"` + + // Host-side interface name + VMInterface string `json:"vm_interface"` + + // VM interface type + VMInterfaceType string `json:"vm_interface_type"` + + // Connection type + ConnectionType string `json:"connection_type"` + + // Via bridge + ViaBridge string `json:"via_bridge"` +} + +// How interface is attached to a bridge +type TopologyBridgeAttachment struct { + // Bridge name + Name string `json:"name"` + + // Bridge type + Type string `json:"type"` + + // Attachment method + Via string `json:"via"` + + // Port details + PortInfo *TopologyPortInfo `json:"port_info"` + + // Linux bridge port ID + BPortID string `json:"bport_id"` +} + +// Bridge port entry +type TopologyPortInfo struct { + // Port name + Name string `json:"name"` + + // Port type + Type string `json:"type"` + + // MTU value + MTU uint64 `json:"mtu"` + + // VLAN tag + VLAN string `json:"vlan"` + + // OVS UUID of the port + UUID string `json:"uuid"` +} + +// OVS bridge port configuration +type TopologyBridgeInfo struct { + // Bridge type + Type string `json:"type"` + + // List of ports on this bridge + Ports []TopologyBridgePort `json:"ports"` +} + +// Port on an OVS bridge +type TopologyBridgePort struct { + // Port name + Name string `json:"name"` + + // Port type + Type string `json:"type"` + + // MTU value + MTU uint64 `json:"mtu"` + + // VLAN tag + VLAN string `json:"vlan"` + + // UUID + UUID string `json:"uuid"` + + // IP address + IP string `json:"ip"` + + // MAC address + MAC string `json:"mac"` +} + +// Peer bridge connected via a patch port +type TopologyBridgeRef struct { + // Name of the peer bridge + Name string `json:"name"` + + // Name of the patch port used to connect to the peer bridge + Via string `json:"via"` + + // VLAN tag on the patch connection + VLAN string `json:"vlan"` +} diff --git a/pkg/cloudbroker/node/node.go b/pkg/cloudbroker/node/node.go new file mode 100644 index 0000000..eea1847 --- /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/v15/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/pci_device_driver_to_kernel.go b/pkg/cloudbroker/node/pci_device_driver_to_kernel.go new file mode 100644 index 0000000..5c0e12c --- /dev/null +++ b/pkg/cloudbroker/node/pci_device_driver_to_kernel.go @@ -0,0 +1,45 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// PCIDeviceDriverToKernelRequest struct to bind PCI device driver to kernel +type PCIDeviceDriverToKernelRequest struct { + // Node ID + // Required: true + NodeID uint64 `url:"nid" json:"nid" validate:"required"` + + // Hardware path of the PCI device, e.g. 0000:81:00.0 + // Required: true + HWPath string `url:"hw_path" json:"hw_path" validate:"required,pciDeviceHWPath"` +} + +// PCIDeviceDriverToKernel binds PCI device driver to kernel +func (n Node) PCIDeviceDriverToKernel(ctx context.Context, req PCIDeviceDriverToKernelRequest) (*RecordPCIDeviceDriver, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/pci_device_driver_to_kernel" + + res, err := n.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + result := RecordPCIDeviceDriver{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return &result, nil +} diff --git a/pkg/cloudbroker/node/pci_device_driver_to_vfio.go b/pkg/cloudbroker/node/pci_device_driver_to_vfio.go new file mode 100644 index 0000000..5466a22 --- /dev/null +++ b/pkg/cloudbroker/node/pci_device_driver_to_vfio.go @@ -0,0 +1,50 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// PCIDeviceDriverToVFIORequest struct to bind PCI device driver to VFIO +type PCIDeviceDriverToVFIORequest struct { + // Node ID + // Required: true + NodeID uint64 `url:"nid" json:"nid" validate:"required"` + + // Hardware path of the PCI device, e.g. 0000:81:00.0 + // Required: true + HWPath string `url:"hw_path" json:"hw_path" validate:"required,pciDeviceHWPath"` + + // Driver binding mode + // Required: true + // Possible values: safe, unsafe + Mode string `url:"mode" json:"mode" validate:"required,oneof=safe unsafe"` +} + +// PCIDeviceDriverToVFIO binds PCI device driver to VFIO +func (n Node) PCIDeviceDriverToVFIO(ctx context.Context, req PCIDeviceDriverToVFIORequest) (*RecordPCIDeviceDriver, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/pci_device_driver_to_vfio" + + res, err := n.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + result := RecordPCIDeviceDriver{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return &result, nil +} diff --git a/pkg/cloudbroker/node/restrict.go b/pkg/cloudbroker/node/restrict.go new file mode 100644 index 0000000..6a05bea --- /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/v15/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..709eccf --- /dev/null +++ b/pkg/cloudbroker/node/serialize.go @@ -0,0 +1,59 @@ +package node + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..27e211e --- /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/v15/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_cpu_allocation_ratio.go b/pkg/cloudbroker/node/set_cpu_allocation_ratio.go new file mode 100644 index 0000000..8463023 --- /dev/null +++ b/pkg/cloudbroker/node/set_cpu_allocation_ratio.go @@ -0,0 +1,44 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// SetCpuAllocationRatioRequest struct to set CPU allocation ratio +type SetCpuAllocationRatioRequest struct { + // Node ID + // Required: true + NodeID uint64 `url:"node_id" json:"node_id" validate:"required"` + + // Allocation ratio (zero or positive value) + // Required: true + Ratio uint64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetCpuAllocationRatio set CPU allocation ratio +func (i Node) SetCpuAllocationRatio(ctx context.Context, req SetCpuAllocationRatioRequest) (*ItemNode, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/set_cpu_allocation_ratio" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := ItemNode{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, 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..d3a4b42 --- /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/v15/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_mem_allocation_ratio.go b/pkg/cloudbroker/node/set_mem_allocation_ratio.go new file mode 100644 index 0000000..a7a076d --- /dev/null +++ b/pkg/cloudbroker/node/set_mem_allocation_ratio.go @@ -0,0 +1,44 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// SetMemAllocationRatioRequest struct to set memory allocation ratio +type SetMemAllocationRatioRequest struct { + // Node ID + // Required: true + NodeID uint64 `url:"node_id" json:"node_id" validate:"required"` + + // Allocation ratio (zero or positive value) + // Required: true + Ratio uint64 `url:"ratio" json:"ratio" validate:"required"` +} + +// SetMemAllocationRatio set memory allocation ratio +func (i Node) SetMemAllocationRatio(ctx context.Context, req SetMemAllocationRatioRequest) (*ItemNode, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/set_mem_allocation_ratio" + + res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := ItemNode{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, 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..f936a9a --- /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/v15/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..9b4034b --- /dev/null +++ b/pkg/cloudbroker/node/set_vfs_number.go @@ -0,0 +1,51 @@ +package node + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Number of VF to assign + // Required: true + VFParams []VFParam `url:"vfParams" json:"vfParams" validate:"required"` +} + +// SetVFsNumber sets number of VFs for individual NIC on node +func (n Node) SetVFsNumber(ctx context.Context, req SetVFsNumberRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/setVFsNumber" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + + if 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/set_vfs_params.go b/pkg/cloudbroker/node/set_vfs_params.go new file mode 100644 index 0000000..6c733d0 --- /dev/null +++ b/pkg/cloudbroker/node/set_vfs_params.go @@ -0,0 +1,62 @@ +package node + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type VFParam struct { + // ID of the FN + // Required: true + FNID uint64 `url:"fnId" json:"fnId" validate:"required"` + + // Trust + // Required: true + Trust bool `url:"trust" json:"trust" validate:"required"` + + // Enable spoof checking + // Required: true + SpoofChk bool `url:"spoofchk" json:"spoofchk" validate:"required"` +} + +// SetVFsParamsRequest struct to set params of VFs for individual NIC on node +type SetVFsParamsRequest 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 + VFParams []VFParam `url:"vfParams" json:"vfParams" validate:"required"` +} + +// SetVFsParams sets params of VFs for individual NIC on node +func (n Node) SetVFsParams(ctx context.Context, req SetVFsParamsRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/setVFsParams" + + res, err := n.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + + if 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/update.go b/pkg/cloudbroker/node/update.go new file mode 100644 index 0000000..972cdd3 --- /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/v15/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/node/update_description.go b/pkg/cloudbroker/node/update_description.go new file mode 100644 index 0000000..2df6a1e --- /dev/null +++ b/pkg/cloudbroker/node/update_description.go @@ -0,0 +1,42 @@ +package node + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateDescriptionRequest struct to update description of the node +type UpdateDescriptionRequest struct { + // Node ID + // Required: true + NID uint64 `url:"nid" json:"nid" validate:"required"` + + // New description for the node + // Required: true + Description string `url:"description" json:"description" validate:"required"` +} + +// UpdateDescription updates description of the node +func (n Node) UpdateDescription(ctx context.Context, req UpdateDescriptionRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/node/update_description" + + res, err := n.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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.go b/pkg/cloudbroker/pcidevice.go new file mode 100644 index 0000000..97456ab --- /dev/null +++ b/pkg/cloudbroker/pcidevice.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..bb3a8df --- /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/v15/internal/validators" +) + +// CreateRequest struct to creating PCI device +type CreateRequest struct { + // NodeID + // Required: true + NodeID uint64 `url:"node_id" json:"node_id" 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..e2bb29e --- /dev/null +++ b/pkg/cloudbroker/pcidevice/delete.go @@ -0,0 +1,41 @@ +package pcidevice + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..ecaa75a --- /dev/null +++ b/pkg/cloudbroker/pcidevice/disable.go @@ -0,0 +1,41 @@ +package pcidevice + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..705ad37 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/enable.go @@ -0,0 +1,37 @@ +package pcidevice + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..e353e3d --- /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/v15/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..61c8a83 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/models.go @@ -0,0 +1,49 @@ +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"` + + // Node ID + NodeID uint64 `json:"nodeId"` + + // 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..0d6f853 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/pcidevice.go @@ -0,0 +1,15 @@ +package pcidevice + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..46fcb09 --- /dev/null +++ b/pkg/cloudbroker/pcidevice/serialize.go @@ -0,0 +1,42 @@ +package pcidevice + +import ( + "encoding/json" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..90a3a8e --- /dev/null +++ b/pkg/cloudbroker/prometheus.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..dfb9d3e --- /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/v15/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..bcc8e73 --- /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/v15/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: false + 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..74ca898 --- /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/v15/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: false + 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..17c0fab --- /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/v15/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: false + 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..6458e6e --- /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/v15/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: false + 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..49e79f9 --- /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/v15/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: false + 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..a7d5bff --- /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/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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..6e4c79f --- /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/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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..0add6f4 --- /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/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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..543d2a5 --- /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/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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..5d2b58d --- /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/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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..e45e651 --- /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/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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..8dc187c --- /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/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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..5195ab5 --- /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/v15/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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + 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/computes.go b/pkg/cloudbroker/prometheus/computes.go new file mode 100644 index 0000000..66c05de --- /dev/null +++ b/pkg/cloudbroker/prometheus/computes.go @@ -0,0 +1,77 @@ +package prometheus + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +const ( + ComputeCPULoad = "computeCPUload" + ComputeMemoryUsage = "computeMemoryUsage" + ComputeMemoryUsable = "computeMemoryUsable" + ComputeMemoryUnused = "computeMemoryUnused" + ComputeMemoryUsed = "computeMemoryUsed" + ComputeMemoryAvailable = "computeMemoryAvailable" + ComputeReadBytes = "computeReadBytes" + ComputeReadRequests = "computeReadRequests" + ComputeReceiveBytes = "computeReceiveBytes" + ComputeTransmitBytes = "computeTransmitBytes" + ComputeTransmitPackets = "computeTransmitPackets" + ComputeWriteBytes = "computeWriteBytes" + ComputeWriteRequests = "computeWriteRequests" +) + +type ComputesRequest struct { + // List of compute IDs to fetch metrics for + // Required: true + ComputeIDs []uint64 `url:"computeIds" json:"computeIds" validate:"required"` + + // List of compute IDs to fetch metrics for + // Required: true + MetricIDs []string `url:"metricIds" json:"metricIds" 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: false + Step uint64 `url:"step,omitempty" json:"step,omitempty"` + + // Number of zeros after the decimal point + // Required: false + DecimalPlaces uint64 `url:"decimalPlaces,omitempty" json:"decimalPlaces,omitempty"` +} + +// Get multiple metrics for multiple compute instances +func (p Prometheus) Computes(ctx context.Context, req ComputesRequest) (*ComputesData, error) { + res, err := p.ComputesRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ComputesData{} + + 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) ComputesRaw(ctx context.Context, req ComputesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/prometheus/computes" + + 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..76ee3bc --- /dev/null +++ b/pkg/cloudbroker/prometheus/models.go @@ -0,0 +1,40 @@ +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"` +} + +// ComputesData represents an array of data points for computes +type ComputesData []ItemCompute + +// ItemCompute represents a single data of compute +type ItemCompute struct { + // Compute ID + ComputeID uint64 `json:"computeId"` + + // Array of metrics + Metrics []ItemMetric `json:"metrics"` + + // Error + Error string `json:"error"` +} + +// ItemMetric represents a single data point of metric +type ItemMetric struct { + // Metric ID + MetricID string `json:"metricId"` + + // Data represents an array of data points + Data PrometheusData `json:"data"` + + // Error + Error string `json:"error"` +} diff --git a/pkg/cloudbroker/prometheus/prometheus.go b/pkg/cloudbroker/prometheus/prometheus.go new file mode 100644 index 0000000..490971a --- /dev/null +++ b/pkg/cloudbroker/prometheus/prometheus.go @@ -0,0 +1,17 @@ +package prometheus + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..57cc38b --- /dev/null +++ b/pkg/cloudbroker/resmon.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..097b460 --- /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/v15/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: false + 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..386ec4d --- /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/v15/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: false + 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..1e8f661 --- /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/v15/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: false + 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_node.go b/pkg/cloudbroker/resmon/get_by_node.go new file mode 100644 index 0000000..edde662 --- /dev/null +++ b/pkg/cloudbroker/resmon/get_by_node.go @@ -0,0 +1,53 @@ +package resmon + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type GetByNodeRequest struct { + // Node ID + // Required: true + NodeID uint64 `url:"nodeId" json:"nodeId" validate:"required"` + + // Start of time period - unixtime + // Required: false + StartTime uint64 `url:"starttime,omitempty" json:"starttime,omitempty"` + + // End of time period - unixtime + // Required: false + EndTime uint64 `url:"endtime,omitempty" json:"endtime,omitempty"` +} + +// Get a grid resource monitoring for the specified time period +func (r Resmon) GetByNode(ctx context.Context, req GetByNodeRequest) (*GetByNodeData, error) { + res, err := r.GetByNodeRaw(ctx, req) + if err != nil { + return nil, err + } + + info := GetByNodeData{} + + 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) GetByNodeRaw(ctx context.Context, req GetByNodeRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/resmon/get_by_node" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/resmon/get_by_nodes.go b/pkg/cloudbroker/resmon/get_by_nodes.go new file mode 100644 index 0000000..130ff79 --- /dev/null +++ b/pkg/cloudbroker/resmon/get_by_nodes.go @@ -0,0 +1,49 @@ +package resmon + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type GetByNodesRequest struct { + // Start of time period - unixtime + // Required: false + StartTime uint64 `url:"starttime,omitempty" json:"starttime,omitempty"` + + // End of time period - unixtime + // Required: false + EndTime uint64 `url:"endtime,omitempty" json:"endtime,omitempty"` +} + +// Get a grid resource monitoring for the specified time period +func (r Resmon) GetByNodes(ctx context.Context, req GetByNodesRequest) (*GetByNodeData, error) { + res, err := r.GetByNodesRaw(ctx, req) + if err != nil { + return nil, err + } + + info := GetByNodeData{} + + 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) GetByNodesRaw(ctx context.Context, req GetByNodesRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/resmon/get_by_nodes" + + 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..ec0d71c --- /dev/null +++ b/pkg/cloudbroker/resmon/models.go @@ -0,0 +1,91 @@ +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"` + NodeID uint64 `json:"nodeId"` +} + +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 { + ID uint64 `json:"id"` + Pool string `json:"pool"` + ResID string `json:"resId"` + SizeUsed float64 `json:"sizeUsed"` + SizeAvailable float64 `json:"sizeAvailable"` + SizeMax float64 `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 { + NodesCPU uint64 `json:"nodesCPU"` + 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"` +} + +// GetByNodeData represents an array of data points +type GetByNodeData []GetByNodePoint + +type GetByNodePoint struct { + Usage NodeUsage `json:"usage"` + CPUInfo CPUinfoByNode `json:"cpuInfo"` + Name string `json:"name"` + ID uint64 `json:"id"` +} + +type NodeUsage 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 CPUinfoByNode 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..3484bd1 --- /dev/null +++ b/pkg/cloudbroker/resmon/resmon.go @@ -0,0 +1,15 @@ +package resmon + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..9f8dc13 --- /dev/null +++ b/pkg/cloudbroker/rg.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..71f0843 --- /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/v15/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..00a7b67 --- /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/v15/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/add_storage_policy.go b/pkg/cloudbroker/rg/add_storage_policy.go new file mode 100644 index 0000000..cf45851 --- /dev/null +++ b/pkg/cloudbroker/rg/add_storage_policy.go @@ -0,0 +1,46 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AddStoragePolicyRequest struct for adding storage policy to the resource group +type AddStoragePolicyRequest struct { + // ID of resource group to add to + // Required: true + RGID uint64 `url:"resgroup_id" json:"resgroup_id" validate:"required"` + + // ID of the storage policy to which to connect resource group + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // Limit storage resources GB. Or -1 unlimit + // Required: false + Limit int `url:"limit,omitempty" json:"limit,omitempty"` +} + +// AddStoragePolicy add storage policy to the account. +func (r RG) AddStoragePolicy(ctx context.Context, req AddStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/add_storage_policy" + + 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..489fd2b --- /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/v15/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..dc134e4 --- /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/v15/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..79de1e1 --- /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/v15/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..6c1023a --- /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/v15/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..6e1380f --- /dev/null +++ b/pkg/cloudbroker/rg/create.go @@ -0,0 +1,111 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Storage policies + // Required: false + StoragePolicies []StoragePolicy `url:"-" json:"storage_policies,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 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"` + + // 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, dpdk, changemac, trunk + // Required: false + ComputeFeatures []string `url:"computeFeatures,omitempty" json:"computeFeatures,omitempty" validate:"omitempty,computeFeatures"` + + // SDN access group id + // Required: false + SDNAccessGroupID string `url:"sdn_access_group_id,omitempty" json:"sdn_access_group_id,omitempty"` +} + +type StoragePolicy struct { + ID uint64 `url:"id" json:"id"` + Limit int `url:"limit" json:"limit"` +} + +// 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.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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/del_storage_policy.go b/pkg/cloudbroker/rg/del_storage_policy.go new file mode 100644 index 0000000..cefb13f --- /dev/null +++ b/pkg/cloudbroker/rg/del_storage_policy.go @@ -0,0 +1,42 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DelStoragePolicyRequest struct for deleting storage policy to the resource group +type DelStoragePolicyRequest struct { + // ID of resource group + // Required: true + RGID uint64 `url:"resgroup_id" json:"resgroup_id" validate:"required"` + + // ID of the storage policy to which to disconnect account + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +// DelStoragePolicy delete storage policy to the account. +func (r RG) DelStoragePolicy(ctx context.Context, req DelStoragePolicyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/del_storage_policy" + + 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/delete.go b/pkg/cloudbroker/rg/delete.go new file mode 100644 index 0000000..4b082ab --- /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/v15/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..89eb175 --- /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/v15/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..a1492ea --- /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/v15/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..9d22512 --- /dev/null +++ b/pkg/cloudbroker/rg/filter_test.go @@ -0,0 +1,241 @@ +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", + ResourceLimits: ResourceLimits{ + CUC: -1, + CuD: -1, + CUI: -1, + CUM: -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", + ResourceLimits: ResourceLimits{ + CUC: -1, + CuD: -1, + CUI: -1, + CUM: -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", + ResourceLimits: ResourceLimits{ + CUC: -1, + CuD: -1, + CUI: -1, + CUM: -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..eb1f18a --- /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/v15/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..05f7549 --- /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/v15/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..030ee4b --- /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/v15/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..2e0db64 --- /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/v15/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..81a4397 --- /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/v15/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..16ca550 --- /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/v15/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..e772509 --- /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/v15/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..6c0f2b1 --- /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/v15/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..d8d4111 --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/massDelete" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/rg/mass_disable.go b/pkg/cloudbroker/rg/mass_disable.go new file mode 100644 index 0000000..21dad05 --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/massDisable" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/rg/mass_enable.go b/pkg/cloudbroker/rg/mass_enable.go new file mode 100644 index 0000000..71a7a3a --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/rg/massEnable" + + res, err := r.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/rg/models.go b/pkg/cloudbroker/rg/models.go new file mode 100644 index 0000000..241577f --- /dev/null +++ b/pkg/cloudbroker/rg/models.go @@ -0,0 +1,723 @@ +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"` + + // 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:"id"` +} + +type ListResourceConsumption struct { + // Data + Data []ItemResourceConsumption `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +// Access Control List +type ACL struct { + // Email + Email string `json:"email"` + + // 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"` + + // GPU units + GPUUnits float64 `json:"gpu_units"` + + // Storage policies + StoragePolicies []StoragePolicy `json:"storage_policy"` +} + +// 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 uint64 `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"` + + // Resource limits + ResourceLimits ResourceLimits `json:"resourceLimits"` + + // Resource types list + ResTypes []string `json:"resourceTypes"` + + // SDN access group id + SDNAccessGroupID string `json:"sdn_access_group_id"` + + // Secret + Secret string `json:"secret"` + + // Storage policy ids + StoragePolicyIDs []uint64 `json:"storage_policy_ids"` + + // 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..3c5cf8f --- /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/v15/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..57f59b9 --- /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/v15/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..4628e6f --- /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/v15/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..19b7148 --- /dev/null +++ b/pkg/cloudbroker/rg/serialize.go @@ -0,0 +1,43 @@ +package rg + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..9676240 --- /dev/null +++ b/pkg/cloudbroker/rg/set_cpu_allocation_parameter.go @@ -0,0 +1,43 @@ +package rg + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..09dc8f9 --- /dev/null +++ b/pkg/cloudbroker/rg/set_cpu_allocation_ratio.go @@ -0,0 +1,41 @@ +package rg + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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 uint64 `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..22e9a9a --- /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/v15/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..b856ff2 --- /dev/null +++ b/pkg/cloudbroker/rg/update.go @@ -0,0 +1,80 @@ +package rg + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 number of assigned public IPs + // Required: false + MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,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"` + + // Storage policies + // Required: false + StoragePolicies []StoragePolicy `url:"-" json:"storage_policies,omitempty"` + + // CPU allocation parameter + // Required: false + CPUAllocationParameter string `url:"cpu_allocation_parameter,omitempty" json:"cpu_allocation_parameter,omitempty"` +} + +// 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.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if 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..a41090d --- /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/v15/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, dpdk, changemac + // 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..76ba0ff --- /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/v15/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..597a1b4 --- /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/v15/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/secgroup/create.go b/pkg/cloudbroker/secgroup/create.go new file mode 100644 index 0000000..1c37f38 --- /dev/null +++ b/pkg/cloudbroker/secgroup/create.go @@ -0,0 +1,46 @@ +package secgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type CreateRequest struct { + // Account ID that owns security group + // Required: true + AccountID uint64 `url:"account_id" json:"account_id" validate:"required"` + + // Security group name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Security group description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` +} + +func (sg SecurityGroup) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/security_group/create" + + res, err := sg.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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/secgroup/create_rule.go b/pkg/cloudbroker/secgroup/create_rule.go new file mode 100644 index 0000000..fe8bc52 --- /dev/null +++ b/pkg/cloudbroker/secgroup/create_rule.go @@ -0,0 +1,63 @@ +package secgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type CreateRuleRequest struct { + // Security group ID + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` + + // Traffic direction (inbound/outbound) + // Required: true + Direction string `url:"direction" json:"direction" validate:"required,securityGroupDirection"` + + // IP protocol version + // Default: IPv4 + // Required: false + Ethertype string `url:"ethertype,omitempty" json:"ethertype,omitempty" validate:"omitempty,securityGroupEthertype"` + + // Network protocol, available values : icmp, tcp, udp + // Required: false + Protocol string `url:"protocol,omitempty" json:"protocol,omitempty" validate:"omitempty,securityGroupProtocol"` + + // Start port number (for TCP/UDP) + // Required: false + PortRangeMin uint64 `url:"port_range_min,omitempty" json:"port_range_min,omitempty"` + + // End port number (for TCP/UDP) + // Required: false + PortRangeMax uint64 `url:"port_range_max,omitempty" json:"port_range_max,omitempty"` + + // Remote IP prefix in CIDR notation + // Required: false + RemoteIPPrefix string `url:"remote_ip_prefix,omitempty" json:"remote_ip_prefix,omitempty"` +} + +func (sg SecurityGroup) CreateRule(ctx context.Context, req CreateRuleRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/security_group/create_rule" + + res, err := sg.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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/secgroup/delete.go b/pkg/cloudbroker/secgroup/delete.go new file mode 100644 index 0000000..02eaeff --- /dev/null +++ b/pkg/cloudbroker/secgroup/delete.go @@ -0,0 +1,36 @@ +package secgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type DeleteRequest struct { + // Security group ID + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` +} + +func (sg SecurityGroup) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/security_group/delete" + + res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/secgroup/delete_rule.go b/pkg/cloudbroker/secgroup/delete_rule.go new file mode 100644 index 0000000..6da23bd --- /dev/null +++ b/pkg/cloudbroker/secgroup/delete_rule.go @@ -0,0 +1,40 @@ +package secgroup + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type DeleteRuleRequest struct { + // Security group ID + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` + + // Rule ID + // Required: true + RuleID uint64 `url:"rule_id" json:"rule_id" validate:"required"` +} + +func (sg SecurityGroup) DeleteRule(ctx context.Context, req DeleteRuleRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/security_group/delete_rule" + + res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/secgroup/filter.go b/pkg/cloudbroker/secgroup/filter.go new file mode 100644 index 0000000..6a03da3 --- /dev/null +++ b/pkg/cloudbroker/secgroup/filter.go @@ -0,0 +1,80 @@ +package secgroup + +// FilterByID returns ListSecurityGroups with specified ID. +func (lsg ListSecurityGroups) FilterByID(id uint64) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.ID == id + } + + return lsg.FilterFunc(predicate) +} + +// FilterByID returns ListSecurityGroups with specified Name. +func (lsg ListSecurityGroups) FilterByName(name string) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.Name == name + } + + return lsg.FilterFunc(predicate) +} + +// FilterByCreatedBy returns ListSecurityGroups with specified CreatedBy. +func (lsg ListSecurityGroups) FilterByCreatedBy(createdBy string) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.CreatedBy == createdBy + } + + return lsg.FilterFunc(predicate) +} + +// FilterByDescription returns ListSecurityGroups with specified Description. +func (lsg ListSecurityGroups) FilterByDescription(description string) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.Description == description + } + + return lsg.FilterFunc(predicate) +} + +// FilterByUpdatedBy returns ListSecurityGroups with specified UpdatedBy. +func (lsg ListSecurityGroups) FilterByUpdatedBy(updatedBy string) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.UpdatedBy == updatedBy + } + + return lsg.FilterFunc(predicate) +} + +// FilterByAccountID returns ListSecurityGroups with specified AccountID. +func (lsg ListSecurityGroups) FilterByAccountID(accountID uint64) ListSecurityGroups { + predicate := func(isg ItemSecurityGroup) bool { + return isg.AccountID == accountID + } + + return lsg.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListSecurityGroups based on a user-specified predicate. +func (lsg ListSecurityGroups) FilterFunc(predicate func(ItemSecurityGroup) bool) ListSecurityGroups { + var result ListSecurityGroups + + for _, item := range lsg.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemSecurityGroup +// If none was found, returns an empty struct. +func (lsg ListSecurityGroups) FindOne() ItemSecurityGroup { + if len(lsg.Data) == 0 { + return ItemSecurityGroup{} + } + + return lsg.Data[0] +} diff --git a/pkg/cloudbroker/secgroup/filter_test.go b/pkg/cloudbroker/secgroup/filter_test.go new file mode 100644 index 0000000..2231f60 --- /dev/null +++ b/pkg/cloudbroker/secgroup/filter_test.go @@ -0,0 +1,87 @@ +package secgroup + +import "testing" + +var securityGroups = ListSecurityGroups{ + Data: []ItemSecurityGroup{ + { + ID: 1, + AccountID: 1, + Name: "sg1", + Description: "some desc", + CreatedBy: "user", + }, + { + ID: 3, + AccountID: 3, + Name: "sg3", + Description: "some desc", + CreatedBy: "anotheruser", + }, + { + ID: 5, + AccountID: 3, + Name: "sg5", + Description: "some other desc", + CreatedBy: "anotheruser", + UpdatedBy: "user", + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := securityGroups.FilterByID(1).FindOne() + if actual.ID != 1 { + t.Fatal("expected ID 1, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := securityGroups.FilterByName("sg3").FindOne() + if actual.Name != "sg3" { + t.Fatal("expected Name sg3, found: ", actual.Name) + } +} + +func TestFilterByDescription(t *testing.T) { + actual := securityGroups.FilterByDescription("some desc") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Description != "some desc" { + t.Fatal("expected Description 'some desc', found: ", item.Description) + } + } +} + +func TestFilterByAccountID(t *testing.T) { + actual := securityGroups.FilterByAccountID(1).FindOne() + if actual.AccountID != 1 { + t.Fatal("expected AccountID 1, found: ", actual.AccountID) + } +} + +func TestFilterByCreatedBy(t *testing.T) { + actual := securityGroups.FilterByCreatedBy("anotheruser") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.CreatedBy != "anotheruser" { + t.Fatal("expected CreatedBy 'anotheruser', found: ", item.CreatedBy) + } + } +} + +func TestFilterByUpdatedBy(t *testing.T) { + actual := securityGroups.FilterByUpdatedBy("user").FindOne() + if actual.UpdatedBy != "user" { + t.Fatal("expected UpdatedBy 'user', found: ", actual.UpdatedBy) + } +} diff --git a/pkg/cloudbroker/secgroup/get.go b/pkg/cloudbroker/secgroup/get.go new file mode 100644 index 0000000..882312c --- /dev/null +++ b/pkg/cloudbroker/secgroup/get.go @@ -0,0 +1,43 @@ +package secgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type GetRequest struct { + // ID of security group + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` +} + +func (sg SecurityGroup) Get(ctx context.Context, req GetRequest) (*RecordSecurityGroup, error) { + res, err := sg.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordSecurityGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sg SecurityGroup) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/security_group/get" + + res, err := sg.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/secgroup/list.go b/pkg/cloudbroker/secgroup/list.go new file mode 100644 index 0000000..ab7ecac --- /dev/null +++ b/pkg/cloudbroker/secgroup/list.go @@ -0,0 +1,86 @@ +package secgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type ListRequest struct { + // Search by security group id + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Search by account id + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` + + // Search by security group name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Search by security group description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Search by created after time (unix timestamp) + // Required: false + CreatedMin uint64 `url:"created_min,omitempty" json:"created_min,omitempty"` + + // Search by created before time (unix timestamp) + // Required: false + CreatedMax uint64 `url:"created_max,omitempty" json:"created_max,omitempty"` + + // Search by updated after time (unix timestamp) + // Required: false + UpdatedMin uint64 `url:"updated_min,omitempty" json:"updated_min,omitempty"` + + // Search by updated before time (unix timestamp) + // Required: false + UpdatedMax uint64 `url:"updated_max,omitempty" json:"updated_max,omitempty"` + + // Sort by one of supported fields, format ± + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,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"` +} + +// List gets list of security groups as a ListSecurityGroups struct +func (sg SecurityGroup) List(ctx context.Context, req ListRequest) (*ListSecurityGroups, error) { + + res, err := sg.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListSecurityGroups{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of security groups as an array of bytes +func (sg SecurityGroup) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/security_group/list" + + res, err := sg.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/secgroup/models.go b/pkg/cloudbroker/secgroup/models.go new file mode 100644 index 0000000..766d731 --- /dev/null +++ b/pkg/cloudbroker/secgroup/models.go @@ -0,0 +1,94 @@ +package secgroup + +type ListSecurityGroups struct { + // List + Data []ItemSecurityGroup `json:"data"` + + // Entry count + EntryCount uint64 `json:"entryCount"` +} + +type ItemSecurityGroup struct { + // ID of the security group + ID uint64 `json:"id"` + + // Account ID that owns the security group + AccountID uint64 `json:"account_id"` + + // Name of the security group + Name string `json:"name"` + + // Description of the security group + Description string `json:"description"` + + // List of rules + Rules Rules `json:"rules"` + + // Created at + CreatedAt uint64 `json:"created_at"` + + // Updated at + UpdatedAt uint64 `json:"updated_at"` + + // Created by + CreatedBy string `json:"created_by"` + + // Updated by + UpdatedBy string `json:"updated_by"` +} + +type RecordSecurityGroup struct { + // ID of the security group + ID uint64 `json:"id"` + + // Account ID that owns the security group + AccountID uint64 `json:"account_id"` + + // Name of the security group + Name string `json:"name"` + + // Description of the security group + Description string `json:"description"` + + // List of rules + Rules Rules `json:"rules"` + + // Created at + CreatedAt uint64 `json:"created_at"` + + // Updated at + UpdatedAt uint64 `json:"updated_at"` + + // Created by + CreatedBy string `json:"created_by"` + + // Updated by + UpdatedBy string `json:"updated_by"` +} + +type Rules []Rule + +type Rule struct { + // ID of the rule + ID uint64 `json:"id"` + + // Traffic direction (inbound/outbound) + Direction string `json:"direction"` + + // IP protocol version + Ethertype string `json:"ethertype"` + + // Network protocol + Protocol string `json:"protocol"` + + // Start port number (for TCP/UDP) + PortRangeMin uint64 `json:"port_range_min"` + + // End port number (for TCP/UDP) + PortRangeMax uint64 `json:"port_range_max"` + + // Remote IP prefix in CIDR notation + RemoteIPPrefix string `json:"remote_ip_prefix"` + + RemoteGroupID uint64 `json:"remote_group_id"` +} diff --git a/pkg/cloudbroker/secgroup/security_group.go b/pkg/cloudbroker/secgroup/security_group.go new file mode 100644 index 0000000..a5c3e21 --- /dev/null +++ b/pkg/cloudbroker/secgroup/security_group.go @@ -0,0 +1,15 @@ +package secgroup + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + +// Structure for creating request to storage policy +type SecurityGroup struct { + client interfaces.Caller +} + +// Builder for security group endpoint +func New(client interfaces.Caller) *SecurityGroup { + return &SecurityGroup{ + client: client, + } +} diff --git a/pkg/cloudbroker/secgroup/sorting.go b/pkg/cloudbroker/secgroup/sorting.go new file mode 100644 index 0000000..57df52b --- /dev/null +++ b/pkg/cloudbroker/secgroup/sorting.go @@ -0,0 +1,41 @@ +package secgroup + +import "sort" + +// SortByCreatedAt sorts ListSecurityGroups by the CreatedAt field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lsg ListSecurityGroups) SortByCreatedAt(inverse bool) ListSecurityGroups { + if len(lsg.Data) < 2 { + return lsg + } + + sort.Slice(lsg.Data, func(i, j int) bool { + if inverse { + return lsg.Data[i].CreatedAt > lsg.Data[j].CreatedAt + } + + return lsg.Data[i].CreatedAt < lsg.Data[j].CreatedAt + }) + + return lsg +} + +// SortByUpdatedAt sorts ListSecurityGroups by the UpdatedAt field in ascending order. +// +// If inverse param is set to true, the order is reversed. +func (lsg ListSecurityGroups) SortByUpdatedAt(inverse bool) ListSecurityGroups { + if len(lsg.Data) < 2 { + return lsg + } + + sort.Slice(lsg.Data, func(i, j int) bool { + if inverse { + return lsg.Data[i].UpdatedAt > lsg.Data[j].UpdatedAt + } + + return lsg.Data[i].UpdatedAt < lsg.Data[j].UpdatedAt + }) + + return lsg +} diff --git a/pkg/cloudbroker/secgroup/update.go b/pkg/cloudbroker/secgroup/update.go new file mode 100644 index 0000000..ca57bc7 --- /dev/null +++ b/pkg/cloudbroker/secgroup/update.go @@ -0,0 +1,51 @@ +package secgroup + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type UpdateRequest struct { + // Security group ID + // Required: true + SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"` + + // New security group name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New security group description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` +} + +func (sg SecurityGroup) Update(ctx context.Context, req UpdateRequest) (*RecordSecurityGroup, error) { + res, err := sg.UpdateRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordSecurityGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sg SecurityGroup) UpdateRaw(ctx context.Context, req UpdateRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/security_group/update" + + res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/securitygroup.go b/pkg/cloudbroker/securitygroup.go new file mode 100644 index 0000000..295df54 --- /dev/null +++ b/pkg/cloudbroker/securitygroup.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/secgroup" +) + +// Accessing the Security Group method group +func (cb *CloudBroker) SecurityGroup() *secgroup.SecurityGroup { + return secgroup.New(cb.client) +} diff --git a/pkg/cloudbroker/sep.go b/pkg/cloudbroker/sep.go new file mode 100644 index 0000000..e8be9b6 --- /dev/null +++ b/pkg/cloudbroker/sep.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..a03f6c2 --- /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/v15/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..7592759 --- /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/v15/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..73f7348 --- /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/v15/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..e2baf88 --- /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/v15/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..cbf7034 --- /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/v15/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) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/addConsumerNodes" + + 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/add_pool.go b/pkg/cloudbroker/sep/add_pool.go new file mode 100644 index 0000000..547730c --- /dev/null +++ b/pkg/cloudbroker/sep/add_pool.go @@ -0,0 +1,80 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + "strings" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Pool structure which contains fields such as "name", "usage_limit", "types", "system", "accessAccountIds", "accessResGroupIds". Added fields for other pool types: Des, Ovs - "uris" list of "ip, port". + // Dorado, Tatlin - "vdisk_discard". Hitachi - "id", "snapshotable", "snapshot_pool_id", "minLdevId", "maxLdevId", "clone_technology", "vdisk_discard". Shared - "description", "wwns", "allocate_type", "stripes", "metadata_size", "metadatatalun", "vdisk_discard" Local - "description", "node_consumer", "block_disk". + // Required: true + Pool string `url:"pool" json:"pool" validate:"required"` +} + +type wrapperAddPoolRequest struct { + AddPoolRequest + AsyncMode bool `url:"asyncMode"` +} + +// AddPool adds pool to SEP in sync mode. +// It returns result of operation and error. +func (s SEP) AddPool(ctx context.Context, req AddPoolRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperAddPoolRequest{ + AddPoolRequest: req, + AsyncMode: false, + } + + url := "/cloudbroker/sep/addPool" + + 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 +} + +// AddPoolAsync adds pool to SEP in async mode. +// It returns guid of task and error. +func (s SEP) AddPoolAsync(ctx context.Context, req AddPoolRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + reqWrapped := wrapperAddPoolRequest{ + AddPoolRequest: req, + AsyncMode: true, + } + + url := "/cloudbroker/sep/addPool" + + 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/add_provider_nodes.go b/pkg/cloudbroker/sep/add_provider_nodes.go new file mode 100644 index 0000000..5447c91 --- /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/v15/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) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/addProviderNodes" + + 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/config_field_edit.go b/pkg/cloudbroker/sep/config_field_edit.go new file mode 100644 index 0000000..12be6eb --- /dev/null +++ b/pkg/cloudbroker/sep/config_field_edit.go @@ -0,0 +1,58 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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) (*RecordConfigFieldEdit, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/configFieldEdit" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + info := RecordConfigFieldEdit{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/cloudbroker/sep/config_insert.go b/pkg/cloudbroker/sep/config_insert.go new file mode 100644 index 0000000..b89295b --- /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/v15/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..fa3887c --- /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/v15/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..a218ccb --- /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/v15/internal/validators" +) + +// ConsumptionRequest struct to get consumption info +type ConsumptionRequest struct { + // Storage endpoint provider ID + // Required: false + SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"` +} + +// Consumption gets SEP consumption info +func (s SEP) Consumption(ctx context.Context, req ConsumptionRequest) (*ListConsumption, 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 := ListConsumption{} + + 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..e3728e5 --- /dev/null +++ b/pkg/cloudbroker/sep/create.go @@ -0,0 +1,58 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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,sepType"` + + // SEP config + // Required: true + Config string `url:"config" json:"config" validate:"required"` + + // Description + // Required: false + Description string `url:"description,omitempty" json:"description,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..a6cbaac --- /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/v15/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..7a84bca --- /dev/null +++ b/pkg/cloudbroker/sep/del_consumer_nodes.go @@ -0,0 +1,49 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // The force flag must be set to true only if the node will never come back online + // Default: false + // Required: false + Force bool `url:"force" json:"force"` +} + +// DelConsumerNodes excludes consumer nodes from SEP parameters +func (s SEP) DelConsumerNodes(ctx context.Context, req DelConsumerNodesRequest) (interface{}, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/delConsumerNodes" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return nil, err + } + + var result interface{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, 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..9423d8a --- /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/v15/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..c1a7a9c --- /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/v15/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..5b45025 --- /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/v15/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..58a6377 --- /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/v15/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..d570ae3 --- /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/v15/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..54d54c7 --- /dev/null +++ b/pkg/cloudbroker/sep/filter.go @@ -0,0 +1,153 @@ +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] +} + +// FilterBySEPID returns ListAvailableSEP with the specified SEPID. +func (sl ListAvailableSEP) FilterBySEPID(sepID uint64) ListAvailableSEP { + predicate := func(sd SEPData) bool { + return sd.SEPID == sepID + } + + return sl.FilterFunc(predicate) +} + +// FilterBySEPName returns ListAvailableSEP with the specified SEPName. +func (sl ListAvailableSEP) FilterBySEPName(SEPName string) ListAvailableSEP { + predicate := func(sd SEPData) bool { + return sd.SEPName == SEPName + } + + return sl.FilterFunc(predicate) +} + +// FilterBySEPType returns ListAvailableSEP with the specified SEPType. +func (sl ListAvailableSEP) FilterBySEPType(SEPType string) ListAvailableSEP { + predicate := func(sd SEPData) bool { + return sd.SEPType == SEPType + } + + return sl.FilterFunc(predicate) +} + +// FilterByPoolType returns ListAvailableSEP where at least one pool has the specified type. +func (sl ListAvailableSEP) FilterByPoolType(poolType string) ListAvailableSEP { + predicate := func(sd SEPData) bool { + for _, pool := range sd.Pools { + for _, pt := range pool.Types { + if pt == poolType { + return true + } + } + } + return false + } + + return sl.FilterFunc(predicate) +} + +// FilterBySystemPool returns ListAvailableSEP where at least one pool is a system pool. +func (sl ListAvailableSEP) FilterBySystemPool(isSystem bool) ListAvailableSEP { + predicate := func(sd SEPData) bool { + for _, pool := range sd.Pools { + if pool.System == isSystem { + return true + } + } + return false + } + + return sl.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListAvailableSEP based on a user-defined predicate. +func (sl ListAvailableSEP) FilterFunc(predicate func(SEPData) bool) ListAvailableSEP { + var result ListAvailableSEP + + for _, item := range sl.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns the first found SEPData. +// If nothing is found, returns an empty struct. +func (sl ListAvailableSEP) FindOne() SEPData { + if len(sl.Data) == 0 { + return SEPData{} + } + + return sl.Data[0] +} diff --git a/pkg/cloudbroker/sep/filter_test.go b/pkg/cloudbroker/sep/filter_test.go new file mode 100644 index 0000000..008c92a --- /dev/null +++ b/pkg/cloudbroker/sep/filter_test.go @@ -0,0 +1,282 @@ +package sep + +import "testing" + +var seps = ListSEP{ + Data: []RecordSEP{ + { + + 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", + }, + { + + 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", + }, + { + + 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)) + } + } +} + +var availableSeps = ListAvailableSEP{ + EntryCount: 3, + Data: []SEPData{ + { + SEPID: 1, + SEPName: "sep_1", + SEPType: "TATLIN", + Pools: []Pool{ + { + Name: "pool_1", + Types: []string{"DES"}, + System: false, + }, + }, + }, + { + SEPID: 2, + SEPName: "sep_2", + SEPType: "SHARED", + Pools: []Pool{ + { + Name: "pool_2", + Types: []string{"DES"}, + System: true, + }, + }, + }, + { + SEPID: 3, + SEPName: "sep_3", + SEPType: "DES", + Pools: []Pool{ + { + Name: "pool_3", + Types: []string{"DES"}, + System: false, + }, + }, + }, + }, +} + +func TestFilterBySEPID(t *testing.T) { + actual := availableSeps.FilterBySEPID(1).FindOne() + + if actual.SEPID != 1 { + t.Fatal("expected SEPID 1, found: ", actual.SEPID) + } +} + +func TestFilterBySEPName(t *testing.T) { + actual := availableSeps.FilterBySEPName("sep_2").FindOne() + + if actual.SEPName != "sep_2" { + t.Fatal("expected SEPName 'sep_2', found: ", actual.SEPName) + } +} + +func TestFilterBySEPType(t *testing.T) { + actual := availableSeps.FilterBySEPType("TATLIN").FindOne() + + if actual.SEPType != "TATLIN" { + t.Fatal("expected SEPType 'TATLIN', found: ", actual.SEPType) + } +} + +func TestFilterByPoolType(t *testing.T) { + actual := availableSeps.FilterByPoolType("DES") + + if len(actual.Data) != 3 { + t.Fatal("expected 3 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + found := false + for _, pool := range item.Pools { + for _, poolType := range pool.Types { + if poolType == "DES" { + found = true + break + } + } + if found { + break + } + } + if !found { + t.Fatal("expected Pool type 'DES', not found in SEP: ", item.SEPID) + } + } +} + +func TestFilterBySystemPool(t *testing.T) { + actual := availableSeps.FilterBySystemPool(true) + + if len(actual.Data) != 1 { + t.Fatal("expected 1 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + found := false + for _, pool := range item.Pools { + if pool.System { + found = true + break + } + } + if !found { + t.Fatal("expected System pool, not found in SEP: ", item.SEPID) + } + } +} diff --git a/pkg/cloudbroker/sep/get.go b/pkg/cloudbroker/sep/get.go new file mode 100644 index 0000000..055ec5f --- /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/v15/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..1650538 --- /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/v15/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..d6e29c1 --- /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/v15/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/get_template.go b/pkg/cloudbroker/sep/get_template.go new file mode 100644 index 0000000..268db6f --- /dev/null +++ b/pkg/cloudbroker/sep/get_template.go @@ -0,0 +1,36 @@ +package sep + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetTemplateRequest struct to get template of SEP +type GetTemplateRequest struct { + // Type of SEP + // Required: true + SepType string `url:"sep_type" json:"sep_type" validate:"required,sepType"` + + // Language of template + // Required: true + Language string `url:"lang" json:"lang" validate:"required,language"` +} + +// GetTemplate gets data to sep template +func (s SEP) GetTemplate(ctx context.Context, req GetTemplateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/getTemplate" + + res, err := s.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), 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..f019002 --- /dev/null +++ b/pkg/cloudbroker/sep/list.go @@ -0,0 +1,87 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Sort by SEP IDs + SepIDs []uint64 `url:"sep_ids,omitempty" json:"sep_ids,omitempty"` + + // 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/list_available_sep_and_pools.go b/pkg/cloudbroker/sep/list_available_sep_and_pools.go new file mode 100644 index 0000000..954adc6 --- /dev/null +++ b/pkg/cloudbroker/sep/list_available_sep_and_pools.go @@ -0,0 +1,51 @@ +package sep + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListAvailableSEPAndPoolsRequest struct to get dict with entry count and list of dict with SEPs and pools details accessible by the Account and RG +type ListAvailableSEPAndPoolsRequest struct { + // Account ID + // Required: true + AccountID uint64 `url:"account_id" json:"account_id" validate:"required"` + + // RG ID + // Required: false + RGID uint64 `url:"rg_id,omitempty" json:"rg_id,omitempty"` +} + +// ListAvailableSEPAndPools get dict with entry count and list of dict with SEPs and pools details accessible by the Account and RG +func (s SEP) ListAvailableSEPAndPools(ctx context.Context, req ListAvailableSEPAndPoolsRequest) (*ListAvailableSEP, error) { + res, err := s.ListAvailableSEPAndPoolsRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListAvailableSEP{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list as an array of bytes +func (s SEP) ListAvailableSEPAndPoolsRaw(ctx context.Context, req ListAvailableSEPAndPoolsRequest) ([]byte, error) { + + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/listAvailableSepAndPools" + + 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..4127214 --- /dev/null +++ b/pkg/cloudbroker/sep/models.go @@ -0,0 +1,296 @@ +package sep + +import "encoding/json" + +// 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"` +} + +// List of Resource groups +type ListConsumption struct { + // Data + Data []RecordConsumption `json:"data"` + + // Enrtry count + EntryCount uint64 `json:"entryCount"` +} + +// Main information about consumption +type RecordConsumption struct { + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + // By pool + ByPool map[string]ByPool `json:"byPool"` + + // Total resource information + Total Total `json:"total"` + + // Type + Type string `json:"type"` + + // Consumed by + ConsumedBy []uint64 `json:"consumedBy"` +} + +// 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 { + // 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"` + + // MultipathNum + MultipathNum uint64 `json:"multipathNum"` + + // 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"` +} + +type Pool struct { + // Pool name + Name string `json:"name"` + + // Pool types + Types []string `json:"types"` + + // System + System bool `json:"system"` +} + +type SEPData struct { + // SEP ID + SEPID uint64 `json:"sepId"` + + // SEP name + SEPName string `json:"sepName"` + + // Sep type + SEPType string `json:"sepType"` + + // Pools + Pools []Pool `json:"pools"` +} + +type ListAvailableSEP struct { + // Entry count + EntryCount uint64 `json:"entryCount"` + + // Data + Data []SEPData `json:"data"` +} + +// Disk clean settings +type DiskCleanSettings struct { + // Block size + BlockSize string `json:"blocksize"` + + // Write bytes per second + WBPS uint64 `json:"wbps"` + + // Write I/O operations per second + WIOPS uint64 `json:"wiops"` +} + +// Pool configuration +type PoolConfig struct { + // Disk clean + DiskClean *string `json:"disk_clean"` + + // Disk clean settings + DiskCleanSettings DiskCleanSettings `json:"disk_clean_settings"` + + // Pool name + Name string `json:"name"` + + // Usage limit + UsageLimit uint64 `json:"usage_limit"` + + // Additional properties + AdditionalProperties map[string]interface{} `json:"-"` +} + +func (p *PoolConfig) UnmarshalJSON(data []byte) error { + type tmpAlias PoolConfig + if err := json.Unmarshal(data, (*tmpAlias)(p)); err != nil { + return err + } + + all := make(map[string]interface{}) + if err := json.Unmarshal(data, &all); err != nil { + return err + } + + delete(all, "disk_clean") + delete(all, "disk_clean_settings") + delete(all, "name") + delete(all, "usage_limit") + + if len(all) > 0 { + p.AdditionalProperties = all + } + + return nil +} + +// Sep configuration information +type RecordConfigFieldEdit struct { + // Format + Format string `json:"format"` + + // Pools + Pools []PoolConfig `json:"pools"` + + // Protocol + Protocol string `json:"protocol"` + + // Additional properties + AdditionalProperties map[string]interface{} `json:"-"` +} + +func (r *RecordConfigFieldEdit) UnmarshalJSON(data []byte) error { + type tmpAlias RecordConfigFieldEdit + if err := json.Unmarshal(data, (*tmpAlias)(r)); err != nil { + return err + } + + all := make(map[string]interface{}) + if err := json.Unmarshal(data, &all); err != nil { + return err + } + + delete(all, "format") + delete(all, "pools") + delete(all, "protocol") + + if len(all) > 0 { + r.AdditionalProperties = all + } + + return nil +} diff --git a/pkg/cloudbroker/sep/sep.go b/pkg/cloudbroker/sep/sep.go new file mode 100644 index 0000000..d5a115d --- /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/v15/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..89c1c44 --- /dev/null +++ b/pkg/cloudbroker/sep/serialize.go @@ -0,0 +1,79 @@ +package sep + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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) +} + +// 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 ListAvailableSEP) 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 SEPData) 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..3863568 --- /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/v15/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..89a3f28 --- /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/v15/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.go b/pkg/cloudbroker/sep/update.go new file mode 100644 index 0000000..1bb00e0 --- /dev/null +++ b/pkg/cloudbroker/sep/update.go @@ -0,0 +1,48 @@ +package sep + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update SEP object +type UpdateRequest struct { + // ID of SEP to update + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required,min=1"` + + // New SEP name + // Required: false + // Default: null + SEPName string `url:"name,omitempty" json:"name,omitempty" validate:"omitempty,min=1,max=256,sepName"` + + // New description + // Required: false + // Default: null + Description string `url:"description,omitempty" json:"description,omitempty" validate:"omitempty,max=4096,sepDescription"` +} + +// Update updates SEP object +func (s SEP) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/sep/update" + + 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/storage_policy.go b/pkg/cloudbroker/storage_policy.go new file mode 100644 index 0000000..1edab13 --- /dev/null +++ b/pkg/cloudbroker/storage_policy.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/stpolicy" +) + +// Accessing the Storage Policy method group +func (cb *CloudBroker) StPolicy() *stpolicy.StPolicy { + return stpolicy.New(cb.client) +} diff --git a/pkg/cloudbroker/stpolicy/add_pool.go b/pkg/cloudbroker/stpolicy/add_pool.go new file mode 100644 index 0000000..30a6acd --- /dev/null +++ b/pkg/cloudbroker/stpolicy/add_pool.go @@ -0,0 +1,51 @@ +package stpolicy + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type AddPoolRequest struct { + // ID of storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // Storage endpoint provider ID to add + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Pool to add + // Required: true + PoolName string `url:"pool_name" json:"pool_name" validate:"required"` +} + +func (sp StPolicy) AddPool(ctx context.Context, req AddPoolRequest) (*InfoStoragePolicyWithID, error) { + res, err := sp.AddPoolRaw(ctx, req) + if err != nil { + return nil, err + } + + info := InfoStoragePolicyWithID{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sp StPolicy) AddPoolRaw(ctx context.Context, req AddPoolRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/add_pool" + + res, err := sp.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/stpolicy/create.go b/pkg/cloudbroker/stpolicy/create.go new file mode 100644 index 0000000..8575910 --- /dev/null +++ b/pkg/cloudbroker/stpolicy/create.go @@ -0,0 +1,63 @@ +package stpolicy + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type AccessSEPsPool struct { + + // Storage endpoint provider ID + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Names of pools + // Required: true + PoolNames []string `url:"pool_names" json:"pool_names" validate:"required"` +} + +type CreateRequest struct { + // Name of the storage policy + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // List of storage endpoint access objects + // Required: true + AccessSEPsPools []AccessSEPsPool `url:"access_seps_pools" json:"access_seps_pools" validate:"required"` + + // Description of the storage policy + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Maximum IOPS limit + // Default: 2000 + // Required: false + LimitIOPS uint64 `url:"limit_iops,omitempty" json:"limit_iops,omitempty"` +} + +// Create creates a new storage policy +func (sp StPolicy) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/create" + + res, err := sp.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, 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/stpolicy/delete.go b/pkg/cloudbroker/stpolicy/delete.go new file mode 100644 index 0000000..748032a --- /dev/null +++ b/pkg/cloudbroker/stpolicy/delete.go @@ -0,0 +1,36 @@ +package stpolicy + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type DeleteRequest struct { + // ID of storage policy to delete + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +func (sp StPolicy) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/delete" + + res, err := sp.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/stpolicy/delete_pool.go b/pkg/cloudbroker/stpolicy/delete_pool.go new file mode 100644 index 0000000..1091e59 --- /dev/null +++ b/pkg/cloudbroker/stpolicy/delete_pool.go @@ -0,0 +1,51 @@ +package stpolicy + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type DeletePoolRequest struct { + // ID of storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // Storage endpoint provider ID to delete + // Required: true + SEPID uint64 `url:"sep_id" json:"sep_id" validate:"required"` + + // Pool to delete + // Required: true + PoolName string `url:"pool_name" json:"pool_name" validate:"required"` +} + +func (sp StPolicy) DeletePool(ctx context.Context, req DeletePoolRequest) (*InfoStoragePolicyWithID, error) { + res, err := sp.DeletePoolRaw(ctx, req) + if err != nil { + return nil, err + } + + info := InfoStoragePolicyWithID{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sp StPolicy) DeletePoolRaw(ctx context.Context, req DeletePoolRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/delete_pool" + + res, err := sp.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/stpolicy/disable.go b/pkg/cloudbroker/stpolicy/disable.go new file mode 100644 index 0000000..09c4ffe --- /dev/null +++ b/pkg/cloudbroker/stpolicy/disable.go @@ -0,0 +1,36 @@ +package stpolicy + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type DisableRequest struct { + // ID of storage policy to disable + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +func (sp StPolicy) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/disable" + + res, err := sp.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/stpolicy/enable.go b/pkg/cloudbroker/stpolicy/enable.go new file mode 100644 index 0000000..8672c98 --- /dev/null +++ b/pkg/cloudbroker/stpolicy/enable.go @@ -0,0 +1,36 @@ +package stpolicy + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type EnableRequest struct { + // ID of storage policy to enable + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +func (sp StPolicy) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/enable" + + res, err := sp.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/stpolicy/filter.go b/pkg/cloudbroker/stpolicy/filter.go new file mode 100644 index 0000000..25599e2 --- /dev/null +++ b/pkg/cloudbroker/stpolicy/filter.go @@ -0,0 +1,71 @@ +package stpolicy + +// FilterByID returns ListStoragePolicies with specified ID. +func (lsp ListStoragePolicies) FilterByID(id uint64) ListStoragePolicies { + predicate := func(isp ItemStoragePolicy) bool { + return isp.ID == id + } + + return lsp.FilterFunc(predicate) +} + +// FilterByName returns ListStoragePolicies with specified Name. +func (lsp ListStoragePolicies) FilterByName(name string) ListStoragePolicies { + predicate := func(isp ItemStoragePolicy) bool { + return isp.Name == name + } + + return lsp.FilterFunc(predicate) +} + +// FilterByStatus returns ListStoragePolicies with specified Status. +func (lsp ListStoragePolicies) FilterByStatus(status string) ListStoragePolicies { + predicate := func(isp ItemStoragePolicy) bool { + return isp.Status == status + } + + return lsp.FilterFunc(predicate) +} + +// FilterByStatus returns ListStoragePolicies with specified Desc. +func (lsp ListStoragePolicies) FilterByDesc(desc string) ListStoragePolicies { + predicate := func(isp ItemStoragePolicy) bool { + return isp.Description == desc + } + + return lsp.FilterFunc(predicate) +} + +// FilterByLimitIOPS returns ListStoragePolicies with specified IOPS. +func (lsp ListStoragePolicies) FilterByLimitIOPS(limitIOPS uint64) ListStoragePolicies { + predicate := func(isp ItemStoragePolicy) bool { + return isp.LimitIOPS == limitIOPS + } + + return lsp.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListStoragePolicies based on a user-specified predicate. +func (lsp ListStoragePolicies) FilterFunc(predicate func(ItemStoragePolicy) bool) ListStoragePolicies { + var result ListStoragePolicies + + for _, item := range lsp.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemStoragePolicy +// If none was found, returns an empty struct. +func (lsp ListStoragePolicies) FindOne() ItemStoragePolicy { + if len(lsp.Data) == 0 { + return ItemStoragePolicy{} + } + + return lsp.Data[0] +} diff --git a/pkg/cloudbroker/stpolicy/filter_test.go b/pkg/cloudbroker/stpolicy/filter_test.go new file mode 100644 index 0000000..4eacea4 --- /dev/null +++ b/pkg/cloudbroker/stpolicy/filter_test.go @@ -0,0 +1,110 @@ +package stpolicy + +import "testing" + +var asp9 = AccessSEPPool{ + SEPID: 9, + PoolNames: []string{"data03"}, +} + +var asp7 = AccessSEPPool{ + SEPID: 7, + PoolNames: []string{"pool_QA"}, +} + +var asp8 = AccessSEPPool{ + SEPID: 8, + PoolNames: []string{ + "alpha_pool_block", + "alpha_pool_stripe", + "alpha_pool_file", + }, +} + +var storagePolicyItems = ListStoragePolicies{ + Data: []ItemStoragePolicy{ + { + ID: 1, + GUID: 1, + Name: "storagePolicy01", + Description: "desc", + AccessSEPPools: ListAccessSEPPools{asp7}, + Status: "ENABLED", + LimitIOPS: 2000, + Usage: Usage{}, + }, + { + ID: 3, + GUID: 3, + Name: "storagePolicy03", + Description: "desc", + AccessSEPPools: ListAccessSEPPools{asp7, asp8, asp9}, + Status: "ENABLED", + LimitIOPS: 2500, + Usage: Usage{}, + }, + { + ID: 5, + GUID: 5, + Name: "storagePolicy05", + Description: "another desc", + AccessSEPPools: ListAccessSEPPools{asp8, asp9}, + Status: "DISABLED", + LimitIOPS: 2000, + Usage: Usage{}, + }, + }, + EntryCount: 3, +} + +func TestFilterByID(t *testing.T) { + actual := storagePolicyItems.FilterByID(1).FindOne() + + if actual.ID != 1 { + t.Fatal("expected 1 ID, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + actual := storagePolicyItems.FilterByName("storagePolicy01").FindOne() + + if actual.Name != "storagePolicy01" { + t.Fatal("expected Name 'storagePolicy01', found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := storagePolicyItems.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 TestFilterByDesc(t *testing.T) { + actual := storagePolicyItems.FilterByDesc("desc") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Description != "desc" { + t.Fatal("expected Description 'desc', found: ", item.Status) + } + } +} + +func TestFilterByLimitIOPS(t *testing.T) { + actual := storagePolicyItems.FilterByLimitIOPS(2500).FindOne() + + if actual.LimitIOPS != 2500 { + t.Fatal("expected LimitIOPS '2500', found: ", actual.LimitIOPS) + } +} diff --git a/pkg/cloudbroker/stpolicy/get.go b/pkg/cloudbroker/stpolicy/get.go new file mode 100644 index 0000000..0645bae --- /dev/null +++ b/pkg/cloudbroker/stpolicy/get.go @@ -0,0 +1,43 @@ +package stpolicy + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type GetRequest struct { + // ID of storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` +} + +func (sp StPolicy) Get(ctx context.Context, req GetRequest) (*InfoStoragePolicy, error) { + res, err := sp.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := InfoStoragePolicy{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sp StPolicy) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/get" + + res, err := sp.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/stpolicy/list.go b/pkg/cloudbroker/stpolicy/list.go new file mode 100644 index 0000000..0c3f011 --- /dev/null +++ b/pkg/cloudbroker/stpolicy/list.go @@ -0,0 +1,94 @@ +package stpolicy + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +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"` + + // Search by storage policy ID + // Required: false + ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` + + // Search by storage policy name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Search by storage policy status + // Required: false + Status string `url:"status,omitempty" json:"status,omitempty"` + + // Search by storage policy desc + // Required: false + Desc string `url:"desc,omitempty" json:"desc,omitempty"` + + // Search by storage policy iops limit + // Required: false + LimitIOPS uint64 `url:"limit_iops,omitempty" json:"limit_iops,omitempty"` + + // Sort by one of supported fields, format ± + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // ID of account ID + // Required: false + AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"` + + // Search by resgroup id + // Required: false + ResgroupID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"` + + // Search by sep id + // Required: false + SepID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"` + + // Search by pool name + // Required: false + PoolName string `url:"pool_name,omitempty" json:"pool_name,omitempty"` + + // Filter SEP's by tech status (ENABLED, DISABLED) + // Required: false + SepTechStatus string `url:"sep_tech_status,omitempty" json:"sep_tech_status,omitempty" validate:"omitempty,sepTechStatus"` +} + +// List gets list of storage policies as a ListStoragePolicies struct +func (sp StPolicy) List(ctx context.Context, req ListRequest) (*ListStoragePolicies, error) { + + res, err := sp.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListStoragePolicies{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of storage policies as an array of bytes +func (sp StPolicy) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/list" + + res, err := sp.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/stpolicy/models.go b/pkg/cloudbroker/stpolicy/models.go new file mode 100644 index 0000000..88f2511 --- /dev/null +++ b/pkg/cloudbroker/stpolicy/models.go @@ -0,0 +1,117 @@ +package stpolicy + +type InfoStoragePolicyWithID struct { + // ID of the storage policy + ID uint64 `json:"id"` + + // GUID + GUID uint64 `json:"guid"` + + // Name of the storage policy + Name string `json:"name"` + + // Description of the storage policy + Description string `json:"description"` + + // List of pools in SEP for storage policy + AccessSEPPools ListAccessSEPPools `json:"access_seps_pools"` + + // Status of the storage policy + Status string `json:"status"` + + // Max IOPS for the sotrage policy + LimitIOPS uint64 `json:"limit_iops"` + + // Which accounts and resource groups use the storage policy + Usage Usage `json:"usage"` +} + +type ListStoragePolicies struct { + // List + Data []ItemStoragePolicy `json:"data"` + + // Entry Count + EntryCount uint64 `json:"entryCount"` +} + +type ItemStoragePolicy struct { + // ID of the storage policy + ID uint64 `json:"id"` + + // GUID + GUID uint64 `json:"guid"` + + // Name of the storage policy + Name string `json:"name"` + + // Description of the storage policy + Description string `json:"description"` + + // List of pools in SEP for storage policy + AccessSEPPools ListAccessSEPPools `json:"access_seps_pools"` + + // Status of the storage policy + Status string `json:"status"` + + // Max IOPS for the sotrage policy + LimitIOPS uint64 `json:"limit_iops"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Which accounts and resource groups use the storage policy + Usage Usage `json:"usage"` +} + +type InfoStoragePolicy struct { + // ID of the storage policy + ID uint64 `json:"id"` + + // GUID + GUID uint64 `json:"guid"` + + // Name of the storage policy + Name string `json:"name"` + + // Description of the storage policy + Description string `json:"description"` + + // List of pools in SEP for storage policy + AccessSEPPools ListAccessSEPPools `json:"access_seps_pools"` + + // Status of the storage policy + Status string `json:"status"` + + // Max IOPS for the storage policy + LimitIOPS uint64 `json:"limit_iops"` + + // Storage policy ID + StoragePolicyID uint64 `json:"storage_policy_id"` + + // Which accounts and resource groups use the storage policy + Usage Usage `json:"usage"` +} + +type ListAccessSEPPools []AccessSEPPool + +type AccessSEPPool struct { + // SEP ID + SEPID uint64 `json:"sep_id"` + + // SEP name + Name string `json:"sep_name"` + + // Pool names + PoolNames []string `json:"pool_names"` + + // Technical status of the SEP + SepTechStatus string `json:"sep_tech_status"` +} + +type Usage struct { + // List of accounts + Accounts []uint64 `json:"accounts"` + + // List of resource groups + Resgroups []uint64 `json:"resgroups"` +} diff --git a/pkg/cloudbroker/stpolicy/storage_policy.go b/pkg/cloudbroker/stpolicy/storage_policy.go new file mode 100644 index 0000000..3d3f57a --- /dev/null +++ b/pkg/cloudbroker/stpolicy/storage_policy.go @@ -0,0 +1,15 @@ +package stpolicy + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + +// Structure for creating request to storage policy +type StPolicy struct { + client interfaces.Caller +} + +// Builder for storage policy endpoint +func New(client interfaces.Caller) *StPolicy { + return &StPolicy{ + client: client, + } +} diff --git a/pkg/cloudbroker/stpolicy/update.go b/pkg/cloudbroker/stpolicy/update.go new file mode 100644 index 0000000..58d28c4 --- /dev/null +++ b/pkg/cloudbroker/stpolicy/update.go @@ -0,0 +1,61 @@ +package stpolicy + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type UpdateRequest struct { + // ID of storage policy + // Required: true + StoragePolicyID uint64 `url:"storage_policy_id" json:"storage_policy_id" validate:"required"` + + // List of storage endpoint access objects + // Required: false + AccessSEPsPools []AccessSEPsPool `url:"access_seps_pools,omitempty" json:"access_seps_pools,omitempty"` + + // New name for the storage policy + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // New value for limit IOPS + // Required: false + LimitIOPS uint64 `url:"limit_iops,omitempty" json:"limit_iops,omitempty"` + + // New description of the storage policy + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` +} + +// Update updates storage policy +func (sp StPolicy) Update(ctx context.Context, req UpdateRequest) (*InfoStoragePolicyWithID, error) { + res, err := sp.UpdateRaw(ctx, req) + if err != nil { + return nil, err + } + + info := InfoStoragePolicyWithID{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +func (sp StPolicy) UpdateRaw(ctx context.Context, req UpdateRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/storage_policy/update" + + res, err := sp.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + return res, err +} diff --git a/pkg/cloudbroker/tasks.go b/pkg/cloudbroker/tasks.go new file mode 100644 index 0000000..abc3ab5 --- /dev/null +++ b/pkg/cloudbroker/tasks.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..fa3fd19 --- /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/v15/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..38e4c3a --- /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/v15/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..43b7c17 --- /dev/null +++ b/pkg/cloudbroker/tasks/models.go @@ -0,0 +1,147 @@ +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"` + + // 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..b0e5b2a --- /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/v15/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/trunk.go b/pkg/cloudbroker/trunk.go new file mode 100644 index 0000000..e52389d --- /dev/null +++ b/pkg/cloudbroker/trunk.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/trunk" +) + +// Accessing the Trunk method group +func (cb *CloudBroker) Trunk() *trunk.Trunk { + return trunk.New(cb.client) +} diff --git a/pkg/cloudbroker/trunk/access_grant.go b/pkg/cloudbroker/trunk/access_grant.go new file mode 100644 index 0000000..5883db1 --- /dev/null +++ b/pkg/cloudbroker/trunk/access_grant.go @@ -0,0 +1,42 @@ +package trunk + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AccessGrant struct to grant access to a trunk to some accounts +type AccessGrantRequest struct { + // ID of the trunk to disable + // Required: true + TrunkID uint64 `url:"id" json:"id" validate:"required"` + + // IDs of the accounts to grant access to + // Required: true + AccountIDs []uint64 `url:"account_ids" json:"account_ids" validate:"required"` +} + +// AccessGrant grants access to a trunk +func (t Trunk) AccessGrant(ctx context.Context, req AccessGrantRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/access_grant" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/trunk/access_revoke.go b/pkg/cloudbroker/trunk/access_revoke.go new file mode 100644 index 0000000..ccb09db --- /dev/null +++ b/pkg/cloudbroker/trunk/access_revoke.go @@ -0,0 +1,42 @@ +package trunk + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AccessRevoke struct to grant access to a trunk to some accounts +type AccessRevokeRequest struct { + // ID of the trunk to disable + // Required: true + TrunkID uint64 `url:"id" json:"id" validate:"required"` + + // IDs of the accounts to revoke access from + // Required: true + AccountIDs []uint64 `url:"account_ids" json:"account_ids" validate:"required"` +} + +// AccessRevoke revokes access to a trunk from accounts +func (t Trunk) AccessRevoke(ctx context.Context, req AccessRevokeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/access_revoke" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/trunk/create.go b/pkg/cloudbroker/trunk/create.go new file mode 100644 index 0000000..2e3e718 --- /dev/null +++ b/pkg/cloudbroker/trunk/create.go @@ -0,0 +1,63 @@ +package trunk + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create a trunk +type CreateRequest struct { + // Name of the trunk + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // List of trunk tags (values between 1-4095) + // Required: true + TrunkTags string `url:"trunk_tags" json:"trunk_tags" validate:"required,trunkTags"` + + // OVS bridge name + // Required: true + OVSBridge string `url:"ovs_bridge" json:"ovs_bridge" validate:"required"` + + // Description of the trunk + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // List of account IDs with access to this trunk + // Required: false + AccountIDs []uint64 `url:"account_ids,omitempty" json:"account_ids,omitempty"` + + // Native VLAN ID + // Required: false + NativeVLANID uint64 `url:"native_vlan_id,omitempty" json:"native_vlan_id,omitempty"` + + // MTU + // Default value: 1500 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty"` +} + +// Create creates a user. +func (t Trunk) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/create" + + res, err := t.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/trunk/destroy.go b/pkg/cloudbroker/trunk/destroy.go new file mode 100644 index 0000000..27de09b --- /dev/null +++ b/pkg/cloudbroker/trunk/destroy.go @@ -0,0 +1,38 @@ +package trunk + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DestroyRequest struct to destroy a trunk +type DestroyRequest struct { + // ID of the trunk to destroy + // Required: true + TrunkID uint64 `url:"id" json:"id" validate:"required"` +} + +// Destroy destroys a trunk +func (t Trunk) Destroy(ctx context.Context, req DestroyRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/destroy" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/trunk/disable.go b/pkg/cloudbroker/trunk/disable.go new file mode 100644 index 0000000..e648988 --- /dev/null +++ b/pkg/cloudbroker/trunk/disable.go @@ -0,0 +1,38 @@ +package trunk + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DisableRequest struct to disable a trunk +type DisableRequest struct { + // ID of the trunk to disable + // Required: true + TrunkID uint64 `url:"id" json:"id" validate:"required"` +} + +// Disable disables a trunk by ID +func (t Trunk) Disable(ctx context.Context, req DisableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/disable" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/trunk/enable.go b/pkg/cloudbroker/trunk/enable.go new file mode 100644 index 0000000..4ae5353 --- /dev/null +++ b/pkg/cloudbroker/trunk/enable.go @@ -0,0 +1,38 @@ +package trunk + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// EnableRequest struct to enable a trunk +type EnableRequest struct { + // ID of the trunk to enable + // Required: true + TrunkID uint64 `url:"id" json:"id" validate:"required"` +} + +// Enable enables a trunk +func (t Trunk) Enable(ctx context.Context, req EnableRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/enable" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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/trunk/get.go b/pkg/cloudbroker/trunk/get.go new file mode 100644 index 0000000..6f12040 --- /dev/null +++ b/pkg/cloudbroker/trunk/get.go @@ -0,0 +1,46 @@ +package trunk + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about a trunk +type GetRequest struct { + // ID of trunk + // Required: true + TrunkID uint64 `url:"id" json:"id" validate:"required"` +} + +// Get gets detailed information about a trunk as a ItemTrunk struct +func (t Trunk) Get(ctx context.Context, req GetRequest) (*ItemTrunk, error) { + res, err := t.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ItemTrunk{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed information about a trunk as an array of bytes +func (t Trunk) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/get" + + res, err := t.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/trunk/list.go b/pkg/cloudbroker/trunk/list.go new file mode 100644 index 0000000..419b96c --- /dev/null +++ b/pkg/cloudbroker/trunk/list.go @@ -0,0 +1,64 @@ +package trunk + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get list of trunks +type ListRequest struct { + // Account access ID to filter by + AccountIDs []uint64 `url:"account_ids,omitempty" json:"account_ids,omitempty"` + + // ID of the trunk to filter by + IDs []uint64 `url:"ids,omitempty" json:"ids,omitempty"` + + // Sort by one of supported fields, format ± + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Trunk tags to filter by + TrunkTags string `url:"trunk_tags,omitempty" json:"trunk_tags,omitempty" validate:"omitempty,trunkTags"` + + // Page number + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Page size + Size uint64 `url:"size,omitempty" json:"size,omitempty"` + + // Status + Status string `url:"status,omitempty" json:"status,omitempty"` +} + +// List gets list of all trunks as a ListTrunks struct +func (t Trunk) List(ctx context.Context, req ListRequest) (*ListTrunks, error) { + + res, err := t.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListTrunks{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all trunks as an array of bytes +func (t Trunk) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/list" + + res, err := t.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/trunk/models.go b/pkg/cloudbroker/trunk/models.go new file mode 100644 index 0000000..cc42a96 --- /dev/null +++ b/pkg/cloudbroker/trunk/models.go @@ -0,0 +1,62 @@ +package trunk + +type ItemTrunk struct { + + // List of account IDs with access to this trunk + AccountIDs []uint64 `json:"accountIds"` + + // Created at + CreatedAt uint64 `json:"created_at"` + + // Created by + CreatedBy string `json:"created_by"` + + // Deleted at + DeletedAt uint64 `json:"deleted_at"` + + // Deleted by + DeletedBy string `json:"deleted_by"` + + // Description of a trunk + Description string `json:"description"` + + // GUID + GUID uint64 `json:"guid"` + + // ID of a trunk + ID uint64 `json:"id"` + + // MAC + MAC string `json:"mac"` + + // MTU + MTU uint64 `json:"mtu"` + + // Name of a trunk + Name string `json:"name"` + + // Native VLAN ID + NativeVLANID uint64 `json:"nativeVlanId"` + + // OVS bridge name + OVSBridge string `json:"ovsBridge"` + + // If the trunk is enabled + Status string `json:"status"` + + // List of trunk tags (values between 1-4095) + TrunkTags string `json:"trunkTags"` + + // Updated at + UpdatedAt uint64 `json:"updated_at"` + + // Updated by + UpdatedBy string `json:"updated_by"` +} + +// List of trunks +type ListTrunks struct { + Data []ItemTrunk `json:"data"` + + EntryCount uint64 `json:"entryCount"` +} diff --git a/pkg/cloudbroker/trunk/trunk.go b/pkg/cloudbroker/trunk/trunk.go new file mode 100644 index 0000000..d0b4305 --- /dev/null +++ b/pkg/cloudbroker/trunk/trunk.go @@ -0,0 +1,17 @@ +package trunk + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to trunk +type Trunk struct { + client interfaces.Caller +} + +// Builder for trunk endpoints +func New(client interfaces.Caller) *Trunk { + return &Trunk{ + client, + } +} diff --git a/pkg/cloudbroker/trunk/update.go b/pkg/cloudbroker/trunk/update.go new file mode 100644 index 0000000..c1dcc70 --- /dev/null +++ b/pkg/cloudbroker/trunk/update.go @@ -0,0 +1,59 @@ +package trunk + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update a trunk +type UpdateRequest struct { + // ID of the trunk to update + // Required: true + TrunkID uint64 `url:"id" json:"id" validate:"required"` + + // New name of the trunk + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // List of trunk tags (values between 1-4095) + // Required: false + TrunkTags string `url:"trunk_tags,omitempty" json:"trunk_tags,omitempty" validate:"omitempty,trunkTags"` + + // New description of the trunk + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // New native VLAN ID + // Required: false + NativeVLANID uint64 `url:"native_vlan_id,omitempty" json:"native_vlan_id,omitempty"` + + // MTU + // Default value: 1500 + // Required: false + MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty"` +} + +// Update updates a trunk +func (t Trunk) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/trunk/update" + + res, err := t.client.DecortApiCall(ctx, http.MethodPost, url, req) + if 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.go b/pkg/cloudbroker/user.go new file mode 100644 index 0000000..dce9e27 --- /dev/null +++ b/pkg/cloudbroker/user.go @@ -0,0 +1,7 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..3f6a02c --- /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/v15/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..cfa5fde --- /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/v15/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..29ec9f2 --- /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/v15/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..1936a81 --- /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/v15/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/block.go b/pkg/cloudbroker/user/block.go new file mode 100644 index 0000000..63847c0 --- /dev/null +++ b/pkg/cloudbroker/user/block.go @@ -0,0 +1,38 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// BlockRequest struct to block a user. +type BlockRequest struct { + // ID of the user to block. + // Required: true + UserID string `url:"user_id" json:"user_id" validate:"required"` +} + +// Block blocks a user +func (u User) Block(ctx context.Context, req BlockRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/block" + + 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/create.go b/pkg/cloudbroker/user/create.go new file mode 100644 index 0000000..a53b4e7 --- /dev/null +++ b/pkg/cloudbroker/user/create.go @@ -0,0 +1,49 @@ +package user + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 address of the user. + // Required: true + EmailAddress string `url:"emailaddress" json:"emailaddress" validate:"required"` + + // Password of user + // Required: true + Password string `url:"password" json:"password" validate:"required"` + + // List of apiaccess groups this user belongs to. + // Required: false + APIAccess []uint64 `url:"apiaccess,omitempty" json:"apiaccess,omitempty"` + + // Provider of user + // one of: bvs, decs3o + // Required: false + Provider string `url:"provider,omitempty" json:"provider,omitempty" validate:"omitempty,userProvider"` +} + +// Create creates a user. +func (u User) Create(ctx context.Context, req CreateRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/create" + + res, err := u.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/user/delete.go b/pkg/cloudbroker/user/delete.go new file mode 100644 index 0000000..4cfd967 --- /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/v15/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..7c107a0 --- /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/v15/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..bd193ed --- /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/v15/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..d811336 --- /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/v15/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..b7c6b2b --- /dev/null +++ b/pkg/cloudbroker/user/get_audit.go @@ -0,0 +1,62 @@ +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"` + + // Sort by a field, format +|-(field) + // Default: -timestamp + // 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"` +} + +// 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..ef00131 --- /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/v15/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: false + Limit uint64 `url:"limit,omitempty" json:"limit,omitempty"` +} + +// 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..223a5f7 --- /dev/null +++ b/pkg/cloudbroker/user/list.go @@ -0,0 +1,72 @@ +package user + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 email. + // Required: false + Email string `url:"email,omitempty" json:"email,omitempty"` + + // 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..78fde62 --- /dev/null +++ b/pkg/cloudbroker/user/models.go @@ -0,0 +1,282 @@ +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{} + + // Blocked + Blocked bool `json:"blocked"` + + // 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"` + + // 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:"responsetime"` + + // StatusCode + StatusCode StatusCode `json:"statuscode"` + + // Guid + GUID string `json:"Guid"` + + // Time + Time float64 `json:"timestampEnd"` +} + +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"` + 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"` + Node []string `json:"node,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/unblock.go b/pkg/cloudbroker/user/unblock.go new file mode 100644 index 0000000..9c0bcc9 --- /dev/null +++ b/pkg/cloudbroker/user/unblock.go @@ -0,0 +1,38 @@ +package user + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UnblockRequest struct to block a user. +type UnblockRequest struct { + // ID of the user to block. + // Required: true + UserID string `url:"user_id" json:"user_id" validate:"required"` +} + +// Unblock unblocks a user +func (u User) Unblock(ctx context.Context, req UnblockRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/user/unblock" + + 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/user.go b/pkg/cloudbroker/user/user.go new file mode 100644 index 0000000..2056f48 --- /dev/null +++ b/pkg/cloudbroker/user/user.go @@ -0,0 +1,15 @@ +package user + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..70e2841 --- /dev/null +++ b/pkg/cloudbroker/vfpool.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..6d4ecbf --- /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/v15/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..8cdafb5 --- /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/v15/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..25794c4 --- /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/v15/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..9beaccb --- /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/v15/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..180caa1 --- /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/v15/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..fe25514 --- /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/v15/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..48d7f4f --- /dev/null +++ b/pkg/cloudbroker/vfpool/serialize.go @@ -0,0 +1,59 @@ +package vfpool + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..11db664 --- /dev/null +++ b/pkg/cloudbroker/vfpool/update.go @@ -0,0 +1,80 @@ +package vfpool + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 { + b, err := json.Marshal(req.Config) + if err != nil { + return false, err + } + + config = string(b) + } + + 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..e0e05be --- /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/v15/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..fe2de0a --- /dev/null +++ b/pkg/cloudbroker/vgpu.go @@ -0,0 +1,8 @@ +package cloudbroker + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..621231b --- /dev/null +++ b/pkg/cloudbroker/vgpu/allocate.go @@ -0,0 +1,37 @@ +package vgpu + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..d1d3e0b --- /dev/null +++ b/pkg/cloudbroker/vgpu/create.go @@ -0,0 +1,49 @@ +package vgpu + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..8ba9c25 --- /dev/null +++ b/pkg/cloudbroker/vgpu/deallocate.go @@ -0,0 +1,41 @@ +package vgpu + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..50bd40f --- /dev/null +++ b/pkg/cloudbroker/vgpu/destroy.go @@ -0,0 +1,41 @@ +package vgpu + +import ( + "context" + "net/http" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" + "strconv" +) + +// 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..ec498b9 --- /dev/null +++ b/pkg/cloudbroker/vgpu/list.go @@ -0,0 +1,92 @@ +package vgpu + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 id node + // Required: false + NID uint64 `url:"nid,omitempty" json:"nid,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..3969185 --- /dev/null +++ b/pkg/cloudbroker/vgpu/serialize.go @@ -0,0 +1,43 @@ +package vgpu + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..46be8af --- /dev/null +++ b/pkg/cloudbroker/vgpu/vgpu.go @@ -0,0 +1,15 @@ +package vgpu + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..cfdca30 --- /dev/null +++ b/pkg/cloudbroker/vins.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..2639f78 --- /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/v15/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..27db973 --- /dev/null +++ b/pkg/cloudbroker/vins/create_in_account.go @@ -0,0 +1,114 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // Enable security groups for VINS + // Required: false + // Default: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +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..513475d --- /dev/null +++ b/pkg/cloudbroker/vins/create_in_rg.go @@ -0,0 +1,107 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` + + // Zone ID + // Required: false + ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"` + + // Enable security groups for VINS + // Required: false + // Default: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +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..f69777d --- /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/v15/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..73fe83f --- /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/v15/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..0e3444a --- /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/v15/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..fefd662 --- /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/v15/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..3bfbf1c --- /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/v15/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..71dcf7c --- /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/v15/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..820fc17 --- /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/v15/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..93bb409 --- /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/v15/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..910497f --- /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/v15/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..d3ef4e9 --- /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/v15/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..7c0268e --- /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/v15/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..64c9c33 --- /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/v15/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..40a7f4f --- /dev/null +++ b/pkg/cloudbroker/vins/list.go @@ -0,0 +1,93 @@ +package vins + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 status + // Required: false + Status string `url:"status,omitempty" json:"status,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"` + + // Sort by zone id + // Default value: 0 + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"` +} + +// 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..4afb6ab --- /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/v15/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..90d44b9 --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/massDelete" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/vins/mass_disable.go b/pkg/cloudbroker/vins/mass_disable.go new file mode 100644 index 0000000..1a7c6a9 --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/massDisable" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/vins/mass_enable.go b/pkg/cloudbroker/vins/mass_enable.go new file mode 100644 index 0000000..2a87c8b --- /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/v15/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) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/massEnable" + + res, err := v.client.DecortApiCall(ctx, http.MethodPost, url, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/cloudbroker/vins/migrate_to_zone.go b/pkg/cloudbroker/vins/migrate_to_zone.go new file mode 100644 index 0000000..e791106 --- /dev/null +++ b/pkg/cloudbroker/vins/migrate_to_zone.go @@ -0,0 +1,42 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateToZone struct to move VINS to another zone +type MigrateToZoneRequest struct { + // VINSID to move + // Required: true + VINSID uint64 `url:"net_id" json:"net_id" validate:"required"` + + // ID of the zone to move + // Required: true + ZoneID uint64 `url:"zone_id" json:"zone_id" validate:"required"` +} + +// MigrateToZone moves VINS instance to new zone +func (v VINS) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/migrateToZone" + + 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/models.go b/pkg/cloudbroker/vins/models.go new file mode 100644 index 0000000..c261fa6 --- /dev/null +++ b/pkg/cloudbroker/vins/models.go @@ -0,0 +1,830 @@ +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"` + + // Node ID + NodeID uint64 `json:"node_id"` + + // 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"` + + // Enable security groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // List of security groups + SecGroups []uint64 `json:"security_groups"` + + // SDNInterfaceID + SDNInterfaceID string `json:"sdn_interface_id"` + + // 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"` + + // VNC password + VNCPassword string `json:"vncPasswd"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` + + // Live migration job ID + LiveMigrationJobID uint64 `json:"live_migration_job_id"` +} + +// Main information about reservation +type ItemReservation struct { + // Account ID + AccountID uint64 `json:"account_id"` + + // 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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"` + + // Enable Security Groups + EnableSecGroups bool `json:"enable_secgroups"` + + // 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"` + + // Zone ID + ZoneID uint64 `json:"zoneId"` +} + +// 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..e036af2 --- /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/v15/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..4c5d227 --- /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/v15/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..dba8add --- /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/v15/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..ccbaa71 --- /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/v15/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..e220457 --- /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/v15/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..fc3e615 --- /dev/null +++ b/pkg/cloudbroker/vins/serialize.go @@ -0,0 +1,43 @@ +package vins + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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..db205dd --- /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/v15/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..ff95b64 --- /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/v15/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..3348e19 --- /dev/null +++ b/pkg/cloudbroker/vins/static_route_add.go @@ -0,0 +1,50 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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"` +} + +// 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..69ffec8 --- /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/v15/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..eb085bc --- /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/v15/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/update.go b/pkg/cloudbroker/vins/update.go new file mode 100644 index 0000000..94c85e4 --- /dev/null +++ b/pkg/cloudbroker/vins/update.go @@ -0,0 +1,51 @@ +package vins + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update vins parameters +type UpdateRequest struct { + // VINS ID + // Required: true + VINSID uint64 `url:"vins_id" json:"vins_id" validate:"required"` + + // Name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Desc + // Required: false + Desc string `url:"desc,omitempty" json:"desc,omitempty"` + + // Flag indicating whether security groups are enabled for this network + // Required: false + EnableSecGroups interface{} `url:"enable_secgroups,omitempty" json:"enable_secgroups,omitempty" validate:"omitempty,isBool"` +} + +// Update updates a vins parameters +func (v VINS) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/vins/update" + + 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/vins.go b/pkg/cloudbroker/vins/vins.go new file mode 100644 index 0000000..4a5e041 --- /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/v15/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..2885540 --- /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/v15/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..699b554 --- /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/v15/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..84e1b2d --- /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/v15/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..b311eb7 --- /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/v15/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..ef2f311 --- /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/v15/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/pkg/cloudbroker/zone.go b/pkg/cloudbroker/zone.go new file mode 100644 index 0000000..151f4c1 --- /dev/null +++ b/pkg/cloudbroker/zone.go @@ -0,0 +1,10 @@ +package cloudbroker + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/zone" +) + +// Accessing the Zone method group +func (ca *CloudBroker) Zone() *zone.Zone { + return zone.New(ca.client) +} diff --git a/pkg/cloudbroker/zone/add_cpu_alignment_profile.go b/pkg/cloudbroker/zone/add_cpu_alignment_profile.go new file mode 100644 index 0000000..fb8d74c --- /dev/null +++ b/pkg/cloudbroker/zone/add_cpu_alignment_profile.go @@ -0,0 +1,51 @@ +package zone + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AddCPUAlignmentProfileRequest struct to add CPU alignment profile to zone +type AddCPUAlignmentProfileRequest struct { + // ID of zone + // Required: true + ZoneID uint64 `url:"zone_id" json:"zone_id" validate:"required"` + + // Hypervisor similarity in percentage + // Default: 70 + // Required: false + HypervisorSimilarityInPercentage uint64 `url:"hypervisor_similarity_in_percentage,omitempty" json:"hypervisor_similarity_in_percentage,omitempty"` +} + +// AddCPUAlignmentProfile adds CPU alignment profile to zone +func (e Zone) AddCPUAlignmentProfile(ctx context.Context, req AddCPUAlignmentProfileRequest) ([]CpuAlignmentProfile, error) { + res, err := e.AddCPUAlignmentProfileRaw(ctx, req) + if err != nil { + return nil, err + } + + var profiles []CpuAlignmentProfile + + err = json.Unmarshal(res, &profiles) + if err != nil { + return nil, err + } + + return profiles, nil +} + +// AddCPUAlignmentProfileRaw adds CPU alignment profile to zone and returns the result as an array of bytes +func (e Zone) AddCPUAlignmentProfileRaw(ctx context.Context, req AddCPUAlignmentProfileRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/add_cpu_alignment_profile" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/zone/add_node.go b/pkg/cloudbroker/zone/add_node.go new file mode 100644 index 0000000..2f5ea82 --- /dev/null +++ b/pkg/cloudbroker/zone/add_node.go @@ -0,0 +1,43 @@ +package zone + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AddNodeRequest struct to add node to zone +type AddNodeRequest struct { + // ID of zone + // Required: true + ID uint64 `url:"id" json:"id" validate:"required"` + + // List of node ids + // Required: true + NodeIDs []uint64 `url:"nodeIds" json:"nodeIds" validate:"required"` +} + +// AddNode add nodes to zone +func (e Zone) AddNode(ctx context.Context, req AddNodeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/addNode" + + 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/zone/create.go b/pkg/cloudbroker/zone/create.go new file mode 100644 index 0000000..7b81f86 --- /dev/null +++ b/pkg/cloudbroker/zone/create.go @@ -0,0 +1,51 @@ +package zone + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create zone +type CreateRequest struct { + // Name of zone + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // If true, all nodes belonging to the given zone will be marked for autostart + // Required: false + AutoStart interface{} `url:"autostart,omitempty" json:"autostart,omitempty" validate:"omitempty,isBool"` + + // Enables Distributed Resource Scheduler (DRS) functionality for the zone + // Required: false + DRS interface{} `url:"drs,omitempty" json:"drs,omitempty" validate:"omitempty,isBool"` +} + +// Create creates zone object +func (e Zone) Create(ctx context.Context, req CreateRequest) (uint64, error) { + err := validators.ValidateRequest(req) + if err != nil { + return 0, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/create" + + res, err := e.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/zone/del_node.go b/pkg/cloudbroker/zone/del_node.go new file mode 100644 index 0000000..7e9dfde --- /dev/null +++ b/pkg/cloudbroker/zone/del_node.go @@ -0,0 +1,43 @@ +package zone + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DelNodeRequest struct to remove node from zone +type DelNodeRequest struct { + // ID of zone + // Required: true + ID uint64 `url:"id" json:"id" validate:"required"` + + // List of node ids + // Required: true + NodeIDs []uint64 `url:"nodeIds" json:"nodeIds" validate:"required"` +} + +// DelNode remove nodes from zone +func (e Zone) DelNode(ctx context.Context, req DelNodeRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/delNode" + + 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/zone/delete.go b/pkg/cloudbroker/zone/delete.go new file mode 100644 index 0000000..4d6519f --- /dev/null +++ b/pkg/cloudbroker/zone/delete.go @@ -0,0 +1,39 @@ +package zone + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct to delete zone +type DeleteRequest struct { + // ID of zone + // Required: true + ID uint64 `url:"id" json:"id" validate:"required"` +} + +// Delete deletes zone object +func (e Zone) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/delete" + + 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/zone/delete_cpu_alignment_profile.go b/pkg/cloudbroker/zone/delete_cpu_alignment_profile.go new file mode 100644 index 0000000..4e98455 --- /dev/null +++ b/pkg/cloudbroker/zone/delete_cpu_alignment_profile.go @@ -0,0 +1,38 @@ +package zone + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteCPUAlignmentProfileRequest struct to delete CPU alignment profile from zone +type DeleteCPUAlignmentProfileRequest struct { + // ID of zone + // Required: true + ZoneID uint64 `url:"zone_id" json:"zone_id" validate:"required"` +} + +// DeleteCPUAlignmentProfile deletes CPU alignment profile from zone +func (e Zone) DeleteCPUAlignmentProfile(ctx context.Context, req DeleteCPUAlignmentProfileRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/delete_cpu_alignment_profile" + + 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/zone/filter.go b/pkg/cloudbroker/zone/filter.go new file mode 100644 index 0000000..365108c --- /dev/null +++ b/pkg/cloudbroker/zone/filter.go @@ -0,0 +1,53 @@ +package zone + +// FilterByID returns ListZones with specified ID. +func (list ListZones) FilterByID(id uint64) ListZones { + predicate := func(izone ItemZone) bool { + return izone.ID == id + } + + return list.FilterFunc(predicate) +} + +// FilterByName returns ListZones with specified Name. +func (list ListZones) FilterByName(name string) ListZones { + predicate := func(izone ItemZone) bool { + return izone.Name == name + } + + return list.FilterFunc(predicate) +} + +// FilterByStatus returns ListZones with specified Status. +func (list ListZones) FilterByStatus(status string) ListZones { + predicate := func(izone ItemZone) bool { + return izone.Status == status + } + + return list.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListZones based on a user-specified predicate. +func (list ListZones) FilterFunc(predicate func(ItemZone) bool) ListZones { + var result ListZones + + for _, item := range list.Data { + if predicate(item) { + result.Data = append(result.Data, item) + } + } + + result.EntryCount = uint64(len(result.Data)) + + return result +} + +// FindOne returns first found ItemZone +// If none was found, returns an empty struct. +func (list ListZones) FindOne() ItemZone { + if list.EntryCount == 0 { + return ItemZone{} + } + + return list.Data[0] +} diff --git a/pkg/cloudbroker/zone/filter_test.go b/pkg/cloudbroker/zone/filter_test.go new file mode 100644 index 0000000..973c638 --- /dev/null +++ b/pkg/cloudbroker/zone/filter_test.go @@ -0,0 +1,86 @@ +package zone + +import "testing" + +var zones = ListZones{ + Data: []ItemZone{ + + { + ID: 2, + GUID: 0, + GID: 0, + Name: "System Config", + Description: "", + Deletable: true, + Status: "LOCKED", + CreatedTime: 1640995200, // 2022-01-01 + UpdatedTime: 1640995200, + NodeIDs: nil, + }, + { + ID: 5, + GUID: 5500, + GID: 6600, + Name: "ssss Nodes", + Description: " infrastructure", + Deletable: true, + Status: "DISABLED", + CreatedTime: 1577836800, // 2020-01-01 + UpdatedTime: 1580515200, // 2020-02-01 + NodeIDs: []uint64{777, 888, 999}, + }, + { + ID: 10, + GUID: 5500, + GID: 6600, + Name: "node", + Description: "infrastructure", + Deletable: true, + Status: "DISABLED", + CreatedTime: 1577836800, + UpdatedTime: 1580515200, + NodeIDs: []uint64{777, 888, 999}, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := zones.FilterByID(10).FindOne() + + if actual.ID != 10 { + t.Fatal("expected ID 10, found: ", actual.ID) + } +} + +func TestFilterByName(t *testing.T) { + name := "node" + actual := zones.FilterByName(name).FindOne() + + if actual.Name != name { + t.Fatal("expected ", name, " found: ", actual.Name) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := zones.FilterByStatus("DISABLED") + + if len(actual.Data) != 2 { + t.Fatal("expected 2 found, actual: ", len(actual.Data)) + } + + for _, item := range actual.Data { + if item.Status != "DISABLED" { + t.Fatal("expected Status 'DISABLED', found: ", item.Status) + } + } +} + +func TestFilterFunc(t *testing.T) { + actual := zones.FilterFunc(func(ien ItemZone) bool { + return ien.Deletable == true + }) + + if len(actual.Data) != 3 { + t.Fatal("expected 3 elements, found: ", len(actual.Data)) + } +} diff --git a/pkg/cloudbroker/zone/get.go b/pkg/cloudbroker/zone/get.go new file mode 100644 index 0000000..2e11810 --- /dev/null +++ b/pkg/cloudbroker/zone/get.go @@ -0,0 +1,46 @@ +package zone + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get detailed information about zone +type GetRequest struct { + // ID of zone + // Required: true + ID uint64 `url:"id" json:"id" validate:"required"` +} + +// Get gets detailed information about zone struct +func (e Zone) Get(ctx context.Context, req GetRequest) (*RecordZone, error) { + res, err := e.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordZone{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets detailed information about zone as an array of bytes +func (e Zone) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/get" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/zone/get_cpu_alignment_profile.go b/pkg/cloudbroker/zone/get_cpu_alignment_profile.go new file mode 100644 index 0000000..d0a4f57 --- /dev/null +++ b/pkg/cloudbroker/zone/get_cpu_alignment_profile.go @@ -0,0 +1,46 @@ +package zone + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetCPUAlignmentProfileRequest struct to get CPU alignment profile of zone +type GetCPUAlignmentProfileRequest struct { + // ID of zone + // Required: true + ZoneID uint64 `url:"zone_id" json:"zone_id" validate:"required"` +} + +// GetCPUAlignmentProfile gets CPU alignment profile of zone +func (e Zone) GetCPUAlignmentProfile(ctx context.Context, req GetCPUAlignmentProfileRequest) ([]CpuAlignmentProfile, error) { + res, err := e.GetCPUAlignmentProfileRaw(ctx, req) + if err != nil { + return nil, err + } + + var profiles []CpuAlignmentProfile + + err = json.Unmarshal(res, &profiles) + if err != nil { + return nil, err + } + + return profiles, nil +} + +// GetCPUAlignmentProfileRaw gets CPU alignment profile of zone as an array of bytes +func (e Zone) GetCPUAlignmentProfileRaw(ctx context.Context, req GetCPUAlignmentProfileRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/get_cpu_alignment_profile" + + res, err := e.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/cloudbroker/zone/ids.go b/pkg/cloudbroker/zone/ids.go new file mode 100644 index 0000000..33f6193 --- /dev/null +++ b/pkg/cloudbroker/zone/ids.go @@ -0,0 +1,10 @@ +package zone + +// IDs gets array of IDs from ListZones struct +func (le ListZones) 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/zone/list.go b/pkg/cloudbroker/zone/list.go new file mode 100644 index 0000000..e7970a4 --- /dev/null +++ b/pkg/cloudbroker/zone/list.go @@ -0,0 +1,84 @@ +package zone + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get list of zones +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 deletable + // Required: false + Deletable bool `url:"deletable,omitempty" json:"deletable,omitempty"` + + // Find by node ID + // Required: false + NodeID uint64 `url:"nodeId,omitempty" json:"nodeId,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 zones as a ListZones struct +func (e Zone) List(ctx context.Context, req ListRequest) (*ListZones, error) { + + res, err := e.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListZones{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListRaw gets list of all available zones as an array of bytes +func (e Zone) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/list" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/zone/list_cpu_alignment_profile.go b/pkg/cloudbroker/zone/list_cpu_alignment_profile.go new file mode 100644 index 0000000..b711a7d --- /dev/null +++ b/pkg/cloudbroker/zone/list_cpu_alignment_profile.go @@ -0,0 +1,45 @@ +package zone + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListCPUAlignmentProfileRequest struct to list CPU alignment profiles +type ListCPUAlignmentProfileRequest struct { + // ID of zone + // Required: false + ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,omitempty"` +} + +// ListCPUAlignmentProfile gets list of CPU alignment profiles +func (e Zone) ListCPUAlignmentProfile(ctx context.Context, req ListCPUAlignmentProfileRequest) (*ListCPUAlignmentProfiles, error) { + res, err := e.ListCPUAlignmentProfileRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListCPUAlignmentProfiles{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return &list, nil +} + +// ListCPUAlignmentProfileRaw gets list of CPU alignment profiles as an array of bytes +func (e Zone) ListCPUAlignmentProfileRaw(ctx context.Context, req ListCPUAlignmentProfileRequest) ([]byte, error) { + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/list_cpu_alignment_profile" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/zone/models.go b/pkg/cloudbroker/zone/models.go new file mode 100644 index 0000000..50dc179 --- /dev/null +++ b/pkg/cloudbroker/zone/models.go @@ -0,0 +1,252 @@ +package zone + +// CPU alignment profile +type CpuAlignmentProfile struct { + // Profile name + Name string `json:"name"` + + // Vendor + Vendor string `json:"vendor"` + + // Model + Model string `json:"model"` +} + +// Supported CPU model +type SupportedCpuModel struct { + // Vendor + Vendor string `json:"vendor"` + + // Model + Model string `json:"model"` + + // Count + Count uint64 `json:"count"` + + // Percentage + Percentage float64 `json:"percentage"` +} + +// CPU alignment profile candidate +type CpuAlignmentProfileCandidate struct { + // Profile name + Name string `json:"name"` + + // Vendor + Vendor string `json:"vendor"` + + // Model + Model string `json:"model"` + + // Count + Count uint64 `json:"count"` + + // Percentage + Percentage float64 `json:"percentage"` + + // Required count + RequiredCount uint64 `json:"required_count"` +} + +// Response for test_cpu_alignment_profile +type TestCPUAlignmentProfileResult struct { + // Profiles + Profiles []CpuAlignmentProfile `json:"profiles"` + + // Candidates + Candidates []CpuAlignmentProfileCandidate `json:"candidates"` + + // Supported CPU models + SupportedCpuModels []SupportedCpuModel `json:"supported_cpu_models"` +} + +// Item for list_cpu_alignment_profile response +type ItemCPUAlignmentProfile struct { + // Zone ID + ZoneID uint64 `json:"zoneId"` + + // CPU alignment profiles + CpuAlignmentProfiles []CpuAlignmentProfile `json:"cpu_alignment_profiles"` +} + +// Response for list_cpu_alignment_profile +type ListCPUAlignmentProfiles struct { + // Entry count + EntryCount uint64 `json:"entryCount"` + + // Data + Data []ItemCPUAlignmentProfile `json:"data"` +} + +type ListZones struct { + // Entry count + EntryCount uint64 `json:"entryCount"` + + // Data + Data []ItemZone `json:"data"` +} + +// Detailed information about the zone record +type RecordZone struct { + // If true, all nodes belonging to the given zone will be marked for autostart + AutoStart bool `json:"autostart"` + + // ID + ID uint64 `json:"id"` + + // GUID + GUID uint64 `json:"guid"` + + // GID + GID uint64 `json:"gid"` + + // Name + Name string `json:"name"` + + // List of associated account IDs + AccountIDs []uint64 `json:"accountIds"` + + // List of associated bservice IDs + BserviceIDs []uint64 `json:"bserviceIds"` + + // List of associated compute IDs + ComputeIDs []uint64 `json:"computeIds"` + + // Description + Description string `json:"description"` + + // Deletable flag + Deletable bool `json:"deletable"` + + // List of associated ExtNet IDs + ExtnetIDs []uint64 `json:"extnetIds"` + + // List of associated K8s IDs + K8SIDs []uint64 `json:"k8sIds"` + + // List of associated LB IDs + LBIDs []uint64 `json:"lbIds"` + + // Status + Status string `json:"status"` + + // Created timestamp + CreatedTime uint64 `json:"createdTime"` + + // Updated timestamp + UpdatedTime uint64 `json:"updatedTime"` + + // List of associated Node IDs + NodeIDs []uint64 `json:"nodeIds"` + + // List of associated VINS IDs + VinsIDs []uint64 `json:"vinsIds"` + + // DRS + DRS bool `json:"drs"` + + // DRS UID + DRSUID string `json:"drs_uid"` + + // App ID + AppID string `json:"app_id"` + + // Decort URL + DecortURL string `json:"decort_url"` + + // DRS Name + DRSName string `json:"drs_name"` + + // SSO URL + SSOURL string `json:"sso_url"` + + // SSO type + SSOType string `json:"sso_type"` + + // Ping address + PingAddr string `json:"ping_addr"` + + // Broadcast address + BroadcastAddr string `json:"broadcast_addr"` + + // Skip ssl verify + SSLSkipVerify bool `json:"ssl_skip_verify"` + + // Domain + Domain string `json:"domain"` + + // CPU alignment profiles + CpuAlignmentProfiles []CpuAlignmentProfile `json:"cpu_alignment_profiles"` +} + +// A zone item from a list +type ItemZone struct { + // App ID + AppID string `json:"app_id"` + + // If true, all nodes belonging to the given zone will be marked for autostart + AutoStart bool `json:"autostart"` + + // Created timestamp + CreatedTime uint64 `json:"createdTime"` + + // Decort URL + DecortURL string `json:"decort_url"` + + // Deletable flag + Deletable bool `json:"deletable"` + + // Description + Description string `json:"description"` + + // DRS + DRS bool `json:"drs"` + + // DRS Name + DRSName string `json:"drs_name"` + + // DRS UID + DRSUID string `json:"drs_uid"` + + // GID + GID uint64 `json:"gid"` + + // GUID + GUID uint64 `json:"guid"` + + // ID + ID uint64 `json:"id"` + + // Name + Name string `json:"name"` + + // List of associated Node IDs + NodeIDs []uint64 `json:"nodeIds"` + + // SSO URL + SSOURL string `json:"sso_url"` + + // SSO type + SSOType string `json:"sso_type"` + + // Status + Status string `json:"status"` + + // Updated timestamp + UpdatedTime uint64 `json:"updatedTime"` + + // Ping address + PingAddr string `json:"ping_addr"` + + // Broadcast address + BroadcastAddr string `json:"broadcast_addr"` + + // Skip ssl verify + SSLSkipVerify bool `json:"ssl_skip_verify"` + + // Domain + Domain string `json:"domain"` + + // CPU alignment profiles + CpuAlignmentProfiles []CpuAlignmentProfile `json:"cpu_alignment_profiles"` +} diff --git a/pkg/cloudbroker/zone/node_autostart.go b/pkg/cloudbroker/zone/node_autostart.go new file mode 100644 index 0000000..aef0c2a --- /dev/null +++ b/pkg/cloudbroker/zone/node_autostart.go @@ -0,0 +1,43 @@ +package zone + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// NodeAutoStartRequest struct to set node autostart in zone +type NodeAutoStartRequest struct { + // ID of zone + // Required: true + ZoneID uint64 `url:"zone_id" json:"zone_id" validate:"required"` + + // AutoStart nodes in zone + // Required: true + AutoStart bool `url:"autostart" json:"autostart" validate:"required"` +} + +// NodeAutoStart sets node autostart in zone +func (e Zone) NodeAutoStart(ctx context.Context, req NodeAutoStartRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/node_autostart" + + 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/zone/serialize.go b/pkg/cloudbroker/zone/serialize.go new file mode 100644 index 0000000..8215a55 --- /dev/null +++ b/pkg/cloudbroker/zone/serialize.go @@ -0,0 +1,43 @@ +package zone + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 (list ListZones) Serialize(params ...string) (serialization.Serialized, error) { + if list.EntryCount == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(list, prefix, indent) + } + + return json.Marshal(list) +} + +// 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 (item RecordZone) Serialize(params ...string) (serialization.Serialized, error) { + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(item, prefix, indent) + } + + return json.Marshal(item) +} diff --git a/pkg/cloudbroker/zone/test_cpu_alignment_profile.go b/pkg/cloudbroker/zone/test_cpu_alignment_profile.go new file mode 100644 index 0000000..ffe7205 --- /dev/null +++ b/pkg/cloudbroker/zone/test_cpu_alignment_profile.go @@ -0,0 +1,51 @@ +package zone + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// TestCPUAlignmentProfileRequest struct to test CPU alignment profile for zone +type TestCPUAlignmentProfileRequest struct { + // ID of zone + // Required: true + ZoneID uint64 `url:"zone_id" json:"zone_id" validate:"required"` + + // Hypervisor similarity in percentage + // Default: 70 + // Required: false + HypervisorSimilarityInPercentage uint64 `url:"hypervisor_similarity_in_percentage,omitempty" json:"hypervisor_similarity_in_percentage,omitempty"` +} + +// TestCPUAlignmentProfile tests CPU alignment profile for zone +func (e Zone) TestCPUAlignmentProfile(ctx context.Context, req TestCPUAlignmentProfileRequest) (*TestCPUAlignmentProfileResult, error) { + res, err := e.TestCPUAlignmentProfileRaw(ctx, req) + if err != nil { + return nil, err + } + + result := TestCPUAlignmentProfileResult{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return &result, nil +} + +// TestCPUAlignmentProfileRaw tests CPU alignment profile for zone and returns the result as an array of bytes +func (e Zone) TestCPUAlignmentProfileRaw(ctx context.Context, req TestCPUAlignmentProfileRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/test_cpu_alignment_profile" + + res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) + return res, err +} diff --git a/pkg/cloudbroker/zone/update.go b/pkg/cloudbroker/zone/update.go new file mode 100644 index 0000000..dba3911 --- /dev/null +++ b/pkg/cloudbroker/zone/update.go @@ -0,0 +1,51 @@ +package zone + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update zone +type UpdateRequest struct { + // ID of zone + // Required: true + ID uint64 `url:"id" json:"id" validate:"required"` + + // Name of zone + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // If true, all nodes belonging to the given zone will be marked for autostart + // Required: false + AutoStart interface{} `url:"autostart,omitempty" json:"autostart,omitempty" validate:"omitempty,isBool"` +} + +// Update updates zone object +func (e Zone) Update(ctx context.Context, req UpdateRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/cloudbroker/zone/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/zone/zone.go b/pkg/cloudbroker/zone/zone.go new file mode 100644 index 0000000..a15987f --- /dev/null +++ b/pkg/cloudbroker/zone/zone.go @@ -0,0 +1,18 @@ +// API Actor for use zones +package zone + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to zone +type Zone struct { + client interfaces.Caller +} + +// Builder for zone endpoints +func New(client interfaces.Caller) *Zone { + return &Zone{ + client, + } +} diff --git a/pkg/sdn/access_groups.go b/pkg/sdn/access_groups.go new file mode 100644 index 0000000..96aa026 --- /dev/null +++ b/pkg/sdn/access_groups.go @@ -0,0 +1,10 @@ +package sdn + +import ( + ag "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/acsgroups" +) + +// Accessing the SDN method group +func (sdn *SDN) AccessGroups() *ag.AccessGroups { + return ag.New(sdn.client) +} diff --git a/pkg/sdn/acsgroups/access_groups.go b/pkg/sdn/acsgroups/access_groups.go new file mode 100644 index 0000000..c46b008 --- /dev/null +++ b/pkg/sdn/acsgroups/access_groups.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN access groups +package acsgroups + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to access groups +type AccessGroups struct { + client interfaces.Caller +} + +// Builder for access groups endpoints +func New(client interfaces.Caller) *AccessGroups { + return &AccessGroups{ + client, + } +} diff --git a/pkg/sdn/acsgroups/create.go b/pkg/sdn/acsgroups/create.go new file mode 100644 index 0000000..d630040 --- /dev/null +++ b/pkg/sdn/acsgroups/create.go @@ -0,0 +1,45 @@ +package acsgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create access group +type CreateRequest struct { + // Comment of the access group + // Required: true + Comment string `url:"comment" json:"comment" validate:"required"` + + // Name of acces group + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` +} + +// Create creates a access groups +func (i AccessGroups) Create(ctx context.Context, req CreateRequest) (*AccessGroupItem, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/create" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := AccessGroupItem{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/acsgroups/delete.go b/pkg/sdn/acsgroups/delete.go new file mode 100644 index 0000000..baae474 --- /dev/null +++ b/pkg/sdn/acsgroups/delete.go @@ -0,0 +1,33 @@ +package acsgroups + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct to delete access group +type DeleteRequest struct { + // ID of the access group + // Required: true + GroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` +} + +// Delete an access groups +func (i AccessGroups) Delete(ctx context.Context, req DeleteRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/delete" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return "", err + } + + return string(res), nil +} diff --git a/pkg/sdn/acsgroups/filter.go b/pkg/sdn/acsgroups/filter.go new file mode 100644 index 0000000..2ac4355 --- /dev/null +++ b/pkg/sdn/acsgroups/filter.go @@ -0,0 +1,42 @@ +package acsgroups + +// FilterByID returns AccessGroupList with specified ID. +func (agl AccessGroupList) FilterByID(id string) AccessGroupList { + predicate := func(ia AccessGroup) bool { + return ia.ID == id + } + + return agl.FilterFunc(predicate) +} + +// FilterByName returns AccessGroupList with specified Name. +func (agl AccessGroupList) FilterByName(name string) AccessGroupList { + predicate := func(ia AccessGroup) bool { + return ia.DisplayName == name + } + + return agl.FilterFunc(predicate) +} + +// FilterFunc allows filtering AccessGroupList based on a user-specified predicate. +func (agl AccessGroupList) FilterFunc(predicate func(AccessGroup) bool) AccessGroupList { + var result AccessGroupList + + for _, acc := range agl.AccessGroups { + if predicate(acc) { + result.AccessGroups = append(result.AccessGroups, acc) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (agl AccessGroupList) FindOne() AccessGroup { + if len(agl.AccessGroups) == 0 { + return AccessGroup{} + } + + return agl.AccessGroups[0] +} diff --git a/pkg/sdn/acsgroups/filter_test.go b/pkg/sdn/acsgroups/filter_test.go new file mode 100644 index 0000000..78832e4 --- /dev/null +++ b/pkg/sdn/acsgroups/filter_test.go @@ -0,0 +1,86 @@ +package acsgroups + +import ( + "testing" +) + +var testAccessGroups = AccessGroupList{ + AccessGroups: []AccessGroup{ + { + ID: "group1", + DisplayName: "Developers", + Comment: "First group", + CreatedAt: "2023-01-01", + }, + { + ID: "group2", + DisplayName: "Admins", + Comment: "Second group", + CreatedAt: "2023-01-02", + }, + { + ID: "group3", + DisplayName: "Users", + Comment: "Third group", + CreatedAt: "2023-01-03", + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testAccessGroups.FilterByID("group2").FindOne() + + if actual.ID != "group2" { + t.Fatal("actual:", actual.ID, "> expected: group2") + } +} + +func TestFilterByName(t *testing.T) { + actual := testAccessGroups.FilterByName("Users").FindOne() + + if actual.DisplayName != "Users" { + t.Fatal("actual:", actual.DisplayName, ">> expected: Users") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testAccessGroups.FilterFunc(func(ag AccessGroup) bool { + return ag.Comment == "Second group" + }) + + if len(actual.AccessGroups) != 1 || actual.AccessGroups[0].ID != "group2" { + t.Fatal("Expected 1 group with comment 'Second group', found:", len(actual.AccessGroups)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testAccessGroups.FilterByID("group1").FindOne() + if result.ID != "group1" { + t.Fatal("Expected group1, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := AccessGroupList{} + result := emptyList.FindOne() + + if result.ID != "" || result.DisplayName != "" { + t.Fatal("Expected empty AccessGroup, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := testAccessGroups.FilterByID("nonexistent") + + if len(actual.AccessGroups) != 0 { + t.Fatal("Expected 0 groups, found:", len(actual.AccessGroups)) + } +} + +func TestFilterByNameNotFound(t *testing.T) { + actual := testAccessGroups.FilterByName("Nonexistent Group") + + if len(actual.AccessGroups) != 0 { + t.Fatal("Expected 0 groups, found:", len(actual.AccessGroups)) + } +} diff --git a/pkg/sdn/acsgroups/get.go b/pkg/sdn/acsgroups/get.go new file mode 100644 index 0000000..a441e8f --- /dev/null +++ b/pkg/sdn/acsgroups/get.go @@ -0,0 +1,46 @@ +package acsgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetGroupRequest struct to get an access group +type GetGroupRequest struct { + // ID of the access group + // Required: true + GroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` +} + +// Info about access group +func (i AccessGroups) Get(ctx context.Context, req GetGroupRequest) (*AccessGroup, error) { + res, err := i.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + group := AccessGroup{} + + err = json.Unmarshal(res, &group) + if err != nil { + return nil, err + } + + return &group, nil +} + +// GetRaw gets a details of group as an array of bytes +func (a AccessGroups) GetRaw(ctx context.Context, req GetGroupRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/acsgroups/ids.go b/pkg/sdn/acsgroups/ids.go new file mode 100644 index 0000000..1c925e8 --- /dev/null +++ b/pkg/sdn/acsgroups/ids.go @@ -0,0 +1,19 @@ +package acsgroups + +// IDs gets array of IDs from AccessGroupList struct +func (agl AccessGroupList) IDs() []string { + res := make([]string, 0, len(agl.AccessGroups)) + for _, c := range agl.AccessGroups { + res = append(res, c.ID) + } + return res +} + +// IDs gets array of IDs from UsersList struct +func (ul UsersList) IDs() []string { + res := make([]string, 0, len(ul.Users)) + for _, c := range ul.Users { + res = append(res, c.ID) + } + return res +} diff --git a/pkg/sdn/acsgroups/list.go b/pkg/sdn/acsgroups/list.go new file mode 100644 index 0000000..afec1e9 --- /dev/null +++ b/pkg/sdn/acsgroups/list.go @@ -0,0 +1,84 @@ +package acsgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListGroupsRequest struct to get a list of access groups +type ListGroupsRequest struct { + // Find by enabled status, true or false + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Find by deleted status, true or false + // Required: false + Deleted interface{} `url:"deleted,omitempty" json:"deleted,omitempty" validate:"omitempty,isBool"` + + // Display name filter + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Owner display name filter + // Required: false + OwnerDisplayName string `url:"owner_display_name,omitempty" json:"owner_display_name,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, created_at, updated_at, deleted_at, owner_login) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` + + // Creation date lower bound (inclusive) + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Creation date upper bound (inclusive) + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` +} + +// List of access groups +func (i AccessGroups) List(ctx context.Context, req ListGroupsRequest) (*AccessGroupList, error) { + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + groups := []AccessGroup{} + + err = json.Unmarshal(res, &groups) + if err != nil { + return nil, err + } + + result := AccessGroupList{AccessGroups: groups} + + return &result, nil +} + +// ListRaw gets a list of all users as an array of bytes +func (a AccessGroups) ListRaw(ctx context.Context, req ListGroupsRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/acsgroups/models.go b/pkg/sdn/acsgroups/models.go new file mode 100644 index 0000000..919f1c5 --- /dev/null +++ b/pkg/sdn/acsgroups/models.go @@ -0,0 +1,48 @@ +package acsgroups + +type AccessGroupItem struct { + Name string `json:"display_name"` + ID string `json:"id"` + Comment string `json:"comment"` +} + +type AccessGroupList struct { + AccessGroups []AccessGroup +} + +type AccessGroup struct { + ID string `json:"id"` + DisplayName string `json:"display_name"` + Comment string `json:"comment"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + NetObjectAccessGroup NetObjectAccessGroup `json:"net_object_access_group"` + DefaultSecurityPolicy DefaultSecurityPolicy `json:"default_security_policy"` +} + +type NetObjectAccessGroup struct { + ID string `json:"id"` + VersionID int64 `json:"version_id"` + AccessGroupID string `json:"access_group_id"` +} + +type DefaultSecurityPolicy struct { + ID string `json:"id"` + DisplayName string `json:"display_name"` + Description string `json:"description"` + VersionID int64 `json:"version_id"` + AccessGroupID string `json:"access_group_id"` + DefaultAclDrop string `json:"default_acl_drop"` + DefaultOpenSessionDrop bool `json:"default_open_session_drop"` +} + +type User struct { + Name string `json:"display_name"` + ID string `json:"id"` + RoleID string `json:"role_id"` + Login string `json:"login"` +} + +type UsersList struct { + Users []User +} diff --git a/pkg/sdn/acsgroups/update.go b/pkg/sdn/acsgroups/update.go new file mode 100644 index 0000000..58f8e95 --- /dev/null +++ b/pkg/sdn/acsgroups/update.go @@ -0,0 +1,49 @@ +package acsgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update access group +type UpdateRequest struct { + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Comment of the acces group + // Required: false + Comment string `url:"comment,omitempty" json:"comment,omitempty"` + + // Name of acces group + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` +} + +// Update updates a access groups +func (i AccessGroups) Update(ctx context.Context, req UpdateRequest) (*AccessGroup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/update" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPatch, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := AccessGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/acsgroups/user_add.go b/pkg/sdn/acsgroups/user_add.go new file mode 100644 index 0000000..a3eb700 --- /dev/null +++ b/pkg/sdn/acsgroups/user_add.go @@ -0,0 +1,41 @@ +package acsgroups + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UserAddRequest struct to userAdd access group +type UserAddRequest struct { + // Comment of the access group + // Required: true + GroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Access group role ID + // Required: true + AccessGroupRoleID string `url:"access_group_role_id" json:"access_group_role_id" validate:"required"` + + // User ID + // Required: true + UserID string `url:"user_id" json:"user_id" validate:"required"` +} + +// UserAdd a access groups +func (i AccessGroups) UserAdd(ctx context.Context, req UserAddRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/user_add" + + _, err = i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/sdn/acsgroups/user_delete.go b/pkg/sdn/acsgroups/user_delete.go new file mode 100644 index 0000000..06b934c --- /dev/null +++ b/pkg/sdn/acsgroups/user_delete.go @@ -0,0 +1,37 @@ +package acsgroups + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UserDeleteRequest struct to userDelete access group +type UserDeleteRequest struct { + // Comment of the access group + // Required: true + GroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // User ID + // Required: true + UserID string `url:"user_id" json:"user_id" validate:"required"` +} + +// UserDelete a access groups +func (i AccessGroups) UserDelete(ctx context.Context, req UserDeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/user_delete" + + _, err = i.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/sdn/acsgroups/user_list.go b/pkg/sdn/acsgroups/user_list.go new file mode 100644 index 0000000..3b25298 --- /dev/null +++ b/pkg/sdn/acsgroups/user_list.go @@ -0,0 +1,113 @@ +package acsgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UsersListRequest struct to get a list of users +type UsersListRequest struct { + // Access group identifier + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Filter by global role + // Required: false + GlobalRole string `url:"global_role,omitempty" json:"global_role,omitempty"` + + // Filter by access group role + // Required: false + AccessGroupRole string `url:"access_group_role,omitempty" json:"access_group_role,omitempty"` + + // Filter by enabled status (true/false) + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Filter by deleted status (true/false) + // Required: false + Deleted interface{} `url:"deleted,omitempty" json:"deleted,omitempty" validate:"omitempty,isBool"` + + // Filter by display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Filter by user login + // Required: false + Login string `url:"login,omitempty" json:"login,omitempty"` + + // Filter by creator login + // Required: false + CreatedBy string `url:"created_by,omitempty" json:"created_by,omitempty"` + + // Filter by last updater login + // Required: false + UpdatedBy string `url:"updated_by,omitempty" json:"updated_by,omitempty"` + + // Filter by deleter login + // Required: false + DeletedBy string `url:"deleted_by,omitempty" json:"deleted_by,omitempty"` + + // Filter by disabler login + // Required: false + DisabledBy string `url:"disabled_by,omitempty" json:"disabled_by,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, email, phone, created_at, updated_at, deleted_at) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` + + // Creation date lower bound (inclusive) + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Creation date upper bound (exclusive) + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` +} + +// List of access groups +func (i AccessGroups) UsersList(ctx context.Context, req UsersListRequest) (*UsersList, error) { + + res, err := i.UserListRaw(ctx, req) + if err != nil { + return nil, err + } + + users := []User{} + + err = json.Unmarshal(res, &users) + if err != nil { + return nil, err + } + + result := UsersList{Users: users} + + return &result, nil +} + +// ListRaw gets a list of all users as an array of bytes +func (a AccessGroups) UserListRaw(ctx context.Context, req UsersListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/user_list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/acsgroups/user_update_role.go b/pkg/sdn/acsgroups/user_update_role.go new file mode 100644 index 0000000..1318d20 --- /dev/null +++ b/pkg/sdn/acsgroups/user_update_role.go @@ -0,0 +1,41 @@ +package acsgroups + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UserUpdateRoleRequest struct to userUpdateRole access group +type UserUpdateRoleRequest struct { + // Comment of the access group + // Required: true + GroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Access group role ID + // Required: true + AccessGroupRoleID string `url:"access_group_role_id" json:"access_group_role_id" validate:"required"` + + // User ID + // Required: true + UserID string `url:"user_id" json:"user_id" validate:"required"` +} + +// UserUpdateRole a access groups +func (i AccessGroups) UserUpdateRole(ctx context.Context, req UserUpdateRoleRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/update_role" + + _, err = i.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/sdn/address_pools.go b/pkg/sdn/address_pools.go new file mode 100644 index 0000000..004f33a --- /dev/null +++ b/pkg/sdn/address_pools.go @@ -0,0 +1,10 @@ +package sdn + +import ( + ap "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/adrspools" +) + +// Accessing the SDN method group +func (sdn *SDN) AddressPools() *ap.AddressPools { + return ap.New(sdn.client) +} diff --git a/pkg/sdn/adrspools/adress_pools.go b/pkg/sdn/adrspools/adress_pools.go new file mode 100644 index 0000000..1bae017 --- /dev/null +++ b/pkg/sdn/adrspools/adress_pools.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN adress pools +package adrspools + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to address pools +type AddressPools struct { + client interfaces.Caller +} + +// Builder for adress pools endpoints +func New(client interfaces.Caller) *AddressPools { + return &AddressPools{ + client, + } +} diff --git a/pkg/sdn/adrspools/create.go b/pkg/sdn/adrspools/create.go new file mode 100644 index 0000000..32f7ce7 --- /dev/null +++ b/pkg/sdn/adrspools/create.go @@ -0,0 +1,80 @@ +package adrspools + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create address pool +type CreateRequest struct { + // ID of the access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Description of the network + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Name of the network + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Network address type + // Required: true + NetAddressType string `url:"net_address_type" json:"net_address_type" validate:"required,addressPoolNetTypeValidator"` + + // List of network addresses + // Required: false + NetAddresses []NetAddress `url:"net_addresses,omitempty" json:"net_addresses,omitempty" validate:"dive"` +} + +// NetAddress struct representing network address +type NetAddress struct { + // Network address type + // Required: true + NetAddressType string `url:"net_address_type" json:"net_address_type" validate:"required,addressPoolNetTypeValidator"` + + // IP address + // Required: true + IPAddr string `url:"ip_addr" json:"ip_addr" validate:"required"` + + // End of IP address range + // Required: false + IPAddrRangeEnd string `url:"ip_addr_range_end,omitempty" json:"ip_addr_range_end,omitempty"` + + // IP prefix + // Required: false + IPPrefix string `url:"ip_prefix,omitempty" json:"ip_prefix,omitempty"` + + // MAC address + // Required: false + MACAddr string `url:"mac_addr,omitempty" json:"mac_addr,omitempty"` +} + +// Create creates a address pool +func (i AddressPools) Create(ctx context.Context, req CreateRequest) (*NetworkAddressPool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/address_pool/create" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := NetworkAddressPool{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/adrspools/delete.go b/pkg/sdn/adrspools/delete.go new file mode 100644 index 0000000..eb20422 --- /dev/null +++ b/pkg/sdn/adrspools/delete.go @@ -0,0 +1,51 @@ +package adrspools + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct to delete address pool +type DeleteRequest struct { + // Address pool ID + // Required: true + AddressPoolID string `url:"address_pool_id" json:"address_pool_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete + // Required: false + Force interface{} `url:"force,omitempty" json:"force,omitempty" validate:"omitempty,isBool"` +} + +// Delete an address pool +func (i AddressPools) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/address_pool/delete" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + if string(res) == "" { + return true, nil + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/sdn/adrspools/filter.go b/pkg/sdn/adrspools/filter.go new file mode 100644 index 0000000..6c64333 --- /dev/null +++ b/pkg/sdn/adrspools/filter.go @@ -0,0 +1,42 @@ +package adrspools + +// FilterByID returns AddressPoolsList with specified ID. +func (agl AddressPoolsList) FilterByID(id string) AddressPoolsList { + predicate := func(ia NetworkAddressPool) bool { + return ia.ID == id + } + + return agl.FilterFunc(predicate) +} + +// FilterByName returns AddressPoolsList with specified Name. +func (agl AddressPoolsList) FilterByName(name string) AddressPoolsList { + predicate := func(ia NetworkAddressPool) bool { + return ia.Name == name + } + + return agl.FilterFunc(predicate) +} + +// FilterFunc allows filtering AddressPoolsList based on a user-specified predicate. +func (agl AddressPoolsList) FilterFunc(predicate func(NetworkAddressPool) bool) AddressPoolsList { + var result AddressPoolsList + + for _, acc := range agl.Pools { + if predicate(acc) { + result.Pools = append(result.Pools, acc) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (agl AddressPoolsList) FindOne() NetworkAddressPool { + if len(agl.Pools) == 0 { + return NetworkAddressPool{} + } + + return agl.Pools[0] +} diff --git a/pkg/sdn/adrspools/filter_test.go b/pkg/sdn/adrspools/filter_test.go new file mode 100644 index 0000000..813f749 --- /dev/null +++ b/pkg/sdn/adrspools/filter_test.go @@ -0,0 +1,151 @@ +package adrspools + +import ( + "testing" +) + +var testAddressPools = AddressPoolsList{ + Pools: []NetworkAddressPool{ + { + ID: "pool1", + Name: "DevelopersPool", + Description: "First pool", + CreatedAt: "2023-01-01", + AccessGroupID: "group1", + AccessGroupName: "Developers", + NetAddressType: "IPv4", + NetAddresses: []NetworkAddress{ + { + ID: "addr1", + IPAddr: "192.168.1.1", + NetAddressType: "IPv4", + NetAddressPoolID: "pool1", + }, + }, + PoolCounters: PoolCounters{ + SecurityRules: 5, + }, + VersionID: 1, + }, + { + ID: "pool2", + Name: "AdminsPool", + Description: "Second pool", + CreatedAt: "2023-01-02", + AccessGroupID: "group2", + AccessGroupName: "Admins", + NetAddressType: "IPv4", + NetAddresses: []NetworkAddress{ + { + ID: "addr2", + IPAddr: "192.168.1.2", + NetAddressType: "IPv4", + NetAddressPoolID: "pool2", + }, + }, + PoolCounters: PoolCounters{ + SecurityRules: 3, + }, + VersionID: 2, + }, + { + ID: "pool3", + Name: "UsersPool", + Description: "Third pool", + CreatedAt: "2023-01-03", + AccessGroupID: "group3", + AccessGroupName: "Users", + NetAddressType: "IPv6", + NetAddresses: []NetworkAddress{ + { + ID: "addr3", + IPAddr: "2001:db8::1", + NetAddressType: "IPv6", + NetAddressPoolID: "pool3", + }, + }, + PoolCounters: PoolCounters{ + SecurityRules: 7, + }, + VersionID: 3, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testAddressPools.FilterByID("pool2").FindOne() + + if actual.ID != "pool2" { + t.Fatal("actual:", actual.ID, "> expected: pool2") + } +} + +func TestFilterByName(t *testing.T) { + actual := testAddressPools.FilterByName("UsersPool").FindOne() + + if actual.Name != "UsersPool" { + t.Fatal("actual:", actual.Name, ">> expected: UsersPool") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testAddressPools.FilterFunc(func(ap NetworkAddressPool) bool { + return ap.Description == "Second pool" + }) + + if len(actual.Pools) != 1 || actual.Pools[0].ID != "pool2" { + t.Fatal("Expected 1 pool with description 'Second pool', found:", len(actual.Pools)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testAddressPools.FilterByID("pool1").FindOne() + if result.ID != "pool1" { + t.Fatal("Expected pool1, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := AddressPoolsList{} + result := emptyList.FindOne() + + if result.ID != "" || result.Name != "" { + t.Fatal("Expected empty NetworkAddressPool, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := testAddressPools.FilterByID("nonexistent") + + if len(actual.Pools) != 0 { + t.Fatal("Expected 0 pools, found:", len(actual.Pools)) + } +} + +func TestFilterByNameNotFound(t *testing.T) { + actual := testAddressPools.FilterByName("Nonexistent Pool") + + if len(actual.Pools) != 0 { + t.Fatal("Expected 0 pools, found:", len(actual.Pools)) + } +} + +func TestFilterByNetAddressType(t *testing.T) { + actual := testAddressPools.FilterFunc(func(ap NetworkAddressPool) bool { + return ap.NetAddressType == "IPv6" + }) + + if len(actual.Pools) != 1 || actual.Pools[0].ID != "pool3" { + t.Fatal("Expected 1 pool with IPv6 type, found:", len(actual.Pools)) + } +} + +func TestFilterBySecurityRulesCount(t *testing.T) { + actual := testAddressPools.FilterFunc(func(ap NetworkAddressPool) bool { + return ap.PoolCounters.SecurityRules > 4 + }) + + if len(actual.Pools) != 2 { + t.Fatal("Expected 2 pools with more than 4 security rules, found:", len(actual.Pools)) + } +} diff --git a/pkg/sdn/adrspools/get.go b/pkg/sdn/adrspools/get.go new file mode 100644 index 0000000..4194181 --- /dev/null +++ b/pkg/sdn/adrspools/get.go @@ -0,0 +1,47 @@ +package adrspools + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about address group +type GetRequest struct { + // ID an address group + // Required: true + ID string `url:"address_pool_id" json:"address_pool_id" validate:"required"` +} + +// Get gets address pool details as a NetworkAddressPool struct +func (a AddressPools) Get(ctx context.Context, req GetRequest) (*NetworkAddressPool, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := NetworkAddressPool{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets address pool details as an array of bytes +func (a AddressPools) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/address_pool/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/adrspools/ids.go b/pkg/sdn/adrspools/ids.go new file mode 100644 index 0000000..92c19a7 --- /dev/null +++ b/pkg/sdn/adrspools/ids.go @@ -0,0 +1,10 @@ +package adrspools + +// IDs gets array of IDs from AddressPoolsList struct +func (agl AddressPoolsList) IDs() []string { + res := make([]string, 0, len(agl.Pools)) + for _, c := range agl.Pools { + res = append(res, c.ID) + } + return res +} diff --git a/pkg/sdn/adrspools/list.go b/pkg/sdn/adrspools/list.go new file mode 100644 index 0000000..5d4920e --- /dev/null +++ b/pkg/sdn/adrspools/list.go @@ -0,0 +1,92 @@ +package adrspools + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListAddressPoolsRequest struct to get a list of a groups +type ListAddressPoolsRequest struct { + // Filter by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Filter by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Filter by minimum address count (greater than or equal to) + // Required: false + AddrNumberMin uint64 `url:"addr_number_min,omitempty" json:"addr_number_min,omitempty"` + + // Filter by maximum address count (less than) + // Required: false + AddrNumberMax uint64 `url:"addr_number_max,omitempty" json:"addr_number_max,omitempty"` + + // Updated at lower bound (greater than or equal to) + // Required: false + UpdatedFrom string `url:"updated_from,omitempty" json:"updated_from,omitempty"` + + // Updated at upper bound (less than) + // Required: false + UpdatedTo string `url:"updated_to,omitempty" json:"updated_to,omitempty"` + + // Created at lower bound (greater than or equal to) + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Created at upper bound (less than) + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (name, addr_count, created_at, updated_at) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` +} + +// List of address pools +func (i AddressPools) List(ctx context.Context, req ListAddressPoolsRequest) (*AddressPoolsList, error) { + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + pools := []NetworkAddressPool{} + + err = json.Unmarshal(res, &pools) + if err != nil { + return nil, err + } + + result := AddressPoolsList{Pools: pools} + + return &result, nil +} + +// ListRaw gets a list of all address pools as an array of bytes +func (a AddressPools) ListRaw(ctx context.Context, req ListAddressPoolsRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/access_group/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/adrspools/models.go b/pkg/sdn/adrspools/models.go new file mode 100644 index 0000000..97d9508 --- /dev/null +++ b/pkg/sdn/adrspools/models.go @@ -0,0 +1,62 @@ +package adrspools + +type AddressPoolsList struct { + Pools []NetworkAddressPool +} + +// Main information about network address pool +type NetworkAddressPool struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt string `json:"created_at"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Description + Description string `json:"description"` + + // ID + ID string `json:"id"` + + // Name + Name string `json:"name"` + + // Network address type + NetAddressType string `json:"net_address_type"` + + // List of network addresses + NetAddresses []NetworkAddress `json:"net_addresses"` + + // Pool counters + PoolCounters PoolCounters `json:"pool_counters"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Network address information +type NetworkAddress struct { + // ID + ID string `json:"id"` + + // IP address + IPAddr string `json:"ip_addr"` + + // Network address type + NetAddressType string `json:"net_address_type"` + + // Network address pool ID + NetAddressPoolID string `json:"net_address_pool_id"` +} + +// Pool counters information +type PoolCounters struct { + // Security rules count + SecurityRules uint64 `json:"security_rules"` +} diff --git a/pkg/sdn/adrspools/serialize.go b/pkg/sdn/adrspools/serialize.go new file mode 100644 index 0000000..e605298 --- /dev/null +++ b/pkg/sdn/adrspools/serialize.go @@ -0,0 +1,43 @@ +package adrspools + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 AddressPoolsList) Serialize(params ...string) (serialization.Serialized, error) { + if len(la.Pools) == 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 NetworkAddressPool) 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/sdn/adrspools/update.go b/pkg/sdn/adrspools/update.go new file mode 100644 index 0000000..7943571 --- /dev/null +++ b/pkg/sdn/adrspools/update.go @@ -0,0 +1,84 @@ +package adrspools + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update address pool +type UpdateRequest struct { + // ID of the address pool + // Required: true + AddressPoolID string `url:"address_pool_id" json:"address_pool_id" validate:"required"` + + // ID of the version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Description of the network + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Name of the network + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Network address type + // Required: true + NetAddressType string `url:"net_address_type" json:"net_address_type" validate:"required,addressPoolNetTypeValidator"` + + // List of network addresses + // Required: false + NetAddresses []UpdateNetAddress `url:"net_addresses,omitempty" json:"net_addresses,omitempty" validate:"dive"` +} + +// UpdateNetAddress struct representing network address +type UpdateNetAddress struct { + // Network address type + // Required: true + NetAddressType string `url:"net_address_type" json:"net_address_type" validate:"required,addressPoolNetTypeValidator"` + + // IP address + // Required: true + IPAddr string `url:"ip_addr" json:"ip_addr" validate:"required"` + + // End of IP address range + // Required: false + IPAddrRangeEnd string `url:"ip_addr_range_end,omitempty" json:"ip_addr_range_end,omitempty"` + + // IP prefix + // Required: false + IPPrefix string `url:"ip_prefix,omitempty" json:"ip_prefix,omitempty"` + + // MAC address + // Required: false + MACAddr string `url:"mac_addr,omitempty" json:"mac_addr,omitempty"` +} + +// Update updates a address pool +func (i AddressPools) Update(ctx context.Context, req UpdateRequest) (*NetworkAddressPool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/address_pool/update" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := NetworkAddressPool{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/default_security_policies.go b/pkg/sdn/default_security_policies.go new file mode 100644 index 0000000..5867402 --- /dev/null +++ b/pkg/sdn/default_security_policies.go @@ -0,0 +1,10 @@ +package sdn + +import ( + dsp "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/defsecpolicies" +) + +// Accessing the SDN method group +func (sdn *SDN) DefaultSecurityPolicies() *dsp.DefaultSecurityPolicies { + return dsp.New(sdn.client) +} diff --git a/pkg/sdn/defsecpolicies/default_security_policies.go b/pkg/sdn/defsecpolicies/default_security_policies.go new file mode 100644 index 0000000..5f62f8e --- /dev/null +++ b/pkg/sdn/defsecpolicies/default_security_policies.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN default secirity policies +package defsecpolicies + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to default security policies +type DefaultSecurityPolicies struct { + client interfaces.Caller +} + +// Builder for adress pools endpoints +func New(client interfaces.Caller) *DefaultSecurityPolicies { + return &DefaultSecurityPolicies{ + client, + } +} diff --git a/pkg/sdn/defsecpolicies/filter.go b/pkg/sdn/defsecpolicies/filter.go new file mode 100644 index 0000000..d094997 --- /dev/null +++ b/pkg/sdn/defsecpolicies/filter.go @@ -0,0 +1,42 @@ +package defsecpolicies + +// FilterByID returns SecurityPoliciesList with specified ID. +func (agl SecurityPoliciesList) FilterByID(id string) SecurityPoliciesList { + predicate := func(ia SecurityPolicy) bool { + return ia.ID == id + } + + return agl.FilterFunc(predicate) +} + +// FilterByName returns SecurityPoliciesList with specified Name. +func (agl SecurityPoliciesList) FilterByName(name string) SecurityPoliciesList { + predicate := func(ia SecurityPolicy) bool { + return ia.DisplayName == name + } + + return agl.FilterFunc(predicate) +} + +// FilterFunc allows filtering SecurityPoliciesList based on a user-specified predicate. +func (agl SecurityPoliciesList) FilterFunc(predicate func(SecurityPolicy) bool) SecurityPoliciesList { + var result SecurityPoliciesList + + for _, acc := range agl.Policies { + if predicate(acc) { + result.Policies = append(result.Policies, acc) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (agl SecurityPoliciesList) FindOne() SecurityPolicy { + if len(agl.Policies) == 0 { + return SecurityPolicy{} + } + + return agl.Policies[0] +} diff --git a/pkg/sdn/defsecpolicies/filter_test.go b/pkg/sdn/defsecpolicies/filter_test.go new file mode 100644 index 0000000..df89ba7 --- /dev/null +++ b/pkg/sdn/defsecpolicies/filter_test.go @@ -0,0 +1,268 @@ +package defsecpolicies + +import ( + "testing" +) + +var testSecurityPolicies = SecurityPoliciesList{ + Policies: []SecurityPolicy{ + { + ID: "policy1", + DisplayName: "DevelopersPolicy", + Description: "First policy", + CreatedAt: "2023-01-01T00:00:00Z", + UpdatedAt: "2023-01-01T01:00:00Z", + AccessGroupID: "group1", + DefaultACLDrop: "DROP", + DefaultOpenSessionDrop: true, + SecurityRules: []SecurityRule{ + { + ID: "rule1", + DisplayName: "DevRule1", + Action: "ALLOW", + Direction: "INGRESS", + Enabled: true, + Priority: 100, + SecurityPolicyID: "policy1", + VersionID: 1, + }, + }, + Status: Status{ + Common: "ACTIVE", + Hypervisors: []HypervisorStatus{ + { + Name: "hv1", + DisplayName: "Hypervisor1", + Status: "SYNCED", + HypervisorStatus: "HEALTHY", + SyncedAt: "2023-01-01T01:00:00Z", + }, + }, + }, + VersionID: 1, + }, + { + ID: "policy2", + DisplayName: "AdminsPolicy", + Description: "Second policy", + CreatedAt: "2023-01-02T00:00:00Z", + UpdatedAt: "2023-01-02T01:00:00Z", + AccessGroupID: "group2", + DefaultACLDrop: "REJECT", + DefaultOpenSessionDrop: false, + SecurityRules: []SecurityRule{ + { + ID: "rule2", + DisplayName: "AdminRule1", + Action: "DENY", + Direction: "EGRESS", + Enabled: true, + Priority: 50, + SecurityPolicyID: "policy2", + VersionID: 1, + }, + }, + Status: Status{ + Common: "ACTIVE", + Hypervisors: []HypervisorStatus{ + { + Name: "hv2", + DisplayName: "Hypervisor2", + Status: "SYNCED", + HypervisorStatus: "HEALTHY", + SyncedAt: "2023-01-02T01:00:00Z", + }, + }, + }, + VersionID: 2, + }, + { + ID: "policy3", + DisplayName: "UsersPolicy", + Description: "Third policy", + CreatedAt: "2023-01-03T00:00:00Z", + UpdatedAt: "2023-01-03T01:00:00Z", + AccessGroupID: "group3", + DefaultACLDrop: "DROP", + DefaultOpenSessionDrop: true, + SecurityRules: []SecurityRule{ + { + ID: "rule3", + DisplayName: "UserRule1", + Action: "ALLOW", + Direction: "INGRESS", + Enabled: false, + Priority: 200, + SecurityPolicyID: "policy3", + VersionID: 1, + }, + }, + Status: Status{ + Common: "PENDING", + Hypervisors: []HypervisorStatus{ + { + Name: "hv3", + DisplayName: "Hypervisor3", + Status: "SYNCING", + HypervisorStatus: "HEALTHY", + SyncedAt: "2023-01-03T01:00:00Z", + }, + }, + }, + VersionID: 3, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testSecurityPolicies.FilterByID("policy2").FindOne() + + if actual.ID != "policy2" { + t.Fatal("actual:", actual.ID, "> expected: policy2") + } +} + +func TestFilterByDisplayName(t *testing.T) { + actual := testSecurityPolicies.FilterByName("UsersPolicy").FindOne() + + if actual.DisplayName != "UsersPolicy" { + t.Fatal("actual:", actual.DisplayName, ">> expected: UsersPolicy") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + return sp.Description == "Second policy" + }) + + if len(actual.Policies) != 1 || actual.Policies[0].ID != "policy2" { + t.Fatal("Expected 1 policy with description 'Second policy', found:", len(actual.Policies)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testSecurityPolicies.FilterByID("policy1").FindOne() + if result.ID != "policy1" { + t.Fatal("Expected policy1, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := SecurityPoliciesList{} + result := emptyList.FindOne() + + if result.ID != "" || result.DisplayName != "" { + t.Fatal("Expected empty SecurityPolicy, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := testSecurityPolicies.FilterByID("nonexistent") + + if len(actual.Policies) != 0 { + t.Fatal("Expected 0 policies, found:", len(actual.Policies)) + } +} + +func TestFilterByDisplayNameNotFound(t *testing.T) { + actual := testSecurityPolicies.FilterByName("Nonexistent Policy") + + if len(actual.Policies) != 0 { + t.Fatal("Expected 0 policies, found:", len(actual.Policies)) + } +} + +func TestFilterByDefaultACLDrop(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + return sp.DefaultACLDrop == "DROP" + }) + + if len(actual.Policies) != 2 { + t.Fatal("Expected 2 policies with DROP default ACL, found:", len(actual.Policies)) + } +} + +func TestFilterByDefaultOpenSessionDrop(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + return sp.DefaultOpenSessionDrop == true + }) + + if len(actual.Policies) != 2 { + t.Fatal("Expected 2 policies with default open session drop enabled, found:", len(actual.Policies)) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + return sp.Status.Common == "ACTIVE" + }) + + if len(actual.Policies) != 2 { + t.Fatal("Expected 2 policies with ACTIVE status, found:", len(actual.Policies)) + } +} + +func TestFilterByAccessGroupID(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + return sp.AccessGroupID == "group1" + }) + + if len(actual.Policies) != 1 || actual.Policies[0].ID != "policy1" { + t.Fatal("Expected 1 policy with access group ID 'group1', found:", len(actual.Policies)) + } +} + +func TestFilterByRuleAction(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + for _, rule := range sp.SecurityRules { + if rule.Action == "ALLOW" { + return true + } + } + return false + }) + + if len(actual.Policies) != 2 { + t.Fatal("Expected 2 policies with ALLOW rules, found:", len(actual.Policies)) + } +} + +func TestFilterByRuleDirection(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + for _, rule := range sp.SecurityRules { + if rule.Direction == "INGRESS" { + return true + } + } + return false + }) + + if len(actual.Policies) != 2 { + t.Fatal("Expected 2 policies with INGRESS rules, found:", len(actual.Policies)) + } +} + +func TestFilterByRuleEnabled(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + for _, rule := range sp.SecurityRules { + if rule.Enabled { + return true + } + } + return false + }) + + if len(actual.Policies) != 2 { + t.Fatal("Expected 2 policies with enabled rules, found:", len(actual.Policies)) + } +} + +func TestFilterByVersionID(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicy) bool { + return sp.VersionID > 1 + }) + + if len(actual.Policies) != 2 { + t.Fatal("Expected 2 policies with version ID > 1, found:", len(actual.Policies)) + } +} diff --git a/pkg/sdn/defsecpolicies/ids.go b/pkg/sdn/defsecpolicies/ids.go new file mode 100644 index 0000000..ff767e5 --- /dev/null +++ b/pkg/sdn/defsecpolicies/ids.go @@ -0,0 +1,10 @@ +package defsecpolicies + +// IDs gets array of IDs from SecurityPoliciesList struct +func (spl SecurityPoliciesList) IDs() []string { + res := make([]string, 0, len(spl.Policies)) + for _, c := range spl.Policies { + res = append(res, c.ID) + } + return res +} diff --git a/pkg/sdn/defsecpolicies/list.go b/pkg/sdn/defsecpolicies/list.go new file mode 100644 index 0000000..1956103 --- /dev/null +++ b/pkg/sdn/defsecpolicies/list.go @@ -0,0 +1,64 @@ +package defsecpolicies + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of default security group +type ListRequest struct { + // Filter by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (name, addr_count, created_at, updated_at) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` +} + +// List of default security policies +func (i DefaultSecurityPolicies) List(ctx context.Context, req ListRequest) (*SecurityPoliciesList, error) { + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + policies := []SecurityPolicy{} + + err = json.Unmarshal(res, &policies) + if err != nil { + return nil, err + } + + result := SecurityPoliciesList{Policies: policies} + + return &result, nil +} + +// ListRaw gets a list of all default security policies as an array of bytes +func (a DefaultSecurityPolicies) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/default_security_policy/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/defsecpolicies/models.go b/pkg/sdn/defsecpolicies/models.go new file mode 100644 index 0000000..c356562 --- /dev/null +++ b/pkg/sdn/defsecpolicies/models.go @@ -0,0 +1,191 @@ +package defsecpolicies + +type SecurityPoliciesList struct { + Policies []SecurityPolicy `json:"policies"` +} + +// Main information about security policy +type SecurityPolicy struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Created time + CreatedAt string `json:"created_at"` + + // Default ACL drop behavior + DefaultACLDrop string `json:"default_acl_drop"` + + // Default open session drop flag + DefaultOpenSessionDrop bool `json:"default_open_session_drop"` + + // Description + Description string `json:"description"` + + // Display name + DisplayName string `json:"display_name"` + + // ID + ID string `json:"id"` + + // Security rules + SecurityRules []SecurityRule `json:"security_rules"` + + // Locked time + LockedAt string `json:"locked_at"` + + // Status information + Status Status `json:"status"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // Updated time + UpdatedAt string `json:"updated_at"` +} + +// Security rule information +type SecurityRule struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Action + Action string `json:"action"` + + // Description + Description string `json:"description"` + + // Destination network object + DestinationNetObject NetObject `json:"destination_net_object"` + + // Direction + Direction string `json:"direction"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // Filter configuration + Filter Filter `json:"filter"` + + // ID + ID string `json:"id"` + + // Log enabled flag + LogEnabled bool `json:"log_enabled"` + + // Log name + LogName string `json:"log_name"` + + // Log severity + LogSeverity string `json:"log_severity"` + + // Priority + Priority int `json:"priority"` + + // Security policy ID + SecurityPolicyID string `json:"security_policy_id"` + + // Source network object + SourceNetObject NetObject `json:"source_net_object"` + + // Statistics enabled flag + StatisticsEnabled bool `json:"statistics_enabled"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Network object information +type NetObject struct { + // Display name + DisplayName string `json:"display_name"` + + // Network address pool ID + NetAddressPoolID string `json:"net_address_pool_id"` + + // Network object group ID + NetObjectGroupID string `json:"net_object_group_id"` +} + +// Filter configuration +type Filter struct { + // Filter parameters + Filters FilterParams `json:"filters"` + + // Name + Name string `json:"name"` +} + +// Filter parameters +type FilterParams struct { + // All protocols flag + All bool `json:"all"` + + // ARP protocol flag + ARP bool `json:"arp"` + + // DHCP protocol flag + DHCP bool `json:"dhcp"` + + // Filter expression + Expression string `json:"expression"` + + // ICMP protocol flag + ICMP bool `json:"icmp"` + + // IP protocol flag + IP bool `json:"ip"` + + // IPv4 protocol flag + IPv4 bool `json:"ip_v4"` + + // IPv6 protocol flag + IPv6 bool `json:"ip_v6"` + + // Keep opened sessions flag + KeepOpenedSessions bool `json:"keep_opened_sessions"` + + // ND protocol flag + ND bool `json:"nd"` + + // TCP protocol flag + TCP bool `json:"tcp"` + + // TCP destination ports + TCPDstPorts []string `json:"tcp_dst_ports"` + + // UDP protocol flag + UDP bool `json:"udp"` + + // UDP destination ports + UDPDstPorts []string `json:"udp_dst_ports"` +} + +// Status information +type Status struct { + // Common status + Common string `json:"common"` + + // Hypervisor statuses + Hypervisors []HypervisorStatus `json:"hypervisors"` +} + +// Hypervisor status information +type HypervisorStatus struct { + // Status + Status string `json:"status"` + + // Name + Name string `json:"name"` + + // Display name + DisplayName string `json:"display_name"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` + + // Last sync time + SyncedAt string `json:"synced_at"` +} diff --git a/pkg/sdn/defsecpolicies/serialize.go b/pkg/sdn/defsecpolicies/serialize.go new file mode 100644 index 0000000..3508599 --- /dev/null +++ b/pkg/sdn/defsecpolicies/serialize.go @@ -0,0 +1,43 @@ +package defsecpolicies + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 SecurityPoliciesList) Serialize(params ...string) (serialization.Serialized, error) { + if len(la.Policies) == 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 SecurityPolicy) 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/sdn/defsecpolicies/update.go b/pkg/sdn/defsecpolicies/update.go new file mode 100644 index 0000000..34339d1 --- /dev/null +++ b/pkg/sdn/defsecpolicies/update.go @@ -0,0 +1,53 @@ +package defsecpolicies + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update default security policy +type UpdateRequest struct { + // ID of the access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // ID of the version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Default ACL drop behavior + // Required: false + DefaultACLDrop string `url:"default_acl_drop,omitempty" json:"default_acl_drop,omitempty"` + + // Default open session drop flag + // Required: false + DefaultOpenSessionDrop bool `url:"default_open_session_drop,omitempty" json:"default_open_session_drop,omitempty"` +} + +// Update updates a default security policy +func (i DefaultSecurityPolicies) Update(ctx context.Context, req UpdateRequest) (*SecurityPolicy, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/default_security_policy/update" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPatch, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := SecurityPolicy{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/extnet.go b/pkg/sdn/extnet.go new file mode 100644 index 0000000..cc91dc2 --- /dev/null +++ b/pkg/sdn/extnet.go @@ -0,0 +1,10 @@ +package sdn + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/extnet" +) + +// Accessing the ExtNet method group +func (sdn *SDN) ExtNet() *extnet.ExtNet { + return extnet.New(sdn.client) +} diff --git a/pkg/sdn/extnet/create.go b/pkg/sdn/extnet/create.go new file mode 100644 index 0000000..db2655c --- /dev/null +++ b/pkg/sdn/extnet/create.go @@ -0,0 +1,729 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + "time" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct for creating account +type CreateRequest struct { + // Name of the bridge network + // Required: true + BridgeNetworkName string `url:"bridge_network_name" json:"bridge_network_name" validate:"required"` + + // Detailed description of the external network + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // User-friendly name for the external network + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the network is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // List of hypervisor names + // Required: true + Hypervisors []string `url:"hypervisors" json:"hypervisors" validate:"required"` + + // List of external network ports + // Required: false + ExternalNetworkPorts []ExternalNetworkPortRequest `url:"-" json:"external_network_ports,omitempty"` + + // IPv4 default gateway address + // Required: false + DefaultGatewayIPv4 string `url:"default_gateway_ipv4,omitempty" json:"default_gateway_ipv4,omitempty"` + + // IPv6 default gateway address + // Required: false + DefaultGatewayIPv6 string `url:"default_gateway_ipv6,omitempty" json:"default_gateway_ipv6,omitempty"` + + // IPv4 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV4 string `url:"subnet_v4,omitempty" json:"subnet_v4,omitempty"` + + // IPv6 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV6 string `url:"subnet_v6,omitempty" json:"subnet_v6,omitempty"` + + // VLAN tag identifier + // Required: false + VLANTag string `url:"vlan_tag,omitempty" json:"vlan_tag,omitempty" validate:"omitempty,trunkTags"` +} + +type ExternalNetworkPortRequest struct { + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Access group name + // Required: false + AccessGroupName string `url:"access_group_name,omitempty" json:"access_group_name,omitempty"` + + // Comment for the external network port + // Required: true + Comment string `url:"comment" json:"comment" validate:"required"` + + // User-friendly name for the external network port + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the network pork is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // IPv4 + // Required: false + IPv4 string `url:"ipv4,omitempty" json:"ipv4,omitempty"` + + // IPv6 + // Required: false + IPv6 string `url:"ipv6,omitempty" json:"ipv6,omitempty"` + + // IPv6 Config + // Required: false + IPv6Config *IPv6ConfigRequest `url:"-" json:"ipv6_config,omitempty"` + + // MAC address + // Required: true + MAC string `url:"mac" json:"mac" validate:"required"` + + // Router gateway port + // Required: false + RouterGatewayPort *RouterGatewayPortRequest `url:"-" json:"router_gateway_port,omitempty"` + + // Floating IP + // Required: false + FloatingIP *FloatingIPRequest `url:"-" json:"floating_ip,omitempty"` +} + +type IPv6ConfigRequest struct { + //Address Mode (Slaac or DhcpV6Stateful) + // Required: true + AddressMode string `url:"address_mode" json:"address_mode" validate:"required"` + + // If true, the port will periodically send RA packets. + // Required: true + EnablePeriodicRa bool `url:"enable_periodic_ra" json:"enable_periodic_ra"` + + // The number of waiting seconds between sending periodic RA + // Required: true + IntervalRa int64 `url:"interval_ra" json:"interval_ra" validate:"required"` + + // The Default Router Preference (PRF) indicates whether this router should be preferred over other default routers. + // high, low, medium + // Required: true + RouterPreference string `url:"router_preference" json:"router_preference" validate:"required"` +} + +type RouterGatewayPortRequest struct { + // Created at + // Required: true + CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"` + + // Description + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Port id + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // User-friendly name for the external network port + // Required: true + RouterDisplayName string `url:"router_display_name" json:"router_display_name" validate:"required"` + + // Router ID + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` + + // SNAT Enabled + // Required: true + SNATEnabled bool `url:"snat_enabled" json:"snat_enabled"` + + // Updated at + // Required: true + UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"` +} + +type FloatingIPRequest struct { + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Access group name + // Required: true + AccessGroupName string `url:"access_group_name" json:"access_group_name" validate:"required"` + + // Created at + // Required: true + CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"` + + // External network port + // Required: true + ExternalNetworkPort string `url:"external_network_port" json:"external_network_port" validate:"required"` + + // ID of the Floating IP + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // Logical port + // Required: false + LogicalPort *LogicalPortRequest `url:"-" json:"logical_port,omitempty"` + + // Router + // Required: true + Router *RouterRequest `url:"-" json:"router" validate:"required"` + + // Updated at + // Required: true + UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +type LogicalPortRequest struct { + // Logical Port ID + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Access group name + // Required: true + AccessGroupName string `url:"access_group_name" json:"access_group_name" validate:"required"` + + // MAC of adapter + // Required: true + AdapterMAC string `url:"adapter_mac" json:"adapter_mac" validate:"required"` + + // Address detection + // Required: true + AddressDetection bool `url:"address_detection" json:"address_detection" validate:"required"` + + // Description + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Created at + // Required: false + CreatedAt time.Time `url:"created_at,omitempty" json:"created_at,omitempty"` + + // User-friendly name for router + // Required: true + RouterDisplayName string `url:"router_display_name" json:"router_display_name" validate:"required"` + + // Whether the logical pork is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // External Network ID + // Required: false + ExternalNetworkID string `url:"external_network_id,omitempty" json:"external_network_id,omitempty"` + + // Hypervisor + // Required: true + Hypervisor string `url:"hypervisor" json:"hypervisor" validate:"required"` + + // User-friendly name for hypervisor + // Required: false + HypervisorDisplayName string `url:"hypervisor_display_name,omitempty" json:"hypervisor_display_name,omitempty"` + + // Live Migration Target Hv + // Required: true + LiveMigrationTargetHV string `url:"live_migration_target_hv" json:"live_migration_target_hv" validate:"required"` + + // Status + // Required: true + Status *StatusRequest `url:"-" json:"status" validate:"required"` + + // Port bindings + // Required: true + Bindings *PortBindingsRequest `url:"-" json:"bindings" validate:"required"` + + // Unique Identifier + // Required: true + UniqueIDentifier string `url:"unique_identifier" json:"unique_identifier" validate:"required"` + + // Updated at + // Required: true + UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +type StatusRequest struct { + // Common + // Required: true + Common string `url:"common" json:"common" validate:"required"` + + // Hypervisors status + // Required: false + Hypervisors []HypervisorStatusRequest `url:"-" json:"hypervisors,omitempty"` +} + +type PortBindingsRequest struct { + // Binding ID + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // User-friendly name for segment + // Required: true + SegmentDisplayName string `url:"segment_display_name" json:"segment_display_name" validate:"required"` + + // Segment ID + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` + + // Port security + // Required: true + PortSecurity bool `url:"port_security" json:"port_security" validate:"required"` + + // Address detection + // Required: true + AddressDetection bool `url:"address_detection" json:"address_detection" validate:"required"` + + // Is Exclude From Firewall + // Required: true + IsExcludedFromFirewall bool `url:"is_excluded_from_firewall" json:"is_excluded_from_firewall" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Created at + // Required: true + CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"` + + // Updated at + // Required: true + UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"` + + // Logical port addresses + // Required: true + LogicalPortAddresses []LogicalPortAddressRequest `url:"-" json:"logical_port_addresses" validate:"required"` +} + +type HypervisorStatusRequest struct { + // Status + // Required: true + Status string `url:"status" json:"status" validate:"required"` + + // Name of hypervisor + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // User-friendly name for the hypervisor + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Hypervisor status + // Required: true + HypervisorStatus string `url:"hypervisor_status" json:"hypervisor_status" validate:"required"` + + // Synced at + // Required: true + SyncedAt time.Time `url:"synced_at" json:"synced_at" validate:"required"` +} + +type LogicalPortAddressRequest struct { + // IP of port + // Required: true + IP string `url:"ip" json:"ip" validate:"required"` + + // IP type (IPv4 or IPv6) + // Required: true + IPType string `url:"ip_type" json:"ip_type" validate:"required"` + + // Is discovered + // Required: false + IsDiscovered bool `url:"is_discovered,omitempty" json:"is_discovered,omitempty"` + + // Is discovered + // Required: true + IsPrimary bool `url:"is_primary" json:"is_primary" validate:"required"` + + // MAC + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` + + // ID + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // Logical port id + // Required: true + LogicalPortID string `url:"logical_port_id" json:"logical_port_id" validate:"required"` + + // Assigned at + // Required: false + AssignedAt time.Time `url:"assigned_at,omitempty" json:"assigned_at,omitempty"` +} + +type RouterRequest struct { + // Access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Access group name + // Required: false + AccessGroupName string `url:"access_group_name,omitempty" json:"access_group_name,omitempty"` + + // Created at + // Required: false + CreatedAt time.Time `url:"created_at,omitempty" json:"created_at,omitempty"` + + // Detailed description of the router + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // User-friendly name for the router + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the router is enabled + // Required: false + Enabled bool `url:"enabled,omitempty" json:"enabled,omitempty"` + + // Gateway ports + // Required: false + GatewayPorts []GatewayPortRequest `url:"-" json:"gateway_ports,omitempty"` + + // ID + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // Policies + // Required: false + Policies []RouterPolicyRequest `url:"-" json:"policies,omitempty"` + + // Ports + // Required: false + Ports []RouterPortRequest `url:"-" json:"ports,omitempty"` + + // Status + // Required: false + Status *StatusRequest `url:"-" json:"status,omitempty"` + + // Updated at + // Required: false + UpdatedAt time.Time `url:"updated_at,omitempty" json:"updated_at,omitempty"` + + // ID of version + // Required: false + VersionID uint64 `url:"version_id,omitempty" json:"version_id,omitempty"` +} + +type GatewayPortRequest struct { + // Created at + // Required: true + CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"` + + // Description + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // L4 port max + // Required: false + ExternalL4PortMax int64 `url:"external_l4_port_max,omitempty" json:"external_l4_port_max,omitempty"` + + // L4 port min + // Required: false + ExternalL4PortMin int64 `url:"external_l4_port_min,omitempty" json:"external_l4_port_min,omitempty"` + + // External network port + // Required: true + ExternalNetworkPort interface{} `url:"external_network_port" json:"external_network_port" validate:"required"` + + // ID of port + // Required: false + ID string `url:"id,omitempty" json:"id,omitempty"` + + // SNAT Enabled + // Required: true + SNATEnabled bool `url:"snat_enabled" json:"snat_enabled"` + + // Status + // Required: false + Status *StatusRequest `url:"-" json:"status,omitempty"` + + // Updated at + // Required: true + UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +type RouterPolicyRequest struct { + // Action + // Required: true + Action string `url:"action" json:"action" validate:"required"` + + // Created at + // Required: true + CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"` + + // User-friendly name for the policy + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the policy is enabled + // Required: false + Enabled bool `url:"enabled,omitempty" json:"enabled,omitempty"` + + // ID of port + // Required: false + ID string `url:"id,omitempty" json:"id,omitempty"` + + // Match + // Required: true + Match interface{} `url:"match" json:"match" validate:"required"` + + // Next IPv4 address + // Required: true + NextIPv4Address []string `url:"next_ipv4_address" json:"next_ipv4_address" validate:"required"` + + // Next IPv6 address + // Required: true + NextIPv6Address []string `url:"next_ipv6_address" json:"next_ipv6_address" validate:"required"` + + // Priority + // Required: true + Priority int64 `url:"priority" json:"priority" validate:"required"` + + // Updated at + // Required: true + UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +type RouterPortRequest struct { + // Created at + // Required: true + CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"` + + // Detailed description of the router port + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Whether the router port is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // ID of port + // Required: false + ID string `url:"id,omitempty" json:"id,omitempty"` + + // Next IPv4 address + // Required: true + NextIPv4Address []string `url:"next_ipv4_address" json:"next_ipv4_address" validate:"required"` + + // Next IPv6 address + // Required: true + NextIPv6Address []string `url:"next_ipv6_address" json:"next_ipv6_address" validate:"required"` + + // IPv6 Config + // Required: true + IPv6Config *IPv6ConfigRequest `url:"-" json:"ipv6_config" validate:"required"` + + // MAC address + // Required: true + MAC string `url:"mac" json:"mac" validate:"required"` + + // Segment + // Required: true + Segment *SegmentRequest `url:"-" json:"segment" validate:"required"` + + // Segment ID + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` + + // Status + // Required: false + Status *StatusRequest `url:"-" json:"status,omitempty"` + + // Updated at + // Required: true + UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +type SegmentRequest struct { + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Access group name + // Required: false + AccessGroupName string `url:"access_group_name,omitempty" json:"access_group_name,omitempty"` + + // Created at + // Required: true + CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"` + + // Detailed description of the router port + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // DHCP IPv4 + // Required: false + DHCPv4 *DHCPv4ConfigRequest `url:"-" json:"dhcp_v4,omitempty"` + + // DHCP IPv6 + // Required: false + DHCPv6 *DHCPv6ConfigRequest `url:"-" json:"dhcp_v6,omitempty"` + + // User-friendly name for the segment + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the segment is enabled + // Required: false + Enabled bool `url:"enabled,omitempty" json:"enabled,omitempty"` + + // ID of segment + // Required: false + ID string `url:"id,omitempty" json:"id,omitempty"` + + // Logical ports info + // Required: false + LogicalPortsInfo []EntityInfoRequest `url:"-" json:"logical_ports_info,omitempty"` + + // Routers info + // Required: false + RoutersInfo []EntityInfoRequest `url:"-" json:"routers_info,omitempty"` + + // Status + // Required: false + Status *StatusRequest `url:"-" json:"status,omitempty"` + + // IPv4 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV4 string `url:"subnet_v4,omitempty" json:"subnet_v4,omitempty"` + + // IPv6 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV6 string `url:"subnet_v6,omitempty" json:"subnet_v6,omitempty"` + + // Updated at + // Required: true + UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +type DHCPv4ConfigRequest struct { + // DNS + // Required: false + DNS []string `url:"dns,omitempty" json:"dns,omitempty"` + + // Excluded address ranges + // Required: false + ExcludedAddressRanges []string `url:"excluded_address_ranges,omitempty" json:"excluded_address_ranges,omitempty"` + + // Gateway + // Required: true + Gateway string `url:"gateway" json:"gateway" validate:"required"` + + // ID of config + // Required: false + ID string `url:"id,omitempty" json:"id,omitempty"` + + // Lease time + // Required: false + LeaseTime int64 `url:"lease_time,omitempty" json:"lease_time,omitempty"` + + // Server IP + // Required: true + ServerIP string `url:"server_ip" json:"server_ip" validate:"required"` + + // Server MAC + // Required: false + ServerMAC string `url:"server_mac,omitempty" json:"server_mac,omitempty"` + + // Whether the config is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` +} + +type DHCPv6ConfigRequest struct { + // Address prefix + // Required: true + AddressPrefix string `url:"address_prefix" json:"address_prefix" validate:"required"` + + // DNS + // Required: false + DNS []string `url:"dns,omitempty" json:"dns,omitempty"` + + // ID of config + // Required: false + ID string `url:"id,omitempty" json:"id,omitempty"` + + // Lease time + // Required: true + LeaseTime int64 `url:"lease_time" json:"lease_time" validate:"required"` + + // Server MAC + // Required: true + ServerMAC string `url:"server_mac" json:"server_mac" validate:"required"` + + // Whether the config is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` +} + +type EntityInfoRequest struct { + // User-friendly name for the entity + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // ID of entity + // Required: false + ID string `url:"id,omitempty" json:"id,omitempty"` +} + +// Create creates extnet +func (e ExtNet) Create(ctx context.Context, req CreateRequest) (*ExternalNetworkResponse, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/external_network/create" + + res, err := e.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := ExternalNetworkResponse{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/extnet/delete.go b/pkg/sdn/extnet/delete.go new file mode 100644 index 0000000..13aff85 --- /dev/null +++ b/pkg/sdn/extnet/delete.go @@ -0,0 +1,42 @@ +package extnet + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct for delete extnet +type DeleteRequest struct { + // ID of external network + // Required: true + ExtNetID string `url:"external_network_id" json:"external_network_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Delete delete an external network +func (e ExtNet) Delete(ctx context.Context, req DeleteRequest) error { + err := validators.ValidateRequest(req) + if err != nil { + return validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/external_network/delete" + + _, err = e.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + + if err != nil { + return err + } + + return nil +} diff --git a/pkg/sdn/extnet/extnet.go b/pkg/sdn/extnet/extnet.go new file mode 100644 index 0000000..64de75f --- /dev/null +++ b/pkg/sdn/extnet/extnet.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN external networks +package extnet + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to external networks +type ExtNet struct { + client interfaces.Caller +} + +// Builder for external networks endpoints +func New(client interfaces.Caller) *ExtNet { + return &ExtNet{ + client, + } +} diff --git a/pkg/sdn/extnet/filter.go b/pkg/sdn/extnet/filter.go new file mode 100644 index 0000000..30a79ad --- /dev/null +++ b/pkg/sdn/extnet/filter.go @@ -0,0 +1,60 @@ +package extnet + +// FilterByID returns ListExtNet with specified ID. +func (eList ListExtNet) FilterByID(id string) ListExtNet { + predicate := func(extNet ExternalNetworkResponse) bool { + return extNet.ID == id + } + + return eList.FilterFunc(predicate) +} + +// FilterByName returns ListExtNet with specified Bridge network name. +func (eList ListExtNet) FilterByName(name string) ListExtNet { + predicate := func(extNet ExternalNetworkResponse) bool { + return extNet.BridgeNetworkName == name + } + + return eList.FilterFunc(predicate) +} + +// FilterByIPv4 returns ListExtNet with specified default gateway IPv4. +func (eList ListExtNet) FilterByIPv4(IPv4 string) ListExtNet { + predicate := func(extNet ExternalNetworkResponse) bool { + return extNet.DefaultGatewayIPv4 == IPv4 + } + + return eList.FilterFunc(predicate) +} + +// FilterByIPv6 returns ListExtNet with specified default gateway IPv6. +func (eList ListExtNet) FilterByIPv6(IPv6 string) ListExtNet { + predicate := func(extNet ExternalNetworkResponse) bool { + return extNet.DefaultGatewayIPv6 == IPv6 + } + + return eList.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListExtNet based on a user-specified predicate. +func (eList ListExtNet) FilterFunc(predicate func(response ExternalNetworkResponse) bool) ListExtNet { + var result ListExtNet + + for _, item := range eList { + if predicate(item) { + result = append(result, item) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (eList ListExtNet) FindOne() ExternalNetworkResponse { + if len(eList) == 0 { + return ExternalNetworkResponse{} + } + + return eList[0] +} diff --git a/pkg/sdn/extnet/filter_test.go b/pkg/sdn/extnet/filter_test.go new file mode 100644 index 0000000..f542776 --- /dev/null +++ b/pkg/sdn/extnet/filter_test.go @@ -0,0 +1,149 @@ +package extnet + +import ( + "testing" + "time" +) + +var testExtnetList = ListExtNet{ + { + BridgeNetworkName: "br-ext-net-01", + DefaultGatewayIPv4: "192.168.1.1", + DefaultGatewayIPv6: "2001:db8::1", + Description: "test1", + Hypervisors: []string{ + "hv-node-01", + }, + ID: "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + VersionID: 1747141621952, + SubnetV4: "192.168.1.0/24", + SubnetV6: "2001:db8::/64", + CreatedAt: time.Date(2025, 10, 21, 20, 34, 30, 641000000, time.UTC), + UpdatedAt: time.Date(2025, 10, 21, 20, 34, 30, 641000000, time.UTC), + VLANTag: 100, + }, + { + BridgeNetworkName: "br-backup-net-02", + DefaultGatewayIPv4: "10.0.1.1", + DefaultGatewayIPv6: "2001:db8:1::1", + Description: "test2", + Hypervisors: []string{ + "hv-node-02", + }, + ID: "b2c3d4e5-f6g7-8901-bcde-f23456789012", + VersionID: 1747141621953, + SubnetV4: "10.0.1.0/24", + SubnetV6: "2001:db8:1::/64", + CreatedAt: time.Date(2025, 10, 21, 20, 34, 30, 641000000, time.UTC), + UpdatedAt: time.Date(2025, 10, 21, 20, 34, 30, 641000000, time.UTC), + VLANTag: 200, + }, + { + BridgeNetworkName: "br-test-net-03", + DefaultGatewayIPv4: "172.16.1.1", + DefaultGatewayIPv6: "2001:db8:2::1", + Description: "test3", + ExternalNetworkPorts: []ExternalNetworkPort{}, + Hypervisors: []string{ + "hv-node-05", + "hv-node-06", + }, + ID: "c3d4e5f6-g7h8-9012-cdef-345678901234", + VersionID: 1747141621954, + SubnetV4: "172.16.1.0/24", + SubnetV6: "2001:db8:2::/64", + CreatedAt: time.Date(2025, 10, 21, 20, 34, 30, 641000000, time.UTC), + UpdatedAt: time.Date(2025, 10, 21, 20, 34, 30, 641000000, time.UTC), + VLANTag: 300, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testExtnetList.FilterByID("a1b2c3d4-e5f6-7890-abcd-ef1234567890").FindOne() + + if actual.ID != "a1b2c3d4-e5f6-7890-abcd-ef1234567890" { + t.Fatal("actual:", actual.ID, "> expected: a1b2c3d4-e5f6-7890-abcd-ef1234567890") + } +} + +func TestFilterByName(t *testing.T) { + actual := testExtnetList.FilterByName("br-ext-net-01").FindOne() + + if actual.BridgeNetworkName != "br-ext-net-01" { + t.Fatal("actual:", actual.BridgeNetworkName, ">> expected: br-ext-net-01") + } +} + +func TestFilterByIPv4(t *testing.T) { + actual := testExtnetList.FilterByIPv4("192.168.1.1").FindOne() + + if actual.DefaultGatewayIPv4 != "192.168.1.1" { + t.Fatal("actual:", actual.DefaultGatewayIPv4, ">> expected: 192.168.1.1") + } +} + +func TestFilterByIPv6(t *testing.T) { + actual := testExtnetList.FilterByIPv6("2001:db8:1::1").FindOne() + + if actual.DefaultGatewayIPv6 != "2001:db8:1::1" { + t.Fatal("actual:", actual.DefaultGatewayIPv6, ">> expected: 2001:db8:1::1") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testExtnetList.FilterFunc(func(response ExternalNetworkResponse) bool { + return response.BridgeNetworkName == "br-backup-net-02" + }) + + if len(actual) != 1 || actual[0].ID != "b2c3d4e5-f6g7-8901-bcde-f23456789012" { + t.Fatal("Expected 1 extnet with name 'br-backup-net-02', found:", len(actual)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testExtnetList.FilterByID("c3d4e5f6-g7h8-9012-cdef-345678901234").FindOne() + if result.ID != "c3d4e5f6-g7h8-9012-cdef-345678901234" { + t.Fatal("Expected c3d4e5f6-g7h8-9012-cdef-345678901234, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := ListExtNet{} + result := emptyList.FindOne() + + if result.ID != "" || result.BridgeNetworkName != "" { + t.Fatal("Expected empty extNet, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := ListExtNet{}.FilterByID("nonexistent") + + if len(actual) != 0 { + t.Fatal("Expected 0 extNet, found:", len(actual)) + } +} + +func TestFilterByNameNotFound(t *testing.T) { + actual := ListExtNet{}.FilterByName("Nonexistent") + + if len(actual) != 0 { + t.Fatal("Expected 0 extNet, found:", len(actual)) + } +} + +func TestFilterByIPv4NotFound(t *testing.T) { + actual := testExtnetList.FilterByIPv4("nonexistent-ip") + + if len(actual) != 0 { + t.Fatal("Expected 0 extNet, found:", len(actual)) + } +} + +func TestFilterByIPv6NotFound(t *testing.T) { + actual := testExtnetList.FilterByIPv6("nonexistent-ipv6") + + if len(actual) != 0 { + t.Fatal("Expected 0 extNet, found:", len(actual)) + } +} diff --git a/pkg/sdn/extnet/get.go b/pkg/sdn/extnet/get.go new file mode 100644 index 0000000..c509e5a --- /dev/null +++ b/pkg/sdn/extnet/get.go @@ -0,0 +1,50 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about external network +type GetRequest struct { + // ID of external network + // Required: false + ExtNetID string `url:"external_network_id" json:"external_network_id" validate:"required"` + + // ID of access group + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` +} + +// Get gets external network details as a ExternalNetworkResponse struct +func (e ExtNet) Get(ctx context.Context, req GetRequest) (*ExternalNetworkResponse, error) { + res, err := e.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := ExternalNetworkResponse{} + + 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) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/external_network/get" + + res, err := e.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/extnet/ids.go b/pkg/sdn/extnet/ids.go new file mode 100644 index 0000000..7745499 --- /dev/null +++ b/pkg/sdn/extnet/ids.go @@ -0,0 +1,10 @@ +package extnet + +// IDs gets array of IDs from ListExtNet struct +func (eList ListExtNet) IDs() []string { + res := make([]string, 0, len(eList)) + for _, item := range eList { + res = append(res, item.ID) + } + return res +} diff --git a/pkg/sdn/extnet/list.go b/pkg/sdn/extnet/list.go new file mode 100644 index 0000000..2ca4526 --- /dev/null +++ b/pkg/sdn/extnet/list.go @@ -0,0 +1,122 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of external networks +type ListRequest struct { + // Filter by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Filter by display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Filter by IP version (v4 or v6) + // Required: false + Subnet string `url:"subnet,omitempty" json:"subnet,omitempty" validate:"omitempty,ipTypes"` + + // Filter by IPv4 subnet (CIDR notation) + // Required: false + SubnetV4 string `url:"subnet_v4,omitempty" json:"subnet_v4,omitempty"` + + // Filter by IPv4 subnet (CIDR notation) + // Required: false + SubnetV6 string `url:"subnet_v6,omitempty" json:"subnet_v6,omitempty"` + + // Filter by exact bridge network name + // Required: false + BridgeNetworkName string `url:"bridge_network_name,omitempty" json:"bridge_network_name,omitempty"` + + // Filter by VLAN tag + // Required: false + VLANTag string `url:"vlan_tag,omitempty" json:"vlan_tag,omitempty"` + + // Filter by IPv4 default gateway + // Required: false + DefaultGatewayIPv4 string `url:"default_gateway_ipv4,omitempty" json:"default_gateway_ipv4,omitempty"` + + // Filter by IPv6 default gateway + // Required: false + DefaultGatewayIPv6 string `url:"default_gateway_ipv6,omitempty" json:"default_gateway_ipv6,omitempty"` + + // Filter by enabled status + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Filter by update date from + // Required: false + UpdatedFrom string `url:"updated_from,omitempty" json:"updated_from,omitempty"` + + // Filter lby update date to + // Required: false + UpdatedTo string `url:"updated_to,omitempty" json:"updated_to,omitempty"` + + // Filter by create date from + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Filter by create date to + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` + + // Filter by operation status + // Required: false + OperationStatus string `url:"operation_status,omitempty" json:"operation_status,omitempty"` + + // Filter by hypervisor status + // Required: false + HypervisorStatus string `url:"hypervisor_status,omitempty" json:"hypervisor_status,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, created_at, updated_at, deleted_at, etc) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,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 := "/sdn/external_network/list" + + res, err := e.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/extnet/models.go b/pkg/sdn/extnet/models.go new file mode 100644 index 0000000..2886442 --- /dev/null +++ b/pkg/sdn/extnet/models.go @@ -0,0 +1,578 @@ +package extnet + +import "time" + +// List external networks +type ListExtNet []ExternalNetworkResponse + +type ExternalNetworkResponse struct { + // Name of the bridge network + BridgeNetworkName string `json:"bridge_network_name"` + + // IPv4 default gateway address + DefaultGatewayIPv4 string `json:"default_gateway_ipv4"` + + // IPv6 default gateway address + DefaultGatewayIPv6 string `json:"default_gateway_ipv6"` + + // Detailed description of the external network + Description string `json:"description"` + + // List of external network ports + ExternalNetworkPorts []ExternalNetworkPort `json:"external_network_ports"` + + // List of hypervisor names + Hypervisors []string `json:"hypervisors"` + + // Extnet ID + ID string `json:"id"` + + // Status + Status Status `json:"status"` + + // ID of version + VersionID uint64 `json:"version_id"` + + // IPv4 subnet in CIDR notation + SubnetV4 string `json:"subnet_v4"` + + // IPv6 subnet in CIDR notation + SubnetV6 string `json:"subnet_v6"` + + // Creation time + CreatedAt time.Time `json:"created_at"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // VLAN tag identifier + VLANTag int64 `json:"vlan_tag"` +} + +type ExternalNetworkPort struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Comment for the external network port + Comment string `json:"comment"` + + // User-friendly name for the external network port + DisplayName string `json:"display_name"` + + // Whether the network port is enabled + Enabled bool `json:"enabled"` + + // IPv4 + IPv4 string `json:"ipv4"` + + // IPv6 + IPv6 string `json:"ipv6"` + + // IPv6 Config + IPv6Config IPv6Config `json:"ipv6_config"` + + // MAC address + MAC string `json:"mac"` + + // Router gateway port + RouterGatewayPort RouterGatewayPort `json:"router_gateway_port"` + + // Floating IP + FloatingIP FloatingIP `json:"floating_ip"` +} + +type IPv6Config struct { + // Address mode + AddressMode string `json:"address_mode"` + + // If true, the port will periodically send RA packets. + EnablePeriodicRa bool `json:"enable_periodic_ra"` + + // The number of waiting seconds between sending periodic RA + IntervalRa int64 `json:"interval_ra"` + + // The Default Router Preference (PRF) indicates whether this router should be preferred over other default routers. + RouterPreference string `json:"router_preference"` +} + +type RouterGatewayPort struct { + // Creation time + CreatedAt time.Time `json:"created_at"` + + // Description + Description string `json:"description"` + + // Port ID + ID string `json:"id"` + + // User-friendly name for the external network port + RouterDisplayName string `json:"router_display_name"` + + // Router ID + RouterID string `json:"router_id"` + + // SNAT Enabled + SNATEnabled bool `json:"snat_enabled"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` +} + +type FloatingIP struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // External network port + ExternalNetworkPort string `json:"external_network_port"` + + // ID of floating IP + ID string `json:"id"` + + // Logical Port + LogicalPort LogicalPort `json:"logical_port"` + + // Router + Router Router `json:"router"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // ID of version + VersionID uint64 `json:"version_id"` +} + +type LogicalPort struct { + // Logical Port ID + ID string `json:"id"` + + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // MAC of adapter + AdapterMAC string `json:"adapter_mac"` + + // Address detection + AddressDetection bool `json:"address_detection"` + + // Description + Description string `json:"description"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // User-friendly name for router + DisplayName string `json:"display_name"` + + // Whether the logical port is enabled + Enabled bool `json:"enabled"` + + // External Network ID + ExternalNetworkID string `json:"external_network_id"` + + // Hypervisor name + Hypervisor string `json:"hypervisor"` + + // User-friendly name for hypervisor + HypervisorDisplayName string `json:"hypervisor_display_name"` + + // Live Migration Target Hv + LiveMigrationTargetHV string `json:"live_migration_target_hv"` + + // Status + Status Status `json:"status"` + + // Bindings struct + Bindings PortBindings `json:"bindings"` + + // Unique Identifier + UniqueIDentifier string `json:"unique_identifier"` + + // Updated time + UpdatedAt time.Time `json:"updated_at"` + + // ID of version + VersionID uint64 `json:"version_id"` +} + +type PortBindings struct { + // Binding ID + ID string `json:"id"` + + // User-friendly name for segment + SegmentDisplayName string `json:"segment_display_name"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Port security + PortSecurity bool `json:"port_security"` + + // Address detection + AddressDetection bool `json:"address_detection"` + + // Is Exclude From Firewall + IsExcludedFromFirewall bool `json:"is_excluded_from_firewall"` + + // ID of version + VersionID uint64 `json:"version_id"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // Logical port addresses + LogicalPortAddresses []LogicalPortAddress `json:"logical_port_addresses"` +} + +type LogicalPortAddress struct { + // IP of port + IP string `json:"ip"` + + // IP type (IPv4 or IPv6) + IPType string `json:"ip_type"` + + // Is discovered + IsDiscovered bool `json:"is_discovered"` + + // Is primary + IsPrimary bool `json:"is_primary"` + + // MAC + MAC string `json:"mac"` + + // ID + ID string `json:"id"` + + // Logical port id + LogicalPortID string `json:"logical_port_id"` + + // Assigned time + AssignedAt time.Time `json:"assigned_at"` +} + +type Router struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // Detailed description of the router + Description string `json:"description"` + + // User-friendly name for the router + DisplayName string `json:"display_name"` + + // Whether the router is enabled + Enabled bool `json:"enabled"` + + // Gateway ports + GatewayPorts []GatewayPort `json:"gateway_ports"` + + // ID of router + ID string `json:"id"` + + // Policies + Policies []RouterPolicy `json:"policies"` + + // Ports + Ports []RouterPort `json:"ports"` + + // Status + Status Status `json:"status"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // ID of version + VersionID uint64 `json:"version_id"` +} + +type GatewayPort struct { + // Created time + CreatedAt time.Time `json:"created_at"` + + // Description + Description string `json:"description"` + + // L4 port max + ExternalL4PortMax int64 `json:"external_l4_port_max"` + + // L4 port min + ExternalL4PortMin int64 `json:"external_l4_port_min"` + + // External network port + ExternalNetworkPort interface{} `json:"external_network_port"` + + // ID of port + ID string `json:"id"` + + // SNAT Enabled + SNATEnabled bool `json:"snat_enabled"` + + // Status + Status Status `json:"status"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // ID of version + VersionID uint64 `json:"version_id"` +} + +type RouterPolicy struct { + // Action + Action string `json:"action"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // User-friendly name for the policy + DisplayName string `json:"display_name"` + + // Whether the policy is enabled + Enabled bool `json:"enabled"` + + // ID of router policy + ID string `json:"id"` + + // Match + Match interface{} `json:"match"` + + // Next IPv4 address + NextIPv4Address []string `json:"next_ipv4_address"` + + // Next IPv6 address + NextIPv6Address []string `json:"next_ipv6_address"` + + // Priority number + Priority int64 `json:"priority"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // ID of version + VersionID uint64 `json:"version_id"` +} + +type RouterPort struct { + // Created at + CreatedAt time.Time `json:"created_at"` + + // Detailed description of the router port + Description string `json:"description"` + + // Whether the router port is enabled + Enabled bool `json:"enabled"` + + // ID of router port + ID string `json:"id"` + + // Next IPv4 address + IPv4Address string `json:"ipv4_address"` + + // Next IPv6 address + IPv6Address string `json:"ipv6_address"` + + // IPv6 Config + IPv6Config IPv6Config `json:"ipv6_config"` + + // MAC address + MAC string `json:"mac"` + + // Segment + Segment Segment `json:"segment"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Status + Status Status `json:"status"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // ID of version + VersionID uint64 `json:"version_id"` +} + +type Segment struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // Detailed description of the router port + Description string `json:"description"` + + // DHCP IPv4 + DHCPv4 DHCPv4Config `json:"dhcp_v4"` + + // DHCP IPv6 + DHCPv6 DHCPv6Config `json:"dhcp_v6"` + + // User-friendly name for the segment + DisplayName string `json:"display_name"` + + // Whether the segment is enabled + Enabled bool `json:"enabled"` + + // ID of segment + ID string `json:"id"` + + // Logical ports info + LogicalPortsInfo []EntityInfo `json:"logical_ports_info"` + + // Routers info + RoutersInfo []EntityInfo `json:"routers_info"` + + // Status + Status Status `json:"status"` + + // IPv4 subnet in CIDR notation + SubnetV4 string `json:"subnet_v4"` + + // IPv6 subnet in CIDR notation + SubnetV6 string `json:"subnet_v6"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // ID of version + VersionID uint64 `json:"version_id"` +} + +type DHCPv4Config struct { + // DNS + DNS []string `json:"dns"` + + // Excluded address ranges + ExcludedAddressRanges []string `json:"excluded_address_ranges"` + + // Gateway + Gateway string `json:"gateway"` + + // ID of config + ID string `json:"id"` + + // Lease time + LeaseTime int64 `json:"lease_time"` + + // Server IP + ServerIP string `json:"server_ip"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Whether the config is enabled + Enabled bool `json:"enabled"` +} + +type DHCPv6Config struct { + // Address prefix + AddressPrefix string `json:"address_prefix"` + + // DNS + DNS []string `json:"dns"` + + // ID of config + ID string `json:"id"` + + // Lease time + LeaseTime int64 `json:"lease_time"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Whether the config is enabled + Enabled bool `json:"enabled"` +} + +type EntityInfo struct { + // User-friendly name for the entity + DisplayName string `json:"display_name"` + + // ID of entity + ID string `json:"id"` +} + +type Status struct { + // Common + Common string `json:"common"` + + // Hypervisors status + Hypervisors []HypervisorStatus `json:"hypervisors"` +} + +type HypervisorStatus struct { + // Status + Status string `json:"status"` + + // Name of hypervisor + Name string `json:"name"` + + // User-friendly name for the hypervisor + DisplayName string `json:"display_name"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` + + // Synced time + SyncedAt time.Time `json:"synced_at"` +} + +type ExternalNetworkAddPortsResponce struct { + // ID of extnet port + ID string `json:"id"` + + // ID of extnet + ExtNetID string `json:"external_network_id"` + + // User-friendly name for the external network port + DisplayName string `json:"display_name"` + + // Comment + Comment string `json:"comment"` + + // Mac + MAC string `json:"mac"` + + // IPv4 gateway address + IPv4 string `json:"ipv4"` + + // IPv6 gateway address + IPv6 string `json:"ipv6"` + + // IPv6 config + IPv6Config IPv6Config `json:"ipv6_config"` + + // Whether the network port is enabled + Enabled bool `json:"enabled"` + + // ID of version + VersionID uint64 `json:"version_id"` + + // Router gateway port + RouterGatewayPort RouterGatewayPort `json:"router_gateway_port"` +} diff --git a/pkg/sdn/extnet/port_add.go b/pkg/sdn/extnet/port_add.go new file mode 100644 index 0000000..6a1d379 --- /dev/null +++ b/pkg/sdn/extnet/port_add.go @@ -0,0 +1,77 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// PortAddRequest struct for add port to extnet +type PortAddRequest struct { + // ID of external network + // Required: true + ExtNetID string `url:"external_network_id" json:"external_network_id" validate:"required"` + + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Description of the port addition operation + // Required: true + Comment string `url:"comment" json:"comment" validate:"required"` + + // User-friendly name for the external network + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the network is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // IPv4 + // Required: false + IPv4 string `url:"ipv4,omitempty" json:"ipv4,omitempty"` + + // IPv6 + // Required: false + IPv6 string `url:"ipv6,omitempty" json:"ipv6,omitempty"` + + // IPv6 Config + // Required: false + IPv6Config *IPv6ConfigRequest `url:"-" json:"ipv6_config,omitempty"` + + // MAC address + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` +} + +// AddPort added a port an external network +func (e ExtNet) AddPort(ctx context.Context, req PortAddRequest) (*ExternalNetworkAddPortsResponce, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/external_network/port_add" + + res, err := e.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := ExternalNetworkAddPortsResponce{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/extnet/port_update.go b/pkg/sdn/extnet/port_update.go new file mode 100644 index 0000000..7a84b0c --- /dev/null +++ b/pkg/sdn/extnet/port_update.go @@ -0,0 +1,85 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// PortUpdateRequest struct for update port to extnet +type PortUpdateRequest struct { + // ID of external network + // Required: true + ExtNetID string `url:"external_network_id" json:"external_network_id" validate:"required"` + + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Port ID + // Required: true + PortID string `url:"port_id" json:"port_id" validate:"required"` + + // Port version ID + // Required: true + PortVersionID uint64 `url:"port_version_id" json:"port_version_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Description of the port addition operation + // Required: true + Comment string `url:"comment" json:"comment" validate:"required"` + + // User-friendly name for the external network + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the network is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // IPv4 + // Required: false + IPv4 string `url:"ipv4,omitempty" json:"ipv4,omitempty"` + + // IPv6 + // Required: false + IPv6 string `url:"ipv6,omitempty" json:"ipv6,omitempty"` + + // IPv6 Config + // Required: false + IPv6Config *IPv6ConfigRequest `url:"-" json:"ipv6_config,omitempty"` + + // MAC address + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` +} + +// UpdatePort updated a port an external network +func (e ExtNet) UpdatePort(ctx context.Context, req PortUpdateRequest) (*ExternalNetworkResponse, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/external_network/port_update" + + res, err := e.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := ExternalNetworkResponse{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/extnet/update.go b/pkg/sdn/extnet/update.go new file mode 100644 index 0000000..ab38d12 --- /dev/null +++ b/pkg/sdn/extnet/update.go @@ -0,0 +1,89 @@ +package extnet + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct for update extnet +type UpdateRequest struct { + // ID of external network + // Required: true + ExtNetID string `url:"external_network_id" json:"external_network_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Bridge network name + // Required: true + BridgeNetworkName string `url:"bridge_network_name" json:"bridge_network_name" validate:"required"` + + // Detailed description of the external network + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // User-friendly name for the external network + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // List of hypervisor names + // Required: true + Hypervisors []string `url:"hypervisors" json:"hypervisors" validate:"required"` + + // Access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // IPv4 default gateway address + // Required: false + DefaultGatewayIPv4 string `url:"default_gateway_ipv4,omitempty" json:"default_gateway_ipv4,omitempty"` + + // IPv6 default gateway address + // Required: false + DefaultGatewayIPv6 string `url:"default_gateway_ipv6,omitempty" json:"default_gateway_ipv6,omitempty"` + + // Whether the network is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // IPv4 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV4 string `url:"subnet_v4,omitempty" json:"subnet_v4,omitempty"` + + // IPv6 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV6 string `url:"subnet_v6,omitempty" json:"subnet_v6,omitempty"` + + // VLAN tag identifier + // Required: false + VLANTag string `url:"vlan_tag,omitempty" json:"vlan_tag,omitempty" validate:"omitempty,trunkTags"` +} + +// Update updated an external network +func (e ExtNet) Update(ctx context.Context, req UpdateRequest) (*ExternalNetworkResponse, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/external_network/update" + + res, err := e.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := ExternalNetworkResponse{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/flips.go b/pkg/sdn/flips.go new file mode 100644 index 0000000..8e899f1 --- /dev/null +++ b/pkg/sdn/flips.go @@ -0,0 +1,10 @@ +package sdn + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/flips" +) + +// Accessing the SDN method group +func (sdn *SDN) FloatingIPs() *flips.FloatingIPs { + return flips.New(sdn.client) +} diff --git a/pkg/sdn/flips/create.go b/pkg/sdn/flips/create.go new file mode 100644 index 0000000..7c7aa3d --- /dev/null +++ b/pkg/sdn/flips/create.go @@ -0,0 +1,52 @@ +package flips + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type CreateRequest struct { + // Access Group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // ID of an external network port + // Required: true + ExtNetPortID string `url:"external_network_port_id" json:"external_network_port_id" validate:"required"` + + // ID of a logical network port + // Required: true + LogicalPortID string `url:"logical_port_id" json:"logical_port_id" validate:"required"` + + // ID of a router + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` +} + +// Create creates a floating ip +func (fi FloatingIPs) Create(ctx context.Context, req CreateRequest) (*RecordFloatingIP, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/floating_ip/create" + + res, err := fi.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordFloatingIP{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/flips/delete.go b/pkg/sdn/flips/delete.go new file mode 100644 index 0000000..84c1ed0 --- /dev/null +++ b/pkg/sdn/flips/delete.go @@ -0,0 +1,51 @@ +package flips + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest to delete a floating ip +type DeleteRequest struct { + // ID of a floating IP + // Required: true + FloatingIPID string `url:"floating_ip_id" json:"floating_ip_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Delete a floating ip +func (fi FloatingIPs) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/floating_ip/delete" + + res, err := fi.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + if string(res) == "" { + return true, nil + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/sdn/flips/filter.go b/pkg/sdn/flips/filter.go new file mode 100644 index 0000000..7ec25fe --- /dev/null +++ b/pkg/sdn/flips/filter.go @@ -0,0 +1,42 @@ +package flips + +// FilterByID returns FloatingIPsList with specified ID. +func (fil FloatingIPsList) FilterByID(id string) FloatingIPsList { + predicate := func(fi RecordFloatingIP) bool { + return fi.ID == id + } + + return fil.FilterFunc(predicate) +} + +// FilterByName returns FloatingIPsList with specified AccessGroupName. +func (fil FloatingIPsList) FilterByAccessGroupName(name string) FloatingIPsList { + predicate := func(fi RecordFloatingIP) bool { + return fi.AccessGroupName == name + } + + return fil.FilterFunc(predicate) +} + +// FilterFunc allows filtering FloatingIPsList based on a user-specified predicate. +func (fil FloatingIPsList) FilterFunc(predicate func(fi RecordFloatingIP) bool) FloatingIPsList { + var result FloatingIPsList + + for _, acc := range fil.Objects { + if predicate(acc) { + result.Objects = append(result.Objects, acc) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (fil FloatingIPsList) FindOne() RecordFloatingIP { + if len(fil.Objects) == 0 { + return RecordFloatingIP{} + } + + return fil.Objects[0] +} diff --git a/pkg/sdn/flips/filter_test.go b/pkg/sdn/flips/filter_test.go new file mode 100644 index 0000000..49666ec --- /dev/null +++ b/pkg/sdn/flips/filter_test.go @@ -0,0 +1,196 @@ +package flips + +import "testing" + +var testFloatingIPs = FloatingIPsList{ + Objects: []RecordFloatingIP{ + { + AccessGroupID: "testid", + AccessGroupName: "testname", + CreatedAt: "2025-09-23T08:05:59.271458Z", + ExternalNetworkPort: ExternalNetworkPort{ + AccessGroupID: "somegroup", + AccessGroupName: "somename", + Comment: "some comment", + DisplayName: "some display name", + Enabled: true, + ExternalNetworkID: "someid", + ID: "someid", + IPv4: "someipv4", + MAC: "somemac", + VersionID: 1111111111111, + }, + ID: "someid", + LogicalPort: LogicalPort{ + ID: "someid", + AccessGroupID: "someid", + AccessGroupName: "somename", + AdapterMAC: "somemac", + AddressDetection: false, + Description: "some description", + DisplayName: "some display name", + Enabled: true, + Hypervisor: "hypervisor", + HypervisorDisplayName: "hypervisor display name", + UniqueIdentifier: "someid", + VersionID: 1111111111111, + CreatedAt: "2025-09-23T08:05:59.271458Z", + UpdatedAt: "2025-09-23T08:05:59.271458Z", + }, + Router: Router{ + ID: "someid", + AccessGroupID: "someid", + AccessGroupName: "somename", + CreatedAt: "2025-09-23T08:05:59.271458Z", + DisplayName: "some display name", + Enabled: true, + UpdatedAt: "2025-09-23T08:05:59.271458Z", + VersionID: 1111111111111, + }, + UpdatedAt: "2025-09-23T08:05:59.271458Z", + VersionID: 1111111111111, + }, + { + AccessGroupID: "testid3", + AccessGroupName: "testname3", + CreatedAt: "2025-09-23T08:05:59.271458Z", + ExternalNetworkPort: ExternalNetworkPort{ + AccessGroupID: "somegroup", + AccessGroupName: "somename", + Comment: "some comment", + DisplayName: "some display name", + Enabled: true, + ExternalNetworkID: "someid2", + ID: "someid", + IPv4: "someipv4", + MAC: "somemac", + VersionID: 1111111111112, + }, + ID: "someid2", + LogicalPort: LogicalPort{ + ID: "someid2", + AccessGroupID: "someid", + AccessGroupName: "somename", + AdapterMAC: "somemac", + AddressDetection: false, + Description: "some description", + DisplayName: "some display name", + Enabled: true, + Hypervisor: "hypervisor", + HypervisorDisplayName: "hypervisor display name", + UniqueIdentifier: "someid", + VersionID: 1111111111112, + CreatedAt: "2025-09-23T08:05:59.271458Z", + UpdatedAt: "2025-09-23T08:05:59.271458Z", + }, + Router: Router{ + ID: "someid2", + AccessGroupID: "someid", + AccessGroupName: "somename", + CreatedAt: "2025-09-23T08:05:59.271458Z", + DisplayName: "some display name", + Enabled: true, + UpdatedAt: "2025-09-23T08:05:59.271458Z", + VersionID: 1111111111112, + }, + UpdatedAt: "2025-09-23T08:05:59.271458Z", + VersionID: 1111111111112, + }, + { + AccessGroupID: "testid3", + AccessGroupName: "testname3", + CreatedAt: "2025-09-23T08:05:59.271458Z", + ExternalNetworkPort: ExternalNetworkPort{ + AccessGroupID: "somegroup", + AccessGroupName: "somename", + Comment: "some comment", + DisplayName: "some display name", + Enabled: true, + ExternalNetworkID: "someid3", + ID: "someid3", + IPv4: "someipv4", + MAC: "somemac", + VersionID: 1111111111113, + }, + ID: "someid3", + LogicalPort: LogicalPort{ + ID: "someid3", + AccessGroupID: "someid", + AccessGroupName: "somename", + AdapterMAC: "somemac", + AddressDetection: false, + Description: "some description", + DisplayName: "some display name", + Enabled: true, + Hypervisor: "hypervisor", + HypervisorDisplayName: "hypervisor display name", + UniqueIdentifier: "someid", + VersionID: 1111111111113, + CreatedAt: "2025-09-23T08:05:59.271458Z", + UpdatedAt: "2025-09-23T08:05:59.271458Z", + }, + Router: Router{ + ID: "someid3", + AccessGroupID: "someid", + AccessGroupName: "somename", + CreatedAt: "2025-09-23T08:05:59.271458Z", + DisplayName: "some display name", + Enabled: true, + UpdatedAt: "2025-09-23T08:05:59.271458Z", + VersionID: 1111111111113, + }, + UpdatedAt: "2025-09-23T08:05:59.271458Z", + VersionID: 1111111111113, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testFloatingIPs.FilterByID("someid").FindOne() + + if actual.ID != "someid" { + t.Fatal("actual:", actual.ID, "> expected: someid") + } +} + +func TestFilterByName(t *testing.T) { + actual := testFloatingIPs.FilterByAccessGroupName("testname").FindOne() + + if actual.AccessGroupName != "testname" { + t.Fatal("actual:", actual.AccessGroupID, ">> expected: testname") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testFloatingIPs.FilterFunc(func(rfi RecordFloatingIP) bool { + return rfi.VersionID == 1111111111111 + }) + + if len(actual.Objects) != 1 || actual.Objects[0].ID != "someid" { + t.Fatal("Expected 1 policy with version ID 1111111111111, found:", len(actual.Objects)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testFloatingIPs.FilterByID("someid").FindOne() + if result.ID != "someid" { + t.Fatal("Expected someid, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := FloatingIPsList{} + result := emptyList.FindOne() + + if result.ID != "" || result.AccessGroupID != "" { + t.Fatal("Expected empty FloatingIP, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := testFloatingIPs.FilterByID("nonex") + + if len(actual.Objects) != 0 { + t.Fatal("Expected 0 policies, found:", len(actual.Objects)) + } +} diff --git a/pkg/sdn/flips/flips.go b/pkg/sdn/flips/flips.go new file mode 100644 index 0000000..8381a55 --- /dev/null +++ b/pkg/sdn/flips/flips.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN floating IPs +package flips + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to floating IPs +type FloatingIPs struct { + client interfaces.Caller +} + +// Builder for floating IPs endpoints +func New(client interfaces.Caller) *FloatingIPs { + return &FloatingIPs{ + client, + } +} diff --git a/pkg/sdn/flips/get.go b/pkg/sdn/flips/get.go new file mode 100644 index 0000000..282b853 --- /dev/null +++ b/pkg/sdn/flips/get.go @@ -0,0 +1,46 @@ +package flips + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type GetRequest struct { + // ID of a floating IP + // Required: true + FloatingIPID string `url:"floating_ip_id" json:"floating_ip_id" validate:"required"` +} + +// Get gets a floating ip details as a RecordFloatingIP struct +func (fi FloatingIPs) Get(ctx context.Context, req GetRequest) (*RecordFloatingIP, error) { + res, err := fi.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordFloatingIP{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets a floating ip details as an array of bytes +func (fi FloatingIPs) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/floating_ip/get" + + res, err := fi.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/flips/list.go b/pkg/sdn/flips/list.go new file mode 100644 index 0000000..20a5a31 --- /dev/null +++ b/pkg/sdn/flips/list.go @@ -0,0 +1,104 @@ +package flips + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// List of floating ips +type ListRequest struct { + // Filter by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + //Is the external network enabled + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Filter by Pv4 of the associated external network port + // Required: false + ExternalNetworkPortIPv4 string `url:"external_network_port_ipv4,omitempty" json:"external_network_port_ipv4,omitempty"` + + // Filter by IP of the associated logical port binding + // Required: false + LogicalPortBindingIP string `url:"logical_port_binding_ip,omitempty" json:"logical_port_binding_ip,omitempty"` + + // Display name of the associated logical port + // Required: false + LogicalPortDisplayName string `url:"logical_port_display_name,omitempty" json:"logical_port_display_name,omitempty"` + + // Filter by display name of the associated external network + // Required: false + ExternalNetworkDisplayName string `url:"external_network_display_name,omitempty" json:"external_network_display_name,omitempty"` + + // Filter by display name of the associated router + // Required: false + RouterDisplayName string `url:"router_display_name,omitempty" json:"router_display_name,omitempty"` + + // Updated at lower bound (greater than or equal to) + // Required: false + UpdatedFrom string `url:"updated_from,omitempty" json:"updated_from,omitempty"` + + // Updated at upper bound (less than) + // Required: false + UpdatedTo string `url:"updated_to,omitempty" json:"updated_to,omitempty"` + + // Created at lower bound (greater than or equal to) + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Created at upper bound (less than) + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (enabled, created_at, updated_at, external_network_port_ipv4, logical_port_binding_ip, logical_port_display_name, external_network_display_name, router_display_name) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` +} + +// List of floating ips +func (fi FloatingIPs) List(ctx context.Context, req ListRequest) (*FloatingIPsList, error) { + res, err := fi.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + objects := []RecordFloatingIP{} + + err = json.Unmarshal(res, &objects) + if err != nil { + return nil, err + } + + result := FloatingIPsList{Objects: objects} + + return &result, nil +} + +// ListRaw gets a list of all floating ips as an array of bytes +func (fi FloatingIPs) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/floating_ip/list" + + res, err := fi.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/flips/models.go b/pkg/sdn/flips/models.go new file mode 100644 index 0000000..a08632a --- /dev/null +++ b/pkg/sdn/flips/models.go @@ -0,0 +1,145 @@ +package flips + +// List of floating ips +type FloatingIPsList struct { + Objects []RecordFloatingIP +} + +// Main info about a floating ip +type RecordFloatingIP struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created at + CreatedAt string `json:"created_at"` + + // Details of an external network port + ExternalNetworkPort ExternalNetworkPort `json:"external_network_port"` + + // ID of a floating IP + ID string `json:"id"` + + // Details of a logical port + LogicalPort LogicalPort `json:"logical_port"` + + // Details of a router + Router Router `json:"router"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Info about a router +type Router struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created at + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Info about a logical port +type LogicalPort struct { + + // ID + ID string `json:"id"` + + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Adapter MAC + AdapterMAC string `json:"adapter_mac"` + + // Is address detected + AddressDetection bool `json:"address_detection"` + + // Description + Description string `json:"description"` + + // Created at + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a logical port enabled + Enabled bool `json:"enabled"` + + // Hypervisor + Hypervisor string `json:"hypervisor"` + + // Hypervisor display name + HypervisorDisplayName string `json:"hypervisor_display_name"` + + // Unique identifier + UniqueIdentifier string `json:"unique_identifier"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Details of external network ports +type ExternalNetworkPort struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Comment + Comment string `json:"comment"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` + + // External network ID + ExternalNetworkID string `json:"external_network_id"` + + // ID + ID string `json:"id"` + + // IP v4 + IPv4 string `json:"ipv4"` + + // MAC + MAC string `json:"mac"` + + // Version ID + VersionID uint64 `json:"version_id"` +} diff --git a/pkg/sdn/flips/update.go b/pkg/sdn/flips/update.go new file mode 100644 index 0000000..0a53e2f --- /dev/null +++ b/pkg/sdn/flips/update.go @@ -0,0 +1,57 @@ +package flips + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update a floating ip +type UpdateRequest struct { + // ID of a floating IP + // Required: true + FloatingIPID string `url:"floating_ip_id" json:"floating_ip_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // ID of an external network port + // Required: true + ExtNetPortID string `url:"external_network_port_id" json:"external_network_port_id" validate:"required"` + + // ID of a logical network port + // Required: true + LogicalPortID string `url:"logical_port_id" json:"logical_port_id" validate:"required"` + + // ID of a router + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` +} + +// Update updates a floating ip +func (fi FloatingIPs) Update(ctx context.Context, req UpdateRequest) (*RecordFloatingIP, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/floating_ip/update" + + res, err := fi.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordFloatingIP{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/hypervisors.go b/pkg/sdn/hypervisors.go new file mode 100644 index 0000000..557a97e --- /dev/null +++ b/pkg/sdn/hypervisors.go @@ -0,0 +1,10 @@ +package sdn + +import ( + hv "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/hypervisors" +) + +// Accessing the SDN method group +func (sdn *SDN) Hypervisors() *hv.Hypervisors { + return hv.New(sdn.client) +} diff --git a/pkg/sdn/hypervisors/connect_node.go b/pkg/sdn/hypervisors/connect_node.go new file mode 100644 index 0000000..1adab19 --- /dev/null +++ b/pkg/sdn/hypervisors/connect_node.go @@ -0,0 +1,30 @@ +package hypervisors + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ConnectNodeRequest to connect a node +type ConnectNodeRequest struct { + // Node to connect + // Required: true + NodeID uint64 `url:"node_id" json:"node_id" validate:"required"` +} + +func (hv Hypervisors) ConnectNode(ctx context.Context, req ConnectNodeRequest) (string, error) { + err := validators.ValidateRequest(req) + if err != nil { + return "", validators.ValidationErrors(validators.GetErrors(err)) + } + url := "/sdn/hypervisor/connect_node" + result, err := hv.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return "", err + } + + return string(result), nil +} diff --git a/pkg/sdn/hypervisors/delete.go b/pkg/sdn/hypervisors/delete.go new file mode 100644 index 0000000..271b87d --- /dev/null +++ b/pkg/sdn/hypervisors/delete.go @@ -0,0 +1,43 @@ +package hypervisors + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest to delete a hypervisor +type DeleteRequest struct { + // Name of a hypervisor + // Required: true + Name string `url:"name" json:"name" validate:"required"` +} + +// Delete a hypervisor +func (hv Hypervisors) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/hypervisor/delete" + + res, err := hv.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + if string(res) == "" { + return true, nil + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/sdn/hypervisors/get.go b/pkg/sdn/hypervisors/get.go new file mode 100644 index 0000000..103e5cc --- /dev/null +++ b/pkg/sdn/hypervisors/get.go @@ -0,0 +1,50 @@ +package hypervisors + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about hypervisor +type GetRequest struct { + // Name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Port info (available options: detailed, general) + // Required: false + PortInfo string `url:"port_info,omitempty" json:"port_info,omitempty"` +} + +// Get gets current configuration of a hypervisor as a RecordHypervisor +func (hv Hypervisors) Get(ctx context.Context, req GetRequest) (*RecordHypervisor, error) { + res, err := hv.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordHypervisor{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets information about a hypervisor as an array of bytes +func (hv Hypervisors) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/hypervisor/get" + + res, err := hv.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/hypervisors/hypervisors.go b/pkg/sdn/hypervisors/hypervisors.go new file mode 100644 index 0000000..6820737 --- /dev/null +++ b/pkg/sdn/hypervisors/hypervisors.go @@ -0,0 +1,17 @@ +package hypervisors + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to hypervisors +type Hypervisors struct { + client interfaces.Caller +} + +// Builder for hypervisors endpoints +func New(client interfaces.Caller) *Hypervisors { + return &Hypervisors{ + client, + } +} diff --git a/pkg/sdn/hypervisors/list.go b/pkg/sdn/hypervisors/list.go new file mode 100644 index 0000000..50d09e9 --- /dev/null +++ b/pkg/sdn/hypervisors/list.go @@ -0,0 +1,90 @@ +package hypervisors + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of hypervisors +type ListRequest struct { + // Page + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Sort by (available options: name, hostname, last_sync, display_name, ip, created_at, updated_at) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (available options: asc, desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` + + // Port info (available options: detailed, general) + // Required: false + PortInfo string `url:"port_info,omitempty" json:"port_info,omitempty"` + + // Hostname + // Required: false + Hostname string `url:"hostname,omitempty" json:"hostname,omitempty"` + + // Display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // IP + // Required: false + IP string `url:"ip,omitempty" json:"ip,omitempty"` + + // Created from + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Created to + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` + + // Updated from + // Required: false + UpdatedFrom string `url:"updated_from,omitempty" json:"updated_from,omitempty"` + + // Updated to + // Required: false + UpdatedTo string `url:"updated_to,omitempty" json:"updated_to,omitempty"` +} + +// List of hypervisors +func (hv Hypervisors) List(ctx context.Context, req ListRequest) (HypervisorsList, error) { + res, err := hv.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + hvs := HypervisorsList{} + + err = json.Unmarshal(res, &hvs) + if err != nil { + return nil, err + } + + return hvs, nil +} + +// ListRaw gets a list of all hypervisors as an array of bytes +func (hv Hypervisors) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/hypervisor/list" + + res, err := hv.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/hypervisors/models.go b/pkg/sdn/hypervisors/models.go new file mode 100644 index 0000000..cd4ec2c --- /dev/null +++ b/pkg/sdn/hypervisors/models.go @@ -0,0 +1,58 @@ +package hypervisors + +// Main information about hypervisor +type RecordHypervisor struct { + // Created at + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Hostname + Hostname string `json:"hostname"` + + // IP + IP string `json:"ip"` + + // Synced at + SyncedAt string `json:"synced_at"` + + // Name + Name string `json:"name"` + + // Ports + Ports Ports `json:"ports"` + + // Status + Status string `json:"status"` +} + +// List of hypervisors +type HypervisorsList []RecordHypervisor + +// Hypervisor ports +type Ports struct { + Data []Port `json:"data"` + Info Info `json:"info"` +} + +// Info about a port +type Port struct { + // ID of a port + ID string `json:"id"` + + // Unique ID of a port + UniqueIdentifier string `json:"unique_identifier"` + + // Display name of a port + DisplayName string `json:"display_name"` + + // Is a port up + UP bool `json:"up"` +} + +// Port counters +type Info struct { + ActivePorts uint64 `json:"active_ports"` + TotalPorts uint64 `json:"total_ports"` +} diff --git a/pkg/sdn/hypervisors/update_display_name.go b/pkg/sdn/hypervisors/update_display_name.go new file mode 100644 index 0000000..288dc4e --- /dev/null +++ b/pkg/sdn/hypervisors/update_display_name.go @@ -0,0 +1,50 @@ +package hypervisors + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateDisplayNameRequest struct to update display name for a hypervisor +type UpdateDisplayNameRequest struct { + // Current name of the hypervisor + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // New display name to set + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` +} + +// UpdateDisplayName updates display name for a hypervisor +func (hv Hypervisors) UpdateDisplayName(ctx context.Context, req UpdateDisplayNameRequest) (*RecordHypervisor, error) { + res, err := hv.UpdateDisplayNameRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordHypervisor{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// UpdateDisplayNameRaw update display name for a hypervisor and get its information as an array of bytes +func (hv Hypervisors) UpdateDisplayNameRaw(ctx context.Context, req UpdateDisplayNameRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/hypervisor/update_display_name" + + res, err := hv.client.DecortApiCall(ctx, http.MethodPut, url, req) + return res, err +} diff --git a/pkg/sdn/logical_ports.go b/pkg/sdn/logical_ports.go new file mode 100644 index 0000000..a6e6fa6 --- /dev/null +++ b/pkg/sdn/logical_ports.go @@ -0,0 +1,10 @@ +package sdn + +import ( + lp "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/logicalports" +) + +// Accessing the SDN method group +func (sdn *SDN) LogicalPorts() *lp.LogicalPorts { + return lp.New(sdn.client) +} diff --git a/pkg/sdn/logicalports/create.go b/pkg/sdn/logicalports/create.go new file mode 100644 index 0000000..d136c5e --- /dev/null +++ b/pkg/sdn/logicalports/create.go @@ -0,0 +1,110 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// Label struct +type CreateLabels struct { + VMID string `url:"vm_id,omitempty" json:"vm_id,omitempty"` + VMName string `url:"vm_name,omitempty" json:"vm_name,omitempty"` +} + +// CreateRequest struct to create logical port +type CreateRequest struct { + // ID of the access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Description + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Display name + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Enabled. True or False + // Required: true + Enabled interface{} `url:"enabled" json:"enabled" validate:"required,isBool"` + + // Hypervisor + // Required: true + Hypervisor string `url:"hypervisor" json:"hypervisor" validate:"required"` + + // Port security. True or False + // Required: true + PortSecurity interface{} `url:"port_security" json:"port_security" validate:"required,isBool"` + + // Segment ID + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` + + // Adapter MAC + // Required: false + AdapterMAC string `url:"adapter_mac,omitempty" json:"adapter_mac,omitempty"` + + // Unique identifier + // Required: false + UniqueIdentifier string `url:"unique_identifier,omitempty" json:"unique_identifier,omitempty"` + + // Logical port addresses + // Required: false + LogicalPortAddresses []LogicalPortAddress `url:"logical_port_addresses,omitempty" json:"logical_port_addresses,omitempty" validate:"dive"` + + // Labels + // Required: false + Labels CreateLabels `url:"labels,omitempty" json:"labels,omitempty"` +} + +// LogicalPortAddressRequest struct representing logical port address +type LogicalPortAddressRequest struct { + // IP address + // Required: true + IP string `url:"ip" json:"ip" validate:"required"` + + // IP type + // Required: true + IPType string `url:"ip_type" json:"ip_type" validate:"required,oneof=IPv4 IPv6"` + + // Is primary. True or False + // Required: true + IsPrimary interface{} `url:"is_primary" json:"is_primary" validate:"required,isBool"` + + // MAC address + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` + + // Is discovered. True or False + // Required: true + IsDiscovered bool `url:"is_discovered" json:"is_discovered" validate:"required"` +} + +// Create creates a logical port +func (l LogicalPorts) Create(ctx context.Context, req CreateRequest) (*LogicalPort, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/create" + + res, err := l.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := LogicalPort{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/logicalports/create_batch.go b/pkg/sdn/logicalports/create_batch.go new file mode 100644 index 0000000..cd53219 --- /dev/null +++ b/pkg/sdn/logicalports/create_batch.go @@ -0,0 +1,134 @@ +package logicalports + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type CreateBatchLogicalPortAddress struct { + // IP + // Required: true + IP string `url:"ip" json:"ip" validate:"required"` + + // IP Type + // Required: true + IPType string `url:"ip_type" json:"ip_type" validate:"required"` + + // Is Discovered + // Required: true + IsDiscovered bool `url:"is_discovered" json:"is_discovered" validate:"required"` + + // Is Primary + // Required: true + IsPrimary bool `url:"is_primary" json:"is_primary" validate:"required"` + + // MAC + // Required: false + MAC string `url:"mac,omitempty" json:"mac,omitempty"` +} + +type CreateBatchBindings struct { + // Address Detection + // Required: true + AddressDetection bool `url:"address_detection" json:"address_detection" validate:"required"` + + // Logical Port Address + // Required: true + LogicalPortAddresses []CreateBatchLogicalPortAddress `url:"logical_port_addresses" json:"logical_port_addresses" validate:"required,dive"` + + // Port Security + // Required: true + PortSecurity bool `url:"port_security" json:"port_security" validate:"required"` + + // Segment ID + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` +} + +type CreateBatchLabels struct { + // VM ID + // Required: false + VMID string `url:"vm_id,omitempty" json:"vm_id,omitempty"` + + // VM Name + // Required: false + VMName string `url:"vm_name,omitempty" json:"vm_name,omitempty"` +} + +type NetObjectGroups struct { + // ID + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +type Ports struct { + // Adapter MAC + // Required: false + AdapterMAC string `url:"adapter_mac,omitempty" json:"adapter_mac,omitempty"` + + // Bindings + // Required: true + Bindings CreateBatchBindings `url:"bindings" json:"bindings" validate:"required"` + + // Description + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Display Name + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled" validate:"required"` + + // Hypervisor + // Required: true + Hypervisor string `url:"hypervisor" json:"hypervisor" validate:"required"` + + // Net Object Groups + // Required: false + NetObjectGroups []NetObjectGroups `json:"net_object_groups" validate:"required,dive"` + + // Labels + // Required: false + Labels []CreateBatchLabels `url:"labels,omitempty" json:"labels,omitempty" validate:"required,dive"` + + // Unique Identifier + // Required: false + UniqueIdentifier string `url:"unique_identifier,omitempty" json:"unique_identifier,omitempty"` +} + +// CreateBatchRequest struct to create a batch of logical ports +type CreateBatchRequest struct { + // Access Group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Ports + // Required: true + Ports []Ports `json:"ports" validate:"required,dive"` +} + +// CreateBatch creates a batch of logical ports +func (lp LogicalPorts) CreateBatch(ctx context.Context, req CreateBatchRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + url := "/sdn/logical_port/create_batch" + + _, err = lp.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/sdn/logicalports/delete.go b/pkg/sdn/logicalports/delete.go new file mode 100644 index 0000000..bdbadcc --- /dev/null +++ b/pkg/sdn/logicalports/delete.go @@ -0,0 +1,41 @@ +package logicalports + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct to delete logical port +type DeleteRequest struct { + // Port ID + // Required: true + ID string `url:"logical_port_id" json:"logical_port_id" validate:"required"` + + // Version + // Required: true + Version uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete. True or false + // Required: false + Force interface{} `url:"force,omitempty" json:"force,omitempty" validate:"omitempty,isBool"` +} + +// Delete a logical port +func (i LogicalPorts) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/delete" + + _, err = i.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/sdn/logicalports/delete_batch.go b/pkg/sdn/logicalports/delete_batch.go new file mode 100644 index 0000000..6ec0695 --- /dev/null +++ b/pkg/sdn/logicalports/delete_batch.go @@ -0,0 +1,50 @@ +package logicalports + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type PortsInfo struct { + // Force + // Required: true + Force bool `url:"force" json:"force" validate:"required"` + + // ID + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +// DeleteBatchRequest struct to delete a batch of logical ports +type DeleteBatchRequest struct { + // Access Group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Ports Info + // Required: true + PortsInfo []PortsInfo `json:"ports_info" validate:"required,dive"` +} + +// DeleteBatch deletes a batch of logical ports +func (lp LogicalPorts) DeleteBatch(ctx context.Context, req DeleteBatchRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + url := "/sdn/logical_port/delete_batch" + + _, err = lp.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/sdn/logicalports/exclude_firewall.go b/pkg/sdn/logicalports/exclude_firewall.go new file mode 100644 index 0000000..af6e27c --- /dev/null +++ b/pkg/sdn/logicalports/exclude_firewall.go @@ -0,0 +1,63 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type LogicalPortsForExcludeFromFirewall struct { + // Exclude IP Addresses + // Required: true + ExcludeIPAddresses bool `url:"exclude_ip_addresses" json:"exclude_ip_addresses" validate:"required"` + + // ID + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +// ExcludeFirewallRequest struct to exclude firewall for logical port +type ExcludeFirewallRequest struct { + // Access Group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Logical Ports For Exclude From Firewall + // Required: true + LogicalPortsForExcludeFromFirewall []LogicalPortsForExcludeFromFirewall `json:"logical_ports_for_exclude_from_firewall" validate:"required,dive"` + + // Exclusion Reason + // Required: false + ExclusionReason string `url:"exclusion_reason,omitempty" json:"exclusion_reason,omitempty"` +} + +// ExcludeFirewall excludes firewall from a logical port +func (lp LogicalPorts) ExcludeFirewall(ctx context.Context, req ExcludeFirewallRequest) (*RecordVersion, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/exclude_firewall" + + res, err := lp.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/logicalports/filter.go b/pkg/sdn/logicalports/filter.go new file mode 100644 index 0000000..62b57ac --- /dev/null +++ b/pkg/sdn/logicalports/filter.go @@ -0,0 +1,42 @@ +package logicalports + +// FilterByID returns LogicalPortsList with specified ID. +func (agl LogicalPortsList) FilterByID(id string) LogicalPortsList { + predicate := func(ia LogicalPort) bool { + return ia.ID == id + } + + return agl.FilterFunc(predicate) +} + +// FilterByName returns LogicalPortsList with specified Name. +func (agl LogicalPortsList) FilterByName(name string) LogicalPortsList { + predicate := func(ia LogicalPort) bool { + return ia.DisplayName == name + } + + return agl.FilterFunc(predicate) +} + +// FilterFunc allows filtering LogicalPortsList based on a user-specified predicate. +func (agl LogicalPortsList) FilterFunc(predicate func(LogicalPort) bool) LogicalPortsList { + var result LogicalPortsList + + for _, acc := range agl.Ports { + if predicate(acc) { + result.Ports = append(result.Ports, acc) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (agl LogicalPortsList) FindOne() LogicalPort { + if len(agl.Ports) == 0 { + return LogicalPort{} + } + + return agl.Ports[0] +} diff --git a/pkg/sdn/logicalports/get.go b/pkg/sdn/logicalports/get.go new file mode 100644 index 0000000..286a46d --- /dev/null +++ b/pkg/sdn/logicalports/get.go @@ -0,0 +1,47 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about logical port +type GetRequest struct { + // ID a logical port + // Required: true + ID string `url:"logical_port_id" json:"logical_port_id" validate:"required"` +} + +// Get gets logical port details as a LogicalPort struct +func (a LogicalPorts) Get(ctx context.Context, req GetRequest) (*LogicalPort, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := LogicalPort{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets logical port details as an array of bytes +func (a LogicalPorts) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/logicalports/get_by_unique_identifier.go b/pkg/sdn/logicalports/get_by_unique_identifier.go new file mode 100644 index 0000000..f7f132c --- /dev/null +++ b/pkg/sdn/logicalports/get_by_unique_identifier.go @@ -0,0 +1,47 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetByUniqueIdentifierRequest struct to get information about logical port +type GetByUniqueIdentifierRequest struct { + // ID a logical port + // Required: true + ID string `url:"unique_identifier" json:"unique_identifier" validate:"required"` +} + +// GetByUniqueIdentifier gets logical port details as a LogicalPort struct +func (a LogicalPorts) GetByUniqueIdentifier(ctx context.Context, req GetByUniqueIdentifierRequest) (*LogicalPort, error) { + res, err := a.GetByUniqueIdentifierRaw(ctx, req) + if err != nil { + return nil, err + } + + info := LogicalPort{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetByUniqueIdentifier gets logical port details as an array of bytes +func (a LogicalPorts) GetByUniqueIdentifierRaw(ctx context.Context, req GetByUniqueIdentifierRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/get_by_unique_identifier" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/logicalports/ids.go b/pkg/sdn/logicalports/ids.go new file mode 100644 index 0000000..91e3175 --- /dev/null +++ b/pkg/sdn/logicalports/ids.go @@ -0,0 +1,10 @@ +package logicalports + +// IDs gets array of IDs from LogicalPortList struct +func (pl LogicalPortsList) IDs() []string { + res := make([]string, 0, len(pl.Ports)) + for _, c := range pl.Ports { + res = append(res, c.ID) + } + return res +} diff --git a/pkg/sdn/logicalports/list.go b/pkg/sdn/logicalports/list.go new file mode 100644 index 0000000..6ea910e --- /dev/null +++ b/pkg/sdn/logicalports/list.go @@ -0,0 +1,128 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of logical ports +type ListRequest struct { + // Find by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Find by segment ID + // Required: false + SegmentID string `url:"segment_id,omitempty" json:"segment_id,omitempty"` + + // Find by segment display name + // Required: false + SegmentDisplayName string `url:"segment_display_name,omitempty" json:"segment_display_name,omitempty"` + + // Find by external network ID + // Required: false + ExternalNetworkID string `url:"external_network_id,omitempty" json:"external_network_id,omitempty"` + + // Find by unique identifier + // Required: false + UniqueIdentifier string `url:"unique_identifier,omitempty" json:"unique_identifier,omitempty"` + + // Find by display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Find by adapter MAC address + // Required: false + AdapterMAC string `url:"adapter_mac,omitempty" json:"adapter_mac,omitempty"` + + // Find by hypervisor + // Required: false + Hypervisor string `url:"hypervisor,omitempty" json:"hypervisor,omitempty"` + + // Find by hypervisor display name + // Required: false + HypervisorDisplayName string `url:"hypervisor_display_name,omitempty" json:"hypervisor_display_name,omitempty"` + + // Find by live migration target hypervisor + // Required: false + LiveMigrationTargetHv string `url:"live_migration_target_hv,omitempty" json:"live_migration_target_hv,omitempty"` + + // Find by port security status, true or false + // Required: false + PortSecurity interface{} `url:"port_security,omitempty" json:"port_security,omitempty" validate:"omitempty,isBool"` + + // Find by address detection status, true or false + // Required: false + AddressDetection interface{} `url:"address_detection,omitempty" json:"address_detection,omitempty" validate:"omitempty,isBool"` + + // Find by enabled status, true or false + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Creation date lower bound (inclusive) + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Creation date upper bound (inclusive) + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` + + // Filter by operation status + // Required: false + OperationStatus string `url:"operation_status,omitempty" json:"operation_status,omitempty"` + + // Filter by hypervisor status + // Required: false + HypervisorStatus string `url:"hypervisor_status,omitempty" json:"hypervisor_status,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, created_at, updated_at, deleted_at, segment_id, hypervisor, port_security, segment_display_name, primary_address, hypervisor_display_name) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` +} + +// List of logical ports +func (i LogicalPorts) List(ctx context.Context, req ListRequest) (*LogicalPortsList, error) { + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + groups := []LogicalPort{} + + err = json.Unmarshal(res, &groups) + if err != nil { + return nil, err + } + + result := LogicalPortsList{Ports: groups} + + return &result, nil +} + +// ListRaw gets a list of all logical portts as an array of bytes +func (a LogicalPorts) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/logicalports/logicalports.go b/pkg/sdn/logicalports/logicalports.go new file mode 100644 index 0000000..875a176 --- /dev/null +++ b/pkg/sdn/logicalports/logicalports.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN logical ports +package logicalports + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to logical ports +type LogicalPorts struct { + client interfaces.Caller +} + +// Builder for logical ports endpoints +func New(client interfaces.Caller) *LogicalPorts { + return &LogicalPorts{ + client, + } +} diff --git a/pkg/sdn/logicalports/migrate_cancel.go b/pkg/sdn/logicalports/migrate_cancel.go new file mode 100644 index 0000000..455b787 --- /dev/null +++ b/pkg/sdn/logicalports/migrate_cancel.go @@ -0,0 +1,44 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateCancelRequest struct to cancel migrate +type MigrateCancelRequest struct { + // ID of the logical port + // Required: true + LogicalPortID string `url:"logical_port_id" json:"logical_port_id" validate:"required"` + + // ID of the version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +func (i LogicalPorts) CancelMigrate(ctx context.Context, req MigrateCancelRequest) (*MigrationStatus, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/migration_cancel" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := MigrationStatus{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/logicalports/migrate_start.go b/pkg/sdn/logicalports/migrate_start.go new file mode 100644 index 0000000..b437d38 --- /dev/null +++ b/pkg/sdn/logicalports/migrate_start.go @@ -0,0 +1,48 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// MigrateStartRequest struct to start migrate +type MigrateStartRequest struct { + // ID of the logical port + // Required: true + LogicalPortID string `url:"logical_port_id" json:"logical_port_id" validate:"required"` + + // ID of the version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Hypervisor + // Required: true + TargetHypervisor string `url:"target_hypervisor" json:"target_hypervisor" validate:"required"` +} + +func (i LogicalPorts) StartMigrate(ctx context.Context, req MigrateStartRequest) (*MigrationStatus, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/migration_start" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := MigrationStatus{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/logicalports/models.go b/pkg/sdn/logicalports/models.go new file mode 100644 index 0000000..98905cf --- /dev/null +++ b/pkg/sdn/logicalports/models.go @@ -0,0 +1,178 @@ +package logicalports + +// LogicalPortsList represents a list of logical ports +type LogicalPortsList struct { + Ports []LogicalPort `json:"ports"` +} + +// Main information about logical port +type LogicalPort struct { + // ID + ID string `json:"id"` + + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Adapter MAC + AdapterMAC string `json:"adapter_mac"` + + // Address detection + AddressDetection bool `json:"address_detection"` + + // Description + Description string `json:"description"` + + // Created time + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled + Enabled bool `json:"enabled"` + + // External network ID + ExternalNetworkID string `json:"external_network_id"` + + // Hypervisor + Hypervisor string `json:"hypervisor"` + + // Hypervisor display name + HypervisorDisplayName string `json:"hypervisor_display_name"` + + // Labels + Labels Labels `json:"labels"` + + // Live migration target hypervisor + LiveMigrationTargetHV string `json:"live_migration_target_hv"` + + // Status information + Status Status `json:"status"` + + // Bindings information + Bindings Bindings `json:"bindings"` + + // Unique identifier + UniqueIdentifier string `json:"unique_identifier"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Status information +type Status struct { + + // Operation status + OperationStatus string `json:"operation_status"` + + // Hypervisors status + Hypervisors []HypervisorStatus `json:"hypervisors"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` +} + +// HypervisorStatus information +type HypervisorStatus struct { + // Operation status + OperationStatus string `json:"operation_status"` + + // Name + Name string `json:"name"` + + // Display name + DisplayName string `json:"display_name"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` + + // Synced time + SyncedAt string `json:"synced_at"` +} + +// Bindings information +type Bindings struct { + // ID + ID string `json:"id"` + + // Segment display name + SegmentDisplayName string `json:"segment_display_name"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Port security + PortSecurity bool `json:"port_security"` + + // Address detection + AddressDetection bool `json:"address_detection"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // Created time + CreatedAt string `json:"created_at"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Logical port addresses + LogicalPortAddresses []LogicalPortAddress `json:"logical_port_addresses"` +} + +// LogicalPortAddress information +type LogicalPortAddress struct { + // IP address + IP string `json:"ip"` + + // IP type + IPType string `json:"ip_type"` + + // Is discovered + IsDiscovered bool `json:"is_discovered"` + + // Is primary + IsPrimary bool `json:"is_primary"` + + // MAC address + MAC string `json:"mac"` + + // ID + ID string `json:"id"` + + // Logical port ID + LogicalPortID string `json:"logical_port_id"` + + // Assigned time + AssignedAt string `json:"assigned_at"` +} + +// Migration status information +type MigrationStatus struct { + // ID + ID string `json:"id"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Labels information +type Labels struct { + // VM ID + VMID string `json:"vm_id"` + + // VM name + VMName string `json:"vm_name"` +} + +// Information about a version +type RecordVersion struct { + // Version ID + VersionID uint64 `json:"version_id"` +} diff --git a/pkg/sdn/logicalports/serialize.go b/pkg/sdn/logicalports/serialize.go new file mode 100644 index 0000000..c7a740c --- /dev/null +++ b/pkg/sdn/logicalports/serialize.go @@ -0,0 +1,43 @@ +package logicalports + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 LogicalPortsList) Serialize(params ...string) (serialization.Serialized, error) { + if len(la.Ports) == 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 LogicalPort) 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/sdn/logicalports/unexclude_firewall.go b/pkg/sdn/logicalports/unexclude_firewall.go new file mode 100644 index 0000000..ce59fae --- /dev/null +++ b/pkg/sdn/logicalports/unexclude_firewall.go @@ -0,0 +1,55 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +type LogicalPortsForUnexcludeFromFirewall struct { + // Exclude IP Addresses + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` +} + +// UnexcludeFirewallRequest struct to unexclude firewall for logical port +type UnexcludeFirewallRequest struct { + // Access Group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Logical Ports For Unexclude From Firewall + // Required: true + LogicalPortsForUnexcludeFromFirewall []LogicalPortsForUnexcludeFromFirewall `json:"logical_ports_for_unexclude_from_firewall" validate:"required,dive"` +} + +// UnexcludeFirewallRequest struct to unexclude firewall for logical port +func (lp LogicalPorts) UnexcludeFirewall(ctx context.Context, req UnexcludeFirewallRequest) (*RecordVersion, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/unexclude_firewall" + + res, err := lp.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/logicalports/update.go b/pkg/sdn/logicalports/update.go new file mode 100644 index 0000000..0e7356f --- /dev/null +++ b/pkg/sdn/logicalports/update.go @@ -0,0 +1,145 @@ +package logicalports + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// Label struct +type UpdateLabels struct { + VMID string `url:"vm_id,omitempty" json:"vm_id,omitempty"` + VMName string `url:"vm_name,omitempty" json:"vm_name,omitempty"` +} + +// UpdateRequest struct to update logical port +type UpdateRequest struct { + // ID of the logical port + // Required: true + LogicalPortID string `url:"logical_port_id" json:"logical_port_id" validate:"required"` + + // ID of the version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Adapter MAC + // Required: true + AdapterMAC string `url:"adapter_mac" json:"adapter_mac" validate:"required"` + + // Description + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Display name + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Enabled. True or False + // Required: true + Enabled interface{} `url:"enabled" json:"enabled" validate:"required,isBool"` + + // Remove addresses + // Required: false + RemoveAddresses []string `url:"remove_addresses,omitempty" json:"remove_addresses,omitempty"` + + // Hypervisor + // Required: true + Hypervisor string `url:"hypervisor" json:"hypervisor" validate:"required"` + + // Port security. True or False + // Required: true + PortSecurity interface{} `url:"port_security" json:"port_security" validate:"required,isBool"` + + // Segment ID + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` + + // Update addresses + // Required: false + UpdateAddresses []UpdateAddress `url:"update_addresses,omitempty" json:"update_addresses,omitempty" validate:"dive"` + + // Add addresses + // Required: false + AddAddresses []AddAddress `url:"add_addresses,omitempty" json:"add_addresses,omitempty" validate:"dive"` + + // Labels + // Required: false + Labels UpdateLabels `url:"labels,omitempty" json:"labels,omitempty"` +} + +// UpdateAddress struct representing update address +type UpdateAddress struct { + // ID of the address + // Required: true + ID string `url:"id" json:"id" validate:"required"` + + // IP address + // Required: true + IP string `url:"ip" json:"ip" validate:"required"` + + // IP type + // Required: true + IPType string `url:"ip_type" json:"ip_type" validate:"required,oneof=IPv4 IPv6"` + + // Is discovered. True or False + // Required: true + IsDiscovered bool `url:"is_discovered" json:"is_discovered" validate:"required"` + + // Is primary. True or False + // Required: true + IsPrimary interface{} `url:"is_primary" json:"is_primary" validate:"required,isBool"` + + // MAC address + // Required: true + MAC string `url:"mac" json:"mac" validate:"required"` +} + +// AddAddress struct representing add address +type AddAddress struct { + // IP address + // Required: true + IP string `url:"ip" json:"ip" validate:"required"` + + // IP type + // Required: true + IPType string `url:"ip_type" json:"ip_type" validate:"required,oneof=IPv4 IPv6"` + + // Is discovered. True or False + // Required: true + IsDiscovered bool `url:"is_discovered" json:"is_discovered" validate:"required"` + + // Is primary. True or False + // Required: true + IsPrimary interface{} `url:"is_primary" json:"is_primary" validate:"required,isBool"` + + // MAC address + // Required: true + MAC string `url:"mac" json:"mac" validate:"required"` +} + +// Update updates a logical port +func (i LogicalPorts) Update(ctx context.Context, req UpdateRequest) (*LogicalPort, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/logical_port/update" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := LogicalPort{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/netobjgroups/attach_extnet_ports.go b/pkg/sdn/netobjgroups/attach_extnet_ports.go new file mode 100644 index 0000000..fc92263 --- /dev/null +++ b/pkg/sdn/netobjgroups/attach_extnet_ports.go @@ -0,0 +1,49 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AttachExtNetPortsRequest struct to attach external network ports to a network object group +type AttachExtNetPortsRequest struct { + // ID of a network object group + // Required: true + ObjectGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` + + // ID of an access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // IDs of external network ports to attach to a network object group + // Required: true + PortIDs []string `url:"port_ids" json:"port_ids" validate:"required"` +} + +// AttachExtNetPorts attaches external network ports to a network object group +func (nog NetworkObjectGroups) AttachExtNetPorts(ctx context.Context, req AttachExtNetPortsRequest) (*RecordVersion, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/attach_external_network_ports" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/netobjgroups/attach_l2_connection_ports.go b/pkg/sdn/netobjgroups/attach_l2_connection_ports.go new file mode 100644 index 0000000..1b8b77e --- /dev/null +++ b/pkg/sdn/netobjgroups/attach_l2_connection_ports.go @@ -0,0 +1,62 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AttachL2ConnectionPortsRequest struct to attach l2 ports to a network object group +type AttachL2ConnectionPortsRequest struct { + // ID of a network object group + // Required: true + ObjectGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` + + // ID of an access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Port Bindings + // Required: true + PortBindings []L2PortBindings `url:"port_bindings" json:"port_bindings" validate:"required,dive"` +} +type L2PortBindings struct { + // ID of a logical port + // Required: true + PortID string `url:"port_id" json:"port_id" validate:"required"` + + // Version of a logical port + // Required: true + PortVersion int64 `url:"port_version" json:"port_version" validate:"required"` +} + +// AttachLogicalPorts attaches l2 ports to a network object group +func (nog NetworkObjectGroups) AttachL2ConnectionPorts(ctx context.Context, req AttachL2ConnectionPortsRequest) (*RecordVersion, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/attach_l2_connection_ports" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/netobjgroups/attach_logical_ports.go b/pkg/sdn/netobjgroups/attach_logical_ports.go new file mode 100644 index 0000000..39f8aee --- /dev/null +++ b/pkg/sdn/netobjgroups/attach_logical_ports.go @@ -0,0 +1,63 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// AttachLogicalPortsRequest struct to attach logical ports to a network object group +type AttachLogicalPortsRequest struct { + // ID of a network object group + // Required: true + ObjectGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` + + // ID of an access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Port Bindings + // Required: true + PortBindings []PortBindings `url:"port_bindings" json:"port_bindings" validate:"required,dive"` +} + +type PortBindings struct { + // ID of a logical port + // Required: true + PortID string `url:"port_id" json:"port_id" validate:"required"` + + // Version of a logical port + // Required: true + PortVersion int64 `url:"port_version" json:"port_version" validate:"required"` +} + +// AttachLogicalPorts attaches logical ports to a network object group +func (nog NetworkObjectGroups) AttachLogicalPorts(ctx context.Context, req AttachLogicalPortsRequest) (*RecordVersion, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/attach_logical_ports" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/netobjgroups/create.go b/pkg/sdn/netobjgroups/create.go new file mode 100644 index 0000000..adcea7c --- /dev/null +++ b/pkg/sdn/netobjgroups/create.go @@ -0,0 +1,70 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create a network object group +type CreateRequest struct { + // Access Group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Description + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Name + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Logical ports bindings + // Required: false + LogicalPortsBindings []LogicalPortsBindings `url:"logical_ports_binding,omitempty" json:"logical_ports_bindings,omitempty" validate:"omitempty,dive"` + + // L2 connection ports bindings + // Required: false + L2ConnectionPortsBindings []LogicalPortsBindings `url:"l2_connection_ports_binding,omitempty" json:"l2_connection_ports_bindings,omitempty" validate:"omitempty,dive"` + + // Addresses + // Required: false + Addresses []NetAddressRequest `url:"addresses,omitempty" json:"addresses,omitempty" validate:"omitempty,dive"` +} +type LogicalPortsBindings struct { + // Port ID + // Required: true + PortID string `url:"port_id" json:"port_id" validate:"required"` + + // Port version + // Required: true + PortVersion uint64 `url:"port_version" json:"port_version" validate:"required"` +} + +// Create creates a network object group +func (nog NetworkObjectGroups) Create(ctx context.Context, req CreateRequest) (*RecordNetObjGroup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/create" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordNetObjGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/netobjgroups/delete.go b/pkg/sdn/netobjgroups/delete.go new file mode 100644 index 0000000..7c34c32 --- /dev/null +++ b/pkg/sdn/netobjgroups/delete.go @@ -0,0 +1,51 @@ +package netobjgroups + +import ( + "context" + "net/http" + "strconv" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest to delete a network object group +type DeleteRequest struct { + // ID of a network object group + // Required: true + ObjectGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Delete a network object group +func (nog NetworkObjectGroups) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/delete" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + if string(res) == "" { + return true, nil + } + + result, err := strconv.ParseBool(string(res)) + if err != nil { + return false, err + } + + return result, nil +} diff --git a/pkg/sdn/netobjgroups/detach_external_network_ports.go b/pkg/sdn/netobjgroups/detach_external_network_ports.go new file mode 100644 index 0000000..df74773 --- /dev/null +++ b/pkg/sdn/netobjgroups/detach_external_network_ports.go @@ -0,0 +1,49 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DetachExtNetPortsRequest struct to detach an external network port from a network object group +type DetachExtNetPortsRequest struct { + // ID of a network object group + // Required: true + ObjectGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` + + // ID of an access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // ID of an external network port to detach from a network object group + // Required: true + ExternalNetworkPortID string `url:"external_network_port_id" json:"external_network_port_id" validate:"required"` +} + +// DetachExtNetlPorts detaches external network ports from a network object group +func (nog NetworkObjectGroups) DetachExtNetPorts(ctx context.Context, req DetachExtNetPortsRequest) (*RecordVersion, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/detach_external_network_ports" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/netobjgroups/detach_l2_connection_ports.go b/pkg/sdn/netobjgroups/detach_l2_connection_ports.go new file mode 100644 index 0000000..32a4614 --- /dev/null +++ b/pkg/sdn/netobjgroups/detach_l2_connection_ports.go @@ -0,0 +1,62 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DetachL2ConnectionPortsRequest struct to detach an l2 port from a network object group +type DetachL2ConnectionPortsRequest struct { + // ID of a network object group + // Required: true + ObjectGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` + + // ID of an access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Port Bindings + // Required: true + PortBindings []DetachL2PortBindings `url:"port_bindings" json:"port_bindings" validate:"required,dive"` +} +type DetachL2PortBindings struct { + // ID of a logical port + // Required: true + PortID string `url:"port_id" json:"port_id" validate:"required"` + + // Version of a logical port + // Required: true + PortVersion int64 `url:"port_version" json:"port_version" validate:"required"` +} + +// DetachL2ConnectionPorts detaches logical ports from a network object group +func (nog NetworkObjectGroups) DetachL2ConnectionPorts(ctx context.Context, req DetachL2ConnectionPortsRequest) (*RecordVersion, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/detach_l2_connection_ports" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/netobjgroups/detach_logical_ports.go b/pkg/sdn/netobjgroups/detach_logical_ports.go new file mode 100644 index 0000000..0dbd483 --- /dev/null +++ b/pkg/sdn/netobjgroups/detach_logical_ports.go @@ -0,0 +1,63 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DetachLogicalPortsRequest struct to detach logical ports from a network object group +type DetachLogicalPortsRequest struct { + // ID of a network object group + // Required: true + ObjectGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` + + // ID of an access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Port bindings + // Required: true + PortBindings []DetachLogicalPortBinding `url:"port_bindings" json:"port_bindings" validate:"required,dive"` +} + +type DetachLogicalPortBinding struct { + // ID of a logical port + // Required: true + PortID string `url:"port_id" json:"port_id" validate:"required"` + + // Version of a logical port + // Required: true + PortVersion uint64 `url:"port_version" json:"port_version" validate:"required"` +} + +// DetachLogicalPorts detaches logical ports from a network object group +func (nog NetworkObjectGroups) DetachLogicalPorts(ctx context.Context, req DetachLogicalPortsRequest) (*RecordVersion, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/detach_logical_ports" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/netobjgroups/filter.go b/pkg/sdn/netobjgroups/filter.go new file mode 100644 index 0000000..706fa5f --- /dev/null +++ b/pkg/sdn/netobjgroups/filter.go @@ -0,0 +1,42 @@ +package netobjgroups + +// FilterByID returns NetObjGroupList with specified ID. +func (nog NetObjGroupList) FilterByID(id string) NetObjGroupList { + predicate := func(netobj RecordNetObjGroup) bool { + return netobj.ID == id + } + + return nog.FilterFunc(predicate) +} + +// FilterByName returns NetObjGroupList with specified Name. +func (nog NetObjGroupList) FilterByName(name string) NetObjGroupList { + predicate := func(netobj RecordNetObjGroup) bool { + return netobj.Name == name + } + + return nog.FilterFunc(predicate) +} + +// FilterFunc allows filtering NetObjGroupList based on a user-specified predicate. +func (nog NetObjGroupList) FilterFunc(predicate func(group RecordNetObjGroup) bool) NetObjGroupList { + var result NetObjGroupList + + for _, acc := range nog.Objects { + if predicate(acc) { + result.Objects = append(result.Objects, acc) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (nog NetObjGroupList) FindOne() RecordNetObjGroup { + if len(nog.Objects) == 0 { + return RecordNetObjGroup{} + } + + return nog.Objects[0] +} diff --git a/pkg/sdn/netobjgroups/filter_test.go b/pkg/sdn/netobjgroups/filter_test.go new file mode 100644 index 0000000..c12166c --- /dev/null +++ b/pkg/sdn/netobjgroups/filter_test.go @@ -0,0 +1,111 @@ +package netobjgroups + +import "testing" + +var testNetObjGroups = NetObjGroupList{ + Objects: []RecordNetObjGroup{ + { + AccessGroupID: "d85d4a08-3216-4240-a8bd-191cd9cc3bf3", + AccessGroupName: "Test1", + CreatedAt: "2025-09-04T13:18:14.412118Z", + Description: "test descr", + ID: "0b16493b-0a38-4c90-80a0-c19f898f593d", + Name: "Test1", + UpdatedAt: "2025-10-28T07:28:15.450717Z", + VersionID: 1761636495441, + Counters: Counter{ + LogicalPortsCount: 1, + SecurityPoliciesCount: 2, + SecurityRulesCount: 0, + }, + }, + { + AccessGroupID: "d85d4a08-3216-4240-a8bd-191cd9cc3bf3", + AccessGroupName: "Test2", + CreatedAt: "2025-09-04T13:18:14.412118Z", + Description: "another descr", + ID: "0b16493b-0a38-4c90-80a0-c19f898f593e", + Name: "Test2", + UpdatedAt: "2025-10-28T07:28:15.450717Z", + VersionID: 1761636495442, + Counters: Counter{ + LogicalPortsCount: 1, + SecurityPoliciesCount: 2, + SecurityRulesCount: 0, + }, + }, + { + AccessGroupID: "d85d4a08-3216-4240-a8bd-191cd9cc3bf3", + AccessGroupName: "Test3", + CreatedAt: "2025-09-04T13:18:14.412118Z", + Description: "another descr", + ID: "0b16493b-0a38-4c90-80a0-c19f898f593f", + Name: "Test3", + UpdatedAt: "2025-10-28T07:28:15.450717Z", + VersionID: 1761636495443, + Counters: Counter{ + LogicalPortsCount: 1, + SecurityPoliciesCount: 2, + SecurityRulesCount: 0, + }, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testNetObjGroups.FilterByID("0b16493b-0a38-4c90-80a0-c19f898f593d").FindOne() + + if actual.ID != "0b16493b-0a38-4c90-80a0-c19f898f593d" { + t.Fatal("actual:", actual.ID, "> expected: 0b16493b-0a38-4c90-80a0-c19f898f593d") + } +} + +func TestFilterByName(t *testing.T) { + actual := testNetObjGroups.FilterByName("Test1").FindOne() + + if actual.Name != "Test1" { + t.Fatal("actual:", actual.Name, ">> expected: Test1") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testNetObjGroups.FilterFunc(func(rnog RecordNetObjGroup) bool { + return rnog.Description == "test descr" + }) + + if len(actual.Objects) != 1 || actual.Objects[0].ID != "0b16493b-0a38-4c90-80a0-c19f898f593d" { + t.Fatal("Expected 1 policy with description 'Second policy', found:", len(actual.Objects)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testNetObjGroups.FilterByID("0b16493b-0a38-4c90-80a0-c19f898f593d").FindOne() + if result.ID != "0b16493b-0a38-4c90-80a0-c19f898f593d" { + t.Fatal("Expected 0b16493b-0a38-4c90-80a0-c19f898f593d, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := NetObjGroupList{} + result := emptyList.FindOne() + + if result.ID != "" || result.Name != "" { + t.Fatal("Expected empty NetObjGroup, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := testNetObjGroups.FilterByID("0b16493b-0a38-4c90-80a0-c19f898f593n") + + if len(actual.Objects) != 0 { + t.Fatal("Expected 0 policies, found:", len(actual.Objects)) + } +} + +func TestFilterByDisplayNameNotFound(t *testing.T) { + actual := testNetObjGroups.FilterByName("Nonexistent Policy") + + if len(actual.Objects) != 0 { + t.Fatal("Expected 0 policies, found:", len(actual.Objects)) + } +} diff --git a/pkg/sdn/netobjgroups/get.go b/pkg/sdn/netobjgroups/get.go new file mode 100644 index 0000000..77ee4b3 --- /dev/null +++ b/pkg/sdn/netobjgroups/get.go @@ -0,0 +1,47 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get info about a network object group +type GetRequest struct { + // ID of a network object group + // Required: true + NetObjGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` +} + +// Get gets network object group details as a NetworkObjectGroups struct +func (nog NetworkObjectGroups) Get(ctx context.Context, req GetRequest) (*RecordNetObjGroup, error) { + res, err := nog.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RecordNetObjGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets network object group details as an array of bytes +func (nog NetworkObjectGroups) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/get" + + res, err := nog.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/netobjgroups/list.go b/pkg/sdn/netobjgroups/list.go new file mode 100644 index 0000000..4fa32c7 --- /dev/null +++ b/pkg/sdn/netobjgroups/list.go @@ -0,0 +1,88 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of network object groups +type ListRequest struct { + // Filter by name + // Required: false + Name string `url:"name,omitempty" json:"name,omitempty"` + + // Filter by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Updated at lower bound (greater than or equal to) + // Required: false + UpdatedFrom string `url:"updated_from,omitempty" json:"updated_from,omitempty"` + + // Updated at upper bound (less than) + // Required: false + UpdatedTo string `url:"updated_to,omitempty" json:"updated_to,omitempty"` + + // Created at lower bound (greater than or equal to) + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Created at upper bound (less than) + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (name, created_at, updated_at) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` + + // Filter by type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` +} + +// List of address pools +func (nog NetworkObjectGroups) List(ctx context.Context, req ListRequest) (*NetObjGroupList, error) { + res, err := nog.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + objects := []RecordNetObjGroup{} + + err = json.Unmarshal(res, &objects) + if err != nil { + return nil, err + } + + result := NetObjGroupList{Objects: objects} + + return &result, nil +} + +// ListRaw gets a list of all address pools as an array of bytes +func (nog NetworkObjectGroups) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/list" + + res, err := nog.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/netobjgroups/models.go b/pkg/sdn/netobjgroups/models.go new file mode 100644 index 0000000..70519b1 --- /dev/null +++ b/pkg/sdn/netobjgroups/models.go @@ -0,0 +1,1002 @@ +package netobjgroups + +// Information about a version +type RecordVersion struct { + // Version ID + VersionID uint64 `json:"version_id"` +} + +// List of network object groups +type NetObjGroupList struct { + Objects []RecordNetObjGroup +} + +// Main info about a network object group +type RecordNetObjGroup struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Addresses + Addresses NetAddresses `json:"addresses"` + + // Counters + Counters Counter `json:"counters"` + + // Created at + CreatedAt string `json:"created_at"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Description + Description string `json:"description"` + + // External network ports attached to a network object group + ExternalNetworkPorts ExternalNetworkPorts `json:"external_network_ports"` + + // ID of a network object group + ID string `json:"id"` + + // L2 connection ports attached to a network object group + L2ConnectionPorts L2ConnectionPorts `json:"l2_connection_ports"` + + // Logical ports attached to a network object group + LogicalPorts LogicalPorts `json:"logical_ports"` + + // Name of a network object group + Name string `json:"name"` + + // Purpose of a network object group + Purpose string `json:"purpose"` + + // Security policies of a network object group + SecurityPolicies SecurityPolicies `json:"security_policies"` + + // Type of a network object group + Type string `json:"type"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Info about counters +type Counter struct { + // Amount of addresses + AddressesCount uint64 `json:"addresses_count"` + + // Amount of L2 connection ports + L2ConnectionPortsCount uint64 `json:"l2_connection_ports_count"` + + // Amount of logical ports + LogicalPortsCount uint64 `json:"logical_ports_count"` + + // Amount of security policies + SecurityPoliciesCount uint64 `json:"security_policies_count"` + + // Amount of security rules + SecurityRulesCount uint64 `json:"security_rules_count"` +} + +// Info about a net address +type NetAddress struct { + // ID + ID string `json:"id"` + + // IP address + IPAddr string `json:"ip_addr"` + + // IP address range end + IPAddrRangeEnd string `json:"ip_addr_range_end"` + + // IP prefix + IPPrefix string `json:"ip_prefix"` + + // MAC address + MACAddr string `json:"mac_addr"` + + // Net address type + NetAddressType string `json:"net_address_type"` +} + +// List of net addresses +type NetAddresses []NetAddress + +// Request info about a net address +type NetAddressRequest struct { + // IP address + // Required: false + IPAddr string `url:"ip_addr" json:"ip_addr"` + + // IP address range end + // Required: false + IPAddrRangeEnd string `url:"ip_addr_range_end" json:"ip_addr_range_end"` + + // IP prefix + // Required: false + IPPrefix string `url:"ip_prefix" json:"ip_prefix"` + + // MAC address + // Required: false + MACAddr string `url:"mac_addr" json:"mac_addr"` + + // Net address type + // Required: true + NetAddressType string `url:"net_address_type" json:"net_address_type" validate:"required"` +} + +// Info about an L2 connection port +type L2ConnectionPort struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Created at + CreatedAt string `json:"created_at"` + + // ID + ID string `json:"id"` + + // L2 external network + L2ExternalNetwork L2ExternalNetwork `json:"l2_external_network"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// List of L2 connection ports +type L2ConnectionPorts []L2ConnectionPort + +// Info about an L2 external network +type L2ExternalNetwork struct { + // Bridge network name + BridgeNetworkName string `json:"bridge_network_name"` + + // Created at + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Display name + DisplayName string `json:"display_name"` + + // Hypervisors + Hypervisors []string `json:"hypervisors"` + + // ID + ID string `json:"id"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // VLAN Tag + VLANTag *int `json:"vlan_tag"` +} + +// Info about an external network port +type ExternalNetworkPort struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Bridge network name + BridgeNetworkName string `json:"bridge_network_name"` + + // Comment + Comment string `json:"comment"` + + // Default gateway for IPv4 + DefaultGatewayIPv4 string `json:"default_gateway_ipv4"` + + // Default gateway for IPv6 + DefaultGatewayIPv6 string `json:"default_gateway_ipv6"` + + // Description + Description string `json:"description"` + + // Is a logical port enabled + Enabled bool `json:"enabled"` + + // Details of external network ports + ExternalNetworkPorts ExternalNetworkPortsField `json:"external_network_ports"` + + // Hypervisors + Hypervisors []string `json:"hypervisors"` + + // ID of an external network port + ID string `json:"id"` + + // IP v4 + IPv4 string `json:"ipv4"` + + // Status + Status Status `json:"status"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // Subnet for V4 + SubnetV4 string `json:"subnet_v4"` + + // Subnet for V6 + SubnetV6 string `json:"subnet_v6"` + + // Created at + CreatedAt string `json:"created_at"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // VLAN Tag + VLANTag int `json:"vlan_tag"` + + // MAC + MAC string `json:"mac"` +} + +// List of external network ports +type ExternalNetworkPorts []ExternalNetworkPort + +// Info about a logical port +type LogicalPort struct { + // ID + ID string `json:"id"` + + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Adapter MAC + AdapterMAC string `json:"adapter_mac"` + + // Is address detected + AddressDetection bool `json:"address_detection"` + + // Description + Description string `json:"description"` + + // Created at + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a logical port enabled + Enabled bool `json:"enabled"` + + // Exclude firewall settings + ExcludeFirewall ExcludeFirewall `json:"exclude_firewall"` + + // External network ID + ExternalNetworkID string `json:"external_network_id"` + + // Hypervisor + Hypervisor string `json:"hypervisor"` + + // Hypervisor display name + HypervisorDisplayName string `json:"hypervisor_display_name"` + + // Labels + Labels Labels `json:"labels"` + + // Live migration target HV + LiveMigrationTargetHV string `json:"live_migration_target_hv"` + + // Status + Status Status `json:"status"` + + // Bindings + Bindings Bindings `json:"bindings"` + + // Unique identifier + UniqueIdentifier string `json:"unique_identifier"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// List of logical ports +type LogicalPorts []LogicalPort + +// Info about exclude firewall settings +type ExcludeFirewall struct { + // Exclusion reason + ExclusionReason string `json:"exclusion_reason"` + + // Is logical port addresses excluded + LogicalPortAddressesExcluded bool `json:"logical_port_addresses_excluded"` + + // Is logical port excluded + LogicalPortExcluded bool `json:"logical_port_excluded"` +} + +// Labels for a logical port +type Labels struct { + // VM ID + VMID string `json:"vm_id"` + + // VM name + VMName string `json:"vm_name"` +} + +// Info about a security policy +type SecurityPolicy struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Applied net object groups + AppliedNetObjectGroups AppliedNetObjectGroups `json:"applied_net_object_groups"` + + // Created at + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` + + // End priority + EndPriority uint64 `json:"end_priority"` + + // ID + ID string `json:"id"` + + // Security rules + SecurityRules SecurityRules `json:"security_rules"` + + // Start priority + StartPriority uint64 `json:"start_priority"` + + // Status + Status Status `json:"status"` + + // Type + Type string `json:"type"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // Updated at + UpdatedAt string `json:"updated_at"` +} + +// List of security policies +type SecurityPolicies []SecurityPolicy + +// Info about an applied net object group in a security policy +type AppliedNetObjectGroup struct { + // ID + ID string `json:"id"` + + // Name + Name string `json:"name"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// List of applied net object groups +type AppliedNetObjectGroups []AppliedNetObjectGroup + +// Details of external network ports field +type ExternalNetworkPortField struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + Comment string `json:"comment"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` + + // IP v4 + IPv4 string `json:"ipv4"` + + // IP v6 + IPv6 string `json:"ipv6"` + + // Config for IP v6 + IPv6Config IPv6Config `json:"ipv6_config"` + + // MAC + MAC string `json:"mac"` + + // Info about a router gateaway port + RouterGateawayPort RouterGateawayPort `json:"router_gateaway_port"` + + // Info about a floating IP + FloatingIP FloatingIP `json:"floating_ip"` +} + +// List of external network ports fields +type ExternalNetworkPortsField []ExternalNetworkPortField + +// Info about a status +type Status struct { + // Operation status + OperationStatus string `json:"operation_status"` + + // Info about hypervisors + Hypervisors HypervisorsInfo `json:"hypervisors"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` +} + +// Config for IP v6 +type IPv6Config struct { + // Address mode + AddressMode string `json:"address_mode"` + + // Is periodic RA enabled + EnablePeriodicRA bool `json:"enable_periodic_ra"` + + // Interval RA + IntervalRA uint64 `json:"interval_ra"` + + // Router preference + RouterPreference string `json:"router_preference"` +} + +// Info about a router gateaway port +type RouterGateawayPort struct { + // Created at + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // ID + ID string `json:"id"` + + // Router display name + RouterDisplayName string `json:"router_display_name"` + + // Router ID + RouterID string `json:"router_id"` + + // Is SNAT enabled + SNATEnabled bool `json:"snat_enabled"` + + // Updated at + UpdatedAt string `json:"updated_at"` +} + +// Info about a floating IP +type FloatingIP struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created at + CreatedAt string `json:"created_at"` + + // External network port in floating IP + ExternalNetworkPort ExternalNetworkPortFieldInFloatingIP `json:"external_network_port"` + + // Logical port + LogicalPort LogicalPort `json:"logical_port"` + + // Router + Router Router `json:"router"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// TODO later +type ExternalNetworkPortFieldInFloatingIP struct{} + +// Info about a router +type Router struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created at + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` + + // Gateaway ports + GateawayPorts GateawayPorts `json:"gateaway_ports"` + + // ID + ID string `json:"id"` + + // Policies + Policies Policies `json:"policies"` + + // Port + Port Ports `json:"ports"` + + // Status + Status Status `json:"status"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Info about bindings +type Bindings struct { + // ID + ID string `json:"id"` + + // Segment display name + SegmentDisplayName string `json:"segment_display_name"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Is a port secured + PortSecurity bool `json:"port_security"` + + // Is an address detected + AddressDetection bool `json:"address_detection"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // Created at + CreatedAt string `json:"created_at"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Logical port addresses + LogicalPortAddresses LogicalPortAddresses `json:"logical_port_addresses"` +} + +// Info about a gateaway port +type GateawayPort struct { + // Created at + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Max external L4 port + ExternalL4PortMax uint64 `json:"external_l4_port_max"` + + // Min external L4 port + ExternalL4PortMin uint64 `json:"external_l4_port_min"` + + // External network port in floating IP + ExternalNetworkPortFieldInFloatingIP ExternalNetworkPortFieldInFloatingIP `json:"external_network_port"` // to check + + // ID + ID string `json:"id"` + + // Is SNAT enabled + SNATEnabled bool `json:"snat_enabled"` + + // Status + Status Status `json:"status"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// List of gateaway ports +type GateawayPorts []GateawayPort + +// Info about a policy +type Policy struct { + // Action + Action string `json:"action"` + + // Created at + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // Match + Match string `json:"match"` + + // List of next IP v4 addresses + NextIPv4Address []string `json:"next_ipv4_address"` + + // List of next IP v6 addresses + NextIPv6Address []string `json:"next_ipv6_address"` + + // Priority + Priority int `json:"priority"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// List of policies +type Policies []Policy + +// Info about a port +type Port struct { + // Created at + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // IPv4 address + IPv4Address string `json:"ipv4_address"` + + // IPv6 address + IPv6Address string `json:"ipv6_address"` + + // Config for IPv6 + IPv6Config IPv6Config `json:"ipv6_config"` + + // MAC + MAC string `json:"mac"` + + // Segment + Segment Segment `json:"segment"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Status + Status Status `json:"status"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // VersionID + VersionID uint64 `json:"version_id"` +} + +// List of ports +type Ports []Port + +// Info about a segment +type Segment struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created at + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + DHCPv4 DHCPv4 `json:"dhcp_v4"` + DHCPv6 DHCPv6 `json:"dhcp_v6"` + + // Display name + DisplayName string `json:"display_name"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // Info about logical ports + LogicalPortsInfo LogicalPortsInfo `json:"logical_ports_info"` + + // Info about routers + RoutersInfo RoutersInfo `json:"routers_info"` + + // Status + Status Status `json:"status"` + + // Subnet v4 + SubnetV4 string `json:"subnet_v4"` + + // Subnet v6 + SubnetV6 string `json:"subnet_v6"` + + // Updated at + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Info about DHCP v4 +type DHCPv4 struct { + // List of DNS + DNS []string `json:"dns"` + + // List of excluded address ranges + ExcludedAddressRanges []string `json:"excluded_address_ranges"` + + // Gateaway + Gateaway string `json:"gateaway"` + + // ID + ID string `json:"id"` + + // Lease time + LeaseTime uint64 `json:"lease_time"` + + // Server IP + ServerIP string `json:"server_ip"` + + // is there a server MAC + ServerMAC bool `json:"server_mac"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` +} + +// Info about DHCP v6 +type DHCPv6 struct { + // Address prefix + AddressPrefix string `json:"address_prefix"` + + // List of DNS + DNS []string `json:"dns"` + + // ID + ID string `json:"id"` + + // Lease time + LeaseTime uint64 `json:"lease_time"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Is a security policy enabled + Enabled bool `json:"enabled"` +} + +// Info about logical ports +type LogicalPortsInfo struct { + // Display name + DisplayName string `json:"display_name"` + + // ID + ID string `json:"id"` +} + +// Info about routers +type RoutersInfo struct { + // Display name + DisplayName string `json:"display_name"` + + // ID + ID string `json:"id"` +} + +// Info about a hypervisor +type HypervisorInfo struct { + // Operation status + OperationStatus string `json:"operation_status"` + + // Name + Name string `json:"name"` + + // Display name + DisplayName string `json:"display_name"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` + + // Synced at + SyncedAt string `json:"synced_at"` +} + +// List of hypervisors info +type HypervisorsInfo []HypervisorInfo + +type LogicalPortAddress struct { + // IP + IP string `json:"ip"` + + // IP Type + IPType string `json:"ip_type"` + + // Is logical port address discovered + IsDiscovered bool `json:"is_discovered"` + + // Is logical port address primary + IsPrimary bool `json:"is_primary"` + + // MAC + MAC string `json:"mac"` + + // ID + ID string `json:"id"` + + // Logical port ID + LogicalPortID string `json:"logical_port_id"` + + // Assigned at + AssignedAt string `json:"assigned_at"` +} + +// List of logical port addresses +type LogicalPortAddresses []LogicalPortAddress + +// Info about a security rule +type SecurityRule struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Action + Action string `json:"action"` + + // Description + Description string `json:"description"` + + // Destination net object + DestinationNetObject *SecurityRuleNetObject `json:"destination_net_object"` + + // Direction + Direction string `json:"direction"` + + // Display name + DisplayName string `json:"display_name"` + + // Is enabled + Enabled bool `json:"enabled"` + + // Filter + Filter *SecurityRuleFilter `json:"filter"` + + // ID + ID string `json:"id"` + + // Is log enabled + LogEnabled bool `json:"log_enabled"` + + // Log name + LogName string `json:"log_name"` + + // Log severity + LogSeverity string `json:"log_severity"` + + // Priority + Priority int `json:"priority"` + + // Security policy ID + SecurityPolicyID string `json:"security_policy_id"` + + // Source net object + SourceNetObject *SecurityRuleNetObject `json:"source_net_object"` + + // Is statistics enabled + StatisticsEnabled bool `json:"statistics_enabled"` + + // Type + Type string `json:"type"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// List of security rules +type SecurityRules []SecurityRule + +// Info about a security rule net object +type SecurityRuleNetObject struct { + // Display name + DisplayName string `json:"display_name"` + + // Net address pool ID + NetAddressPoolID string `json:"net_address_pool_id"` + + // Net object group ID + NetObjectGroupID string `json:"net_object_group_id"` +} + +// Info about a security rule filter +type SecurityRuleFilter struct { + // Filters + Filters SecurityRuleFilters `json:"filters"` + + // Name + Name string `json:"name"` +} + +// Security rule filters +type SecurityRuleFilters struct { + // Filter all traffic + All bool `json:"all"` + + // Filter ARP traffic + ARP bool `json:"arp"` + + // Filter DHCP traffic + DHCP bool `json:"dhcp"` + + // Custom filter expression + Expression string `json:"expression"` + + // Filter ICMP traffic + ICMP bool `json:"icmp"` + + // Filter IP traffic + IP bool `json:"ip"` + + // Filter IPv4 traffic + IPv4 bool `json:"ip_v4"` + + // Filter IPv6 traffic + IPv6 bool `json:"ip_v6"` + + // Keep tracking opened sessions + KeepOpenedSessions bool `json:"keep_opened_sessions"` + + // Filter Neighbor Discovery traffic + ND bool `json:"nd"` + + // Filter TCP traffic + TCP bool `json:"tcp"` + + // List of TCP destination ports to filter + TCPDstPorts []string `json:"tcp_dst_ports"` + + // Filter UDP traffic + UDP bool `json:"udp"` + + // List of UDP destination ports to filter + UDPDstPorts []string `json:"udp_dst_ports"` +} diff --git a/pkg/sdn/netobjgroups/network_object_groups.go b/pkg/sdn/netobjgroups/network_object_groups.go new file mode 100644 index 0000000..6d1c367 --- /dev/null +++ b/pkg/sdn/netobjgroups/network_object_groups.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN network object groups +package netobjgroups + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to network object groups +type NetworkObjectGroups struct { + client interfaces.Caller +} + +// Builder for external network object groups +func New(client interfaces.Caller) *NetworkObjectGroups { + return &NetworkObjectGroups{ + client, + } +} diff --git a/pkg/sdn/netobjgroups/update.go b/pkg/sdn/netobjgroups/update.go new file mode 100644 index 0000000..02efe9e --- /dev/null +++ b/pkg/sdn/netobjgroups/update.go @@ -0,0 +1,61 @@ +package netobjgroups + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update a network object group +type UpdateRequest struct { + // ID of the object group + // Required: true + ObjectGroupID string `url:"object_group_id" json:"object_group_id" validate:"required"` + + // ID of the version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Access Group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Description of the network object group + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Name of the network object group + // Required: true + Name string `url:"name" json:"name" validate:"required"` + + // Addresses + // Required: false + Addresses []NetAddressRequest `url:"addresses,omitempty" json:"addresses,omitempty" validate:"omitempty,dive"` +} + +// Update updates a network object group +func (nog NetworkObjectGroups) Update(ctx context.Context, req UpdateRequest) (*RecordNetObjGroup, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/network_object_group/update" + + res, err := nog.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RecordNetObjGroup{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/network_object_groups.go b/pkg/sdn/network_object_groups.go new file mode 100644 index 0000000..abd8b40 --- /dev/null +++ b/pkg/sdn/network_object_groups.go @@ -0,0 +1,10 @@ +package sdn + +import ( + nog "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/netobjgroups" +) + +// Accessing the SDN method group +func (sdn *SDN) NetworkObjectGroups() *nog.NetworkObjectGroups { + return nog.New(sdn.client) +} diff --git a/pkg/sdn/routers.go b/pkg/sdn/routers.go new file mode 100644 index 0000000..9166da0 --- /dev/null +++ b/pkg/sdn/routers.go @@ -0,0 +1,10 @@ +package sdn + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/routers" +) + +// Accessing the SDN method group +func (sdn *SDN) Routers() *routers.Routers { + return routers.New(sdn.client) +} diff --git a/pkg/sdn/routers/create.go b/pkg/sdn/routers/create.go new file mode 100644 index 0000000..768d4a3 --- /dev/null +++ b/pkg/sdn/routers/create.go @@ -0,0 +1,53 @@ +package routers + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create router +type CreateRequest struct { + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Description + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // Name of acces group + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Enabled True or False + // Required: true + Enabled interface{} `url:"enabled" json:"enabled" validate:"required,isBool"` +} + +// Create creates a access groups +func (i Routers) Create(ctx context.Context, req CreateRequest) (*RoutersModel, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/create" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RoutersModel{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/routers/delete.go b/pkg/sdn/routers/delete.go new file mode 100644 index 0000000..a92fd71 --- /dev/null +++ b/pkg/sdn/routers/delete.go @@ -0,0 +1,42 @@ +package routers + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct for delete router +type DeleteRequest struct { + // ID of router + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete + // Required: false + Force interface{} `url:"force,omitempty" json:"force,omitempty" validate:"omitempty,isBool"` +} + +// Delete delete a router +func (e Routers) Delete(ctx context.Context, req DeleteRequest) error { + err := validators.ValidateRequest(req) + if err != nil { + return validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/delete" + + _, err = e.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + + if err != nil { + return err + } + + return nil +} diff --git a/pkg/sdn/routers/filter.go b/pkg/sdn/routers/filter.go new file mode 100644 index 0000000..508a3dd --- /dev/null +++ b/pkg/sdn/routers/filter.go @@ -0,0 +1,42 @@ +package routers + +// FilterByID returns RoutersList with specified ID. +func (agl RoutersList) FilterByID(id string) RoutersList { + predicate := func(ia RoutersModel) bool { + return ia.ID == id + } + + return agl.FilterFunc(predicate) +} + +// FilterByName returns RoutersList with specified Name. +func (agl RoutersList) FilterByName(name string) RoutersList { + predicate := func(ia RoutersModel) bool { + return ia.DisplayName == name + } + + return agl.FilterFunc(predicate) +} + +// FilterFunc allows filtering RoutersList based on a user-specified predicate. +func (agl RoutersList) FilterFunc(predicate func(RoutersModel) bool) RoutersList { + var result RoutersList + + for _, acc := range agl { + if predicate(acc) { + result = append(result, acc) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (agl RoutersList) FindOne() RoutersModel { + if len(agl) == 0 { + return RoutersModel{} + } + + return agl[0] +} diff --git a/pkg/sdn/routers/filter_test.go b/pkg/sdn/routers/filter_test.go new file mode 100644 index 0000000..8dff8a6 --- /dev/null +++ b/pkg/sdn/routers/filter_test.go @@ -0,0 +1,309 @@ +package routers + +import ( + "testing" +) + +var testRoutersList = RoutersList{ + { + ID: "router1", + DisplayName: "DevelopersRouter", + Description: "First router", + CreatedAt: "2023-01-01", + AccessGroupID: "group1", + AccessGroupName: "Developers", + Enabled: true, + GatewayPorts: []GatewayPort{ + { + ID: "gateway1", + Description: "Gateway port 1", + SNATEnabled: true, + ExternalL4PortMin: 1000, + ExternalL4PortMax: 2000, + CreatedAt: "2023-01-01", + UpdatedAt: "2023-01-01", + VersionID: 1, + ExternalNetworkPort: ExternalNetworkPort{ + IPv4: "192.168.1.1", + IPv6: "2001:db8::1", + }, + }, + }, + Policies: []Policy{ + { + ID: "policy1", + DisplayName: "Policy1", + Action: "allow", + Priority: 1, + Enabled: true, + }, + }, + Ports: []Port{ + { + ID: "port1", + Description: "Port 1", + Enabled: true, + IPv4Address: "10.0.0.1", + }, + }, + Status: Status{ + Common: "active", + }, + VersionID: 1, + }, + { + ID: "router2", + DisplayName: "AdminsRouter", + Description: "Second router", + CreatedAt: "2023-01-02", + AccessGroupID: "group2", + AccessGroupName: "Admins", + Enabled: true, + GatewayPorts: []GatewayPort{ + { + ID: "gateway2", + Description: "Gateway port 2", + SNATEnabled: false, + ExternalL4PortMin: 3000, + ExternalL4PortMax: 4000, + CreatedAt: "2023-01-02", + UpdatedAt: "2023-01-02", + VersionID: 1, + ExternalNetworkPort: ExternalNetworkPort{ + IPv4: "192.168.1.2", + IPv6: "2001:db8::2", + }, + }, + }, + Policies: []Policy{ + { + ID: "policy2", + DisplayName: "Policy2", + Action: "deny", + Priority: 2, + Enabled: true, + }, + }, + Ports: []Port{ + { + ID: "port2", + Description: "Port 2", + Enabled: true, + IPv4Address: "10.0.0.2", + }, + }, + Status: Status{ + Common: "active", + }, + VersionID: 2, + }, + { + ID: "router3", + DisplayName: "UsersRouter", + Description: "Third router", + CreatedAt: "2023-01-03", + AccessGroupID: "group3", + AccessGroupName: "Users", + Enabled: false, + GatewayPorts: []GatewayPort{ + { + ID: "gateway3", + Description: "Gateway port 3", + SNATEnabled: true, + ExternalL4PortMin: 5000, + ExternalL4PortMax: 6000, + CreatedAt: "2023-01-03", + UpdatedAt: "2023-01-03", + VersionID: 1, + ExternalNetworkPort: ExternalNetworkPort{ + IPv4: "192.168.1.3", + IPv6: "2001:db8::3", + }, + }, + }, + Policies: []Policy{ + { + ID: "policy3", + DisplayName: "Policy3", + Action: "allow", + Priority: 3, + Enabled: false, + }, + }, + Ports: []Port{ + { + ID: "port3", + Description: "Port 3", + Enabled: false, + IPv4Address: "10.0.0.3", + }, + }, + Status: Status{ + Common: "inactive", + }, + VersionID: 3, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testRoutersList.FilterByID("router2").FindOne() + + if actual.ID != "router2" { + t.Fatal("actual:", actual.ID, "> expected: router2") + } +} + +func TestFilterByName(t *testing.T) { + actual := testRoutersList.FilterByName("UsersRouter").FindOne() + + if actual.DisplayName != "UsersRouter" { + t.Fatal("actual:", actual.DisplayName, ">> expected: UsersRouter") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + return rm.Description == "Second router" + }) + + if len(actual) != 1 || actual[0].ID != "router2" { + t.Fatal("Expected 1 router with description 'Second router', found:", len(actual)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testRoutersList.FilterByID("router1").FindOne() + if result.ID != "router1" { + t.Fatal("Expected router1, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := RoutersList{} + result := emptyList.FindOne() + + if result.ID != "" || result.DisplayName != "" { + t.Fatal("Expected empty RoutersModel, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := testRoutersList.FilterByID("nonexistent") + + if len(actual) != 0 { + t.Fatal("Expected 0 routers, found:", len(actual)) + } +} + +func TestFilterByNameNotFound(t *testing.T) { + actual := testRoutersList.FilterByName("Nonexistent Router") + + if len(actual) != 0 { + t.Fatal("Expected 0 routers, found:", len(actual)) + } +} + +func TestFilterByEnabledStatus(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + return rm.Enabled + }) + + if len(actual) != 2 { + t.Fatal("Expected 2 enabled routers, found:", len(actual)) + } +} + +func TestFilterByAccessGroup(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + return rm.AccessGroupName == "Developers" + }) + + if len(actual) != 1 || actual[0].ID != "router1" { + t.Fatal("Expected 1 router with Developers access group, found:", len(actual)) + } +} + +func TestFilterByPolicyAction(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + for _, policy := range rm.Policies { + if policy.Action == "deny" { + return true + } + } + return false + }) + + if len(actual) != 1 || actual[0].ID != "router2" { + t.Fatal("Expected 1 router with deny policy, found:", len(actual)) + } +} + +func TestFilterByGatewayPortRange(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + for _, gateway := range rm.GatewayPorts { + if gateway.ExternalL4PortMin >= 3000 && gateway.ExternalL4PortMax <= 4000 { + return true + } + } + return false + }) + + if len(actual) != 1 || actual[0].ID != "router2" { + t.Fatal("Expected 1 router with gateway port range 3000-4000, found:", len(actual)) + } +} + +func TestFilterBySNATEnabled(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + for _, gateway := range rm.GatewayPorts { + if gateway.SNATEnabled { + return true + } + } + return false + }) + + if len(actual) != 2 { + t.Fatal("Expected 2 routers with SNAT enabled, found:", len(actual)) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + return rm.Status.Common == "inactive" + }) + + if len(actual) != 1 || actual[0].ID != "router3" { + t.Fatal("Expected 1 router with inactive status, found:", len(actual)) + } +} + +func TestFilterByPortEnabled(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + for _, port := range rm.Ports { + if port.Enabled { + return true + } + } + return false + }) + + if len(actual) != 2 { + t.Fatal("Expected 2 routers with enabled ports, found:", len(actual)) + } +} + +func TestFilterByPolicyPriority(t *testing.T) { + actual := testRoutersList.FilterFunc(func(rm RoutersModel) bool { + for _, policy := range rm.Policies { + if policy.Priority > 2 { + return true + } + } + return false + }) + + if len(actual) != 1 || actual[0].ID != "router3" { + t.Fatal("Expected 1 router with policy priority > 2, found:", len(actual)) + } +} diff --git a/pkg/sdn/routers/gateaway_port.go b/pkg/sdn/routers/gateaway_port.go new file mode 100644 index 0000000..42d66f4 --- /dev/null +++ b/pkg/sdn/routers/gateaway_port.go @@ -0,0 +1,10 @@ +package routers + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/routers/gwport" +) + +// Accessing the routers gateway port method group +func (r *Routers) GWPort() *gwport.GWPort { + return gwport.New(r.client) +} diff --git a/pkg/sdn/routers/get.go b/pkg/sdn/routers/get.go new file mode 100644 index 0000000..8fd39b1 --- /dev/null +++ b/pkg/sdn/routers/get.go @@ -0,0 +1,47 @@ +package routers + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about router +type GetRequest struct { + // ID + // Required: true + ID string `url:"router_id" json:"router_id" validate:"required"` +} + +// Get gets routers details as a RoutersModel struct +func (a Routers) Get(ctx context.Context, req GetRequest) (*RoutersModel, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := RoutersModel{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets routers details as an array of bytes +func (a Routers) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/routers/gwport/create.go b/pkg/sdn/routers/gwport/create.go new file mode 100644 index 0000000..c08dd5b --- /dev/null +++ b/pkg/sdn/routers/gwport/create.go @@ -0,0 +1,57 @@ +package gwport + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create gateway port +type CreateRequest struct { + // ID of router + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Detailed description of the external network + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // External network port ID + // Required: true + ExternalNetworkPortID string `url:"external_network_port_id" json:"external_network_port_id" validate:"required"` + + // Whether is enabled + // Required: true + Enabled bool `url:"snat_enabled" json:"snat_enabled"` +} + +// Create creates a gateway port +func (i GWPort) Create(ctx context.Context, req CreateRequest) (*GatewayPort, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/gateway_port/create" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := GatewayPort{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/routers/gwport/delete.go b/pkg/sdn/routers/gwport/delete.go new file mode 100644 index 0000000..b37c835 --- /dev/null +++ b/pkg/sdn/routers/gwport/delete.go @@ -0,0 +1,46 @@ +package gwport + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct for delete router +type DeleteRequest struct { + // ID of router + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` + + // ID of gateway port + // Required: true + GatewayPortID string `url:"gateway_port_id" json:"gateway_port_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete + // Required: false + Force interface{} `url:"force,omitempty" json:"force,omitempty" validate:"omitempty,isBool"` +} + +// Delete delete a router +func (e GWPort) Delete(ctx context.Context, req DeleteRequest) error { + err := validators.ValidateRequest(req) + if err != nil { + return validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/gateway_port/delete" + + _, err = e.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + + if err != nil { + return err + } + + return nil +} diff --git a/pkg/sdn/routers/gwport/gateaway_port.go b/pkg/sdn/routers/gwport/gateaway_port.go new file mode 100644 index 0000000..0a1de8c --- /dev/null +++ b/pkg/sdn/routers/gwport/gateaway_port.go @@ -0,0 +1,19 @@ +package gwport + +// API Actor API for managing SDN routers gateway port + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to routers gateway port +type GWPort struct { + client interfaces.Caller +} + +// Builder for routers gateway port endpoints +func New(client interfaces.Caller) *GWPort { + return &GWPort{ + client, + } +} diff --git a/pkg/sdn/routers/gwport/get.go b/pkg/sdn/routers/gwport/get.go new file mode 100644 index 0000000..d6563c8 --- /dev/null +++ b/pkg/sdn/routers/gwport/get.go @@ -0,0 +1,51 @@ +package gwport + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about gateway port +type GetRequest struct { + // Router ID + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` + + // Gateway port ID + // Required: true + GatewayPortID string `url:"gateway_port_id" json:"gateway_port_id" validate:"required"` +} + +// Get gets gateway port details as a GatewayPort struct +func (a GWPort) Get(ctx context.Context, req GetRequest) (*GatewayPort, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := GatewayPort{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets gateway port details as an array of bytes +func (a GWPort) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/gateway_port/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/routers/gwport/list.go b/pkg/sdn/routers/gwport/list.go new file mode 100644 index 0000000..f97e4ea --- /dev/null +++ b/pkg/sdn/routers/gwport/list.go @@ -0,0 +1,47 @@ +package gwport + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get list of gateway ports +type ListRequest struct { + // Router ID + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` +} + +// List gets gateway port list +func (a GWPort) List(ctx context.Context, req ListRequest) (GatewayPortsList, error) { + res, err := a.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + info := []GatewayPort{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return info, nil + +} + +// GetRaw gets gateway port list as an array of bytes +func (a GWPort) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/gateway_port/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/routers/gwport/models.go b/pkg/sdn/routers/gwport/models.go new file mode 100644 index 0000000..1ebcf66 --- /dev/null +++ b/pkg/sdn/routers/gwport/models.go @@ -0,0 +1,481 @@ +package gwport + +// List of ports +type GatewayPortsList []GatewayPort + +// Gateway port information +type GatewayPort struct { + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // External L4 port maximum + ExternalL4PortMax int `json:"external_l4_port_max"` + + // External L4 port minimum + ExternalL4PortMin int `json:"external_l4_port_min"` + + // External network port + ExternalNetworkPort ExternalNetworkPort `json:"external_network_port"` + + // ID + ID string `json:"id"` + + // SNAT enabled flag + SNATEnabled bool `json:"snat_enabled"` + + // Status information + Status Status `json:"status"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// External network port information +type ExternalNetworkPort struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Comment + Comment string `json:"comment"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // IPv4 address + IPv4 string `json:"ipv4"` + + // IPv6 address + IPv6 string `json:"ipv6"` + + // IPv6 configuration + IPv6Config IPv6Config `json:"ipv6_config"` + + // MAC address + MAC string `json:"mac"` + + // Router gateway port + RouterGatewayPort RouterGatewayPort `json:"router_gateway_port"` + + // Floating IP + FloatingIP FloatingIP `json:"floating_ip"` +} + +// IPv6 configuration information +type IPv6Config struct { + // Address mode + AddressMode string `json:"address_mode"` + + // Enable periodic RA flag + EnablePeriodicRA bool `json:"enable_periodic_ra"` + + // Interval RA + IntervalRA int `json:"interval_ra"` + + // Router preference + RouterPreference string `json:"router_preference"` +} + +// Router gateway port information +type RouterGatewayPort struct { + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // ID + ID string `json:"id"` + + // Router display name + RouterDisplayName string `json:"router_display_name"` + + // Router ID + RouterID string `json:"router_id"` + + // SNAT enabled flag + SNATEnabled bool `json:"snat_enabled"` + + // Updated time + UpdatedAt string `json:"updated_at"` +} + +// Floating IP information +type FloatingIP struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt string `json:"created_at"` + + // External network port + ExternalNetworkPort string `json:"external_network_port"` + + // ID + ID string `json:"id"` + + // Logical port + LogicalPort LogicalPort `json:"logical_port"` + + // Router + Router string `json:"router"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Logical port information +type LogicalPort struct { + // ID + ID string `json:"id"` + + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Adapter MAC + AdapterMAC string `json:"adapter_mac"` + + // Address detection flag + AddressDetection bool `json:"address_detection"` + + // Description + Description string `json:"description"` + + // Created time + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // External network ID + ExternalNetworkID string `json:"external_network_id"` + + // Hypervisor + Hypervisor string `json:"hypervisor"` + + // Hypervisor display name + HypervisorDisplayName string `json:"hypervisor_display_name"` + + // Live migration target HV + LiveMigrationTargetHV string `json:"live_migration_target_hv"` + + // Status information + Status Status `json:"status"` + + // Bindings information + Bindings Bindings `json:"bindings"` + + // Unique identifier + UniqueIdentifier string `json:"unique_identifier"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Status information +type Status struct { + // Common status + Common string `json:"common"` + + // Hypervisors status list + Hypervisors []HypervisorStatus `json:"hypervisors"` +} + +// Hypervisor status information +type HypervisorStatus struct { + // Status + Status string `json:"status"` + + // Name + Name string `json:"name"` + + // Display name + DisplayName string `json:"display_name"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` + + // Synced at time + SyncedAt string `json:"synced_at"` +} + +// Bindings information +type Bindings struct { + // ID + ID string `json:"id"` + + // Segment display name + SegmentDisplayName string `json:"segment_display_name"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Port security flag + PortSecurity bool `json:"port_security"` + + // Address detection flag + AddressDetection bool `json:"address_detection"` + + // Is excluded from firewall flag + IsExcludedFromFirewall bool `json:"is_excluded_from_firewall"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // Created time + CreatedAt string `json:"created_at"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Logical port addresses list + LogicalPortAddresses []LogicalPortAddress `json:"logical_port_addresses"` +} + +// Logical port address information +type LogicalPortAddress struct { + // IP address + IP string `json:"ip"` + + // IP type + IPType string `json:"ip_type"` + + // Is discovered flag + IsDiscovered bool `json:"is_discovered"` + + // Is primary flag + IsPrimary bool `json:"is_primary"` + + // MAC address + MAC string `json:"mac"` + + // ID + ID string `json:"id"` + + // Logical port ID + LogicalPortID string `json:"logical_port_id"` + + // Assigned at time + AssignedAt string `json:"assigned_at"` +} + +// Policy information +type Policy struct { + // Action + Action string `json:"action"` + + // Created time + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // Match criteria + Match map[string]interface{} `json:"match"` + + // Next IPv4 addresses list + NextIPv4Address []string `json:"next_ipv4_address"` + + // Next IPv6 addresses list + NextIPv6Address []string `json:"next_ipv6_address"` + + // Priority + Priority int `json:"priority"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Port information +type Port struct { + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // IPv4 address + IPv4Address string `json:"ipv4_address"` + + // IPv6 address + IPv6Address string `json:"ipv6_address"` + + // IPv6 configuration + IPv6Config IPv6Config `json:"ipv6_config"` + + // MAC address + MAC string `json:"mac"` + + // Segment information + Segment Segment `json:"segment"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Status information + Status Status `json:"status"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Segment information +type Segment struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // DHCPv4 configuration + DHCPv4 DHCPv4 `json:"dhcp_v4"` + + // DHCPv6 configuration + DHCPv6 DHCPv6 `json:"dhcp_v6"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // Logical ports info list + LogicalPortsInfo []LogicalPortInfo `json:"logical_ports_info"` + + // Routers info list + RoutersInfo []RouterInfo `json:"routers_info"` + + // Status information + Status Status `json:"status"` + + // IPv4 subnet + SubnetV4 string `json:"subnet_v4"` + + // IPv6 subnet + SubnetV6 string `json:"subnet_v6"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// DHCPv4 configuration +type DHCPv4 struct { + // DNS servers list + DNS []string `json:"dns"` + + // Excluded address ranges list + ExcludedAddressRanges []string `json:"excluded_address_ranges"` + + // Gateway address + Gateway string `json:"gateway"` + + // ID + ID string `json:"id"` + + // Lease time + LeaseTime int `json:"lease_time"` + + // Server IP + ServerIP string `json:"server_ip"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Enabled flag + Enabled bool `json:"enabled"` +} + +// DHCPv6 configuration +type DHCPv6 struct { + // Address prefix + AddressPrefix string `json:"address_prefix"` + + // DNS servers list + DNS []string `json:"dns"` + + // ID + ID string `json:"id"` + + // Lease time + LeaseTime int `json:"lease_time"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Enabled flag + Enabled bool `json:"enabled"` +} + +// Logical port info +type LogicalPortInfo struct { + // Display name + DisplayName string `json:"display_name"` + + // ID + ID string `json:"id"` +} + +// Router info +type RouterInfo struct { + // Display name + DisplayName string `json:"display_name"` + + // ID + ID string `json:"id"` +} diff --git a/pkg/sdn/routers/gwport/update.go b/pkg/sdn/routers/gwport/update.go new file mode 100644 index 0000000..dc7750c --- /dev/null +++ b/pkg/sdn/routers/gwport/update.go @@ -0,0 +1,61 @@ +package gwport + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update gateway port +type UpdateRequest struct { + // ID of router + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` + + // ID of gateway port + // Required: true + GatewayPortID string `url:"gateway_port_id" json:"gateway_port_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Detailed description of the external network + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // External network port ID + // Required: true + ExternalNetworkPortID string `url:"external_network_port_id" json:"external_network_port_id" validate:"required"` + + // Whether is enabled + // Required: true + Enabled bool `url:"snat_enabled" json:"snat_enabled"` +} + +// Updated update a gateway port +func (i GWPort) Update(ctx context.Context, req UpdateRequest) (*GatewayPort, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/gateway_port/update" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := GatewayPort{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/routers/ids.go b/pkg/sdn/routers/ids.go new file mode 100644 index 0000000..d2fc6bd --- /dev/null +++ b/pkg/sdn/routers/ids.go @@ -0,0 +1,10 @@ +package routers + +// IDs gets array of IDs from RoutersList struct +func (rl RoutersList) IDs() []string { + res := make([]string, 0, len(rl)) + for _, c := range rl { + res = append(res, c.ID) + } + return res +} diff --git a/pkg/sdn/routers/list.go b/pkg/sdn/routers/list.go new file mode 100644 index 0000000..e70ed9b --- /dev/null +++ b/pkg/sdn/routers/list.go @@ -0,0 +1,90 @@ +package routers + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of routers +type ListRequest struct { + // Filter by display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Filter by enabled status + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Filter by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Filter by description + // Required: false + Description string `url:"description,omitempty" json:"description,omitempty"` + + // Filter by create date from + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Filter by create date to + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` + + // Filter by update date from + // Required: false + UpdatedFrom string `url:"updated_from,omitempty" json:"updated_from,omitempty"` + + // Filter by update date to + // Required: false + UpdatedTo string `url:"updated_to,omitempty" json:"updated_to,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, subnet, created_at, updated_at) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty" validate:"omitempty,oneof=display_name subnet created_at updated_at"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty" validate:"omitempty,oneof=asc desc"` +} + +// List of routers +func (i Routers) List(ctx context.Context, req ListRequest) (RoutersList, error) { + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + pools := []RoutersModel{} + + err = json.Unmarshal(res, &pools) + if err != nil { + return nil, err + } + + return pools, nil +} + +// ListRaw gets a list of all routers as an array of bytes +func (a Routers) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/routers/models.go b/pkg/sdn/routers/models.go new file mode 100644 index 0000000..9e36b9a --- /dev/null +++ b/pkg/sdn/routers/models.go @@ -0,0 +1,523 @@ +package routers + +// List of routers +type RoutersList []RoutersModel + +// Main information about router +type RoutersModel struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // List of gateway ports + GatewayPorts []GatewayPort `json:"gateway_ports"` + + // ID + ID string `json:"id"` + + // List of policies + Policies []Policy `json:"policies"` + + // List of ports + Ports []Port `json:"ports"` + + // Status information + Status Status `json:"status"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Gateway port information +type GatewayPort struct { + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // External L4 port maximum + ExternalL4PortMax int `json:"external_l4_port_max"` + + // External L4 port minimum + ExternalL4PortMin int `json:"external_l4_port_min"` + + // External network port + ExternalNetworkPort ExternalNetworkPort `json:"external_network_port"` + + // ID + ID string `json:"id"` + + // SNAT enabled flag + SNATEnabled bool `json:"snat_enabled"` + + // Status information + Status Status `json:"status"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// External network port information +type ExternalNetworkPort struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Comment + Comment string `json:"comment"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // IPv4 address + IPv4 string `json:"ipv4"` + + // IPv6 address + IPv6 string `json:"ipv6"` + + // IPv6 configuration + IPv6Config IPv6Config `json:"ipv6_config"` + + // MAC address + MAC string `json:"mac"` + + // Router gateway port + RouterGatewayPort RouterGatewayPort `json:"router_gateway_port"` + + // Floating IP + FloatingIP FloatingIP `json:"floating_ip"` +} + +// IPv6 configuration information +type IPv6Config struct { + // Address mode + AddressMode string `json:"address_mode"` + + // Enable periodic RA flag + EnablePeriodicRA bool `json:"enable_periodic_ra"` + + // Interval RA + IntervalRA int `json:"interval_ra"` + + // Router preference + RouterPreference string `json:"router_preference"` +} + +// Router gateway port information +type RouterGatewayPort struct { + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // ID + ID string `json:"id"` + + // Router display name + RouterDisplayName string `json:"router_display_name"` + + // Router ID + RouterID string `json:"router_id"` + + // SNAT enabled flag + SNATEnabled bool `json:"snat_enabled"` + + // Updated time + UpdatedAt string `json:"updated_at"` +} + +// Floating IP information +type FloatingIP struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt string `json:"created_at"` + + // External network port + ExternalNetworkPort string `json:"external_network_port"` + + // ID + ID string `json:"id"` + + // Logical port + LogicalPort LogicalPort `json:"logical_port"` + + // Router + Router string `json:"router"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Logical port information +type LogicalPort struct { + // ID + ID string `json:"id"` + + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Adapter MAC + AdapterMAC string `json:"adapter_mac"` + + // Address detection flag + AddressDetection bool `json:"address_detection"` + + // Description + Description string `json:"description"` + + // Created time + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // External network ID + ExternalNetworkID string `json:"external_network_id"` + + // Hypervisor + Hypervisor string `json:"hypervisor"` + + // Hypervisor display name + HypervisorDisplayName string `json:"hypervisor_display_name"` + + // Live migration target HV + LiveMigrationTargetHV string `json:"live_migration_target_hv"` + + // Status information + Status Status `json:"status"` + + // Bindings information + Bindings Bindings `json:"bindings"` + + // Unique identifier + UniqueIdentifier string `json:"unique_identifier"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Status information +type Status struct { + // Common status + Common string `json:"common"` + + // Hypervisors status list + Hypervisors []HypervisorStatus `json:"hypervisors"` +} + +// Hypervisor status information +type HypervisorStatus struct { + // Status + Status string `json:"status"` + + // Name + Name string `json:"name"` + + // Display name + DisplayName string `json:"display_name"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` + + // Synced at time + SyncedAt string `json:"synced_at"` +} + +// Bindings information +type Bindings struct { + // ID + ID string `json:"id"` + + // Segment display name + SegmentDisplayName string `json:"segment_display_name"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Port security flag + PortSecurity bool `json:"port_security"` + + // Address detection flag + AddressDetection bool `json:"address_detection"` + + // Is excluded from firewall flag + IsExcludedFromFirewall bool `json:"is_excluded_from_firewall"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // Created time + CreatedAt string `json:"created_at"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Logical port addresses list + LogicalPortAddresses []LogicalPortAddress `json:"logical_port_addresses"` +} + +// Logical port address information +type LogicalPortAddress struct { + // IP address + IP string `json:"ip"` + + // IP type + IPType string `json:"ip_type"` + + // Is discovered flag + IsDiscovered bool `json:"is_discovered"` + + // Is primary flag + IsPrimary bool `json:"is_primary"` + + // MAC address + MAC string `json:"mac"` + + // ID + ID string `json:"id"` + + // Logical port ID + LogicalPortID string `json:"logical_port_id"` + + // Assigned at time + AssignedAt string `json:"assigned_at"` +} + +// Policy information +type Policy struct { + // Action + Action string `json:"action"` + + // Created time + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // Match criteria + Match map[string]interface{} `json:"match"` + + // Next IPv4 addresses list + NextIPv4Address []string `json:"next_ipv4_address"` + + // Next IPv6 addresses list + NextIPv6Address []string `json:"next_ipv6_address"` + + // Priority + Priority int `json:"priority"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Port information +type Port struct { + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // IPv4 address + IPv4Address string `json:"ipv4_address"` + + // IPv6 address + IPv6Address string `json:"ipv6_address"` + + // IPv6 configuration + IPv6Config IPv6Config `json:"ipv6_config"` + + // MAC address + MAC string `json:"mac"` + + // Segment information + Segment Segment `json:"segment"` + + // Segment ID + SegmentID string `json:"segment_id"` + + // Status information + Status Status `json:"status"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Segment information +type Segment struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // DHCPv4 configuration + DHCPv4 DHCPv4 `json:"dhcp_v4"` + + // DHCPv6 configuration + DHCPv6 DHCPv6 `json:"dhcp_v6"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // Logical ports info list + LogicalPortsInfo []LogicalPortInfo `json:"logical_ports_info"` + + // Routers info list + RoutersInfo []RouterInfo `json:"routers_info"` + + // Status information + Status Status `json:"status"` + + // IPv4 subnet + SubnetV4 string `json:"subnet_v4"` + + // IPv6 subnet + SubnetV6 string `json:"subnet_v6"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// DHCPv4 configuration +type DHCPv4 struct { + // DNS servers list + DNS []string `json:"dns"` + + // Excluded address ranges list + ExcludedAddressRanges []string `json:"excluded_address_ranges"` + + // Gateway address + Gateway string `json:"gateway"` + + // ID + ID string `json:"id"` + + // Lease time + LeaseTime int `json:"lease_time"` + + // Server IP + ServerIP string `json:"server_ip"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Enabled flag + Enabled bool `json:"enabled"` +} + +// DHCPv6 configuration +type DHCPv6 struct { + // Address prefix + AddressPrefix string `json:"address_prefix"` + + // DNS servers list + DNS []string `json:"dns"` + + // ID + ID string `json:"id"` + + // Lease time + LeaseTime int `json:"lease_time"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Enabled flag + Enabled bool `json:"enabled"` +} + +// Logical port info +type LogicalPortInfo struct { + // Display name + DisplayName string `json:"display_name"` + + // ID + ID string `json:"id"` +} + +// Router info +type RouterInfo struct { + // Display name + DisplayName string `json:"display_name"` + + // ID + ID string `json:"id"` +} diff --git a/pkg/sdn/routers/policies.go b/pkg/sdn/routers/policies.go new file mode 100644 index 0000000..dacd680 --- /dev/null +++ b/pkg/sdn/routers/policies.go @@ -0,0 +1,10 @@ +package routers + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/routers/policies" +) + +// Accessing the routers policies method group +func (r *Routers) Policies() *policies.Policies { + return policies.New(r.client) +} diff --git a/pkg/sdn/routers/policies/list.go b/pkg/sdn/routers/policies/list.go new file mode 100644 index 0000000..e988959 --- /dev/null +++ b/pkg/sdn/routers/policies/list.go @@ -0,0 +1,66 @@ +package policies + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of policies +type ListRequest struct { + // Router ID + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` + + // Filter by display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, subnet, created_at, updated_at) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty" validate:"omitempty,oneof=display_name subnet created_at updated_at"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty" validate:"omitempty,oneof=asc desc"` +} + +// List of policies +func (i Policies) List(ctx context.Context, req ListRequest) (PoliciesList, error) { + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + pools := []Policy{} + + err = json.Unmarshal(res, &pools) + if err != nil { + return nil, err + } + + return pools, nil +} + +// ListRaw gets a list of all policies as an array of bytes +func (a Policies) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/policies/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/routers/policies/models.go b/pkg/sdn/routers/policies/models.go new file mode 100644 index 0000000..2587820 --- /dev/null +++ b/pkg/sdn/routers/policies/models.go @@ -0,0 +1,39 @@ +package policies + +type PoliciesList []Policy + +// Policy information +type Policy struct { + // Action + Action string `json:"action"` + + // Created time + CreatedAt string `json:"created_at"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // ID + ID string `json:"id"` + + // Match criteria + Match map[string]interface{} `json:"match"` + + // Next IPv4 addresses list + NextIPv4Address []string `json:"next_ipv4_address"` + + // Next IPv6 addresses list + NextIPv6Address []string `json:"next_ipv6_address"` + + // Priority + Priority int `json:"priority"` + + // Updated time + UpdatedAt string `json:"updated_at"` + + // Version ID + VersionID uint64 `json:"version_id"` +} diff --git a/pkg/sdn/routers/policies/policies.go b/pkg/sdn/routers/policies/policies.go new file mode 100644 index 0000000..d783a34 --- /dev/null +++ b/pkg/sdn/routers/policies/policies.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN routers policies +package policies + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to routers policies +type Policies struct { + client interfaces.Caller +} + +// Builder for routers policies endpoints +func New(client interfaces.Caller) *Policies { + return &Policies{ + client, + } +} diff --git a/pkg/sdn/routers/routers.go b/pkg/sdn/routers/routers.go new file mode 100644 index 0000000..ce9e491 --- /dev/null +++ b/pkg/sdn/routers/routers.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN routers +package routers + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to routers +type Routers struct { + client interfaces.Caller +} + +// Builder for routers endpoints +func New(client interfaces.Caller) *Routers { + return &Routers{ + client, + } +} diff --git a/pkg/sdn/routers/serialize.go b/pkg/sdn/routers/serialize.go new file mode 100644 index 0000000..8a64a0b --- /dev/null +++ b/pkg/sdn/routers/serialize.go @@ -0,0 +1,27 @@ +package routers + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 RoutersList) Serialize(params ...string) (serialization.Serialized, error) { + if len(la) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(la, prefix, indent) + } + + return json.Marshal(la) +} diff --git a/pkg/sdn/routers/update.go b/pkg/sdn/routers/update.go new file mode 100644 index 0000000..06ebe54 --- /dev/null +++ b/pkg/sdn/routers/update.go @@ -0,0 +1,57 @@ +package routers + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct for update router +type UpdateRequest struct { + // ID of router + // Required: true + RouterID string `url:"router_id" json:"router_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Detailed description of the external network + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // User-friendly name for the external network + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the network is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` +} + +// Update updated a router +func (e Routers) Update(ctx context.Context, req UpdateRequest) (*RoutersModel, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/router/update" + + res, err := e.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := RoutersModel{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/sdn.go b/pkg/sdn/sdn.go new file mode 100644 index 0000000..26d491f --- /dev/null +++ b/pkg/sdn/sdn.go @@ -0,0 +1,16 @@ +// List of method groups for the SDN +package sdn + +import "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" + +// Structure for creating request to SDN groups +type SDN struct { + client interfaces.Caller +} + +// Builder to get access to SDN +func New(client interfaces.Caller) *SDN { + return &SDN{ + client: client, + } +} diff --git a/pkg/sdn/secpolicies/create.go b/pkg/sdn/secpolicies/create.go new file mode 100644 index 0000000..64e7f52 --- /dev/null +++ b/pkg/sdn/secpolicies/create.go @@ -0,0 +1,77 @@ +package secpolicies + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct to create security policy +type CreateRequest struct { + // Access group ID + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Applied to net object group ID + // Required: true + AppliedToNetObjectGroupID string `url:"applied_to_net_object_group_id" json:"applied_to_net_object_group_id" validate:"required"` + + // Description of the schedule rule + // Required: true + Description string `url:"description" json:"description"` + + // Display name of the schedule rule + // Required: true + DisplayName string `url:"display_name" json:"display_name"` + + // Enabled status of the schedule rule + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // End date and time for the schedule rule + // Required: false + EndDateTime string `url:"end_date_time,omitempty" json:"end_date_time,omitempty"` + + // Insert up reference + // Required: false + InsertUp string `url:"insert_up,omitempty" json:"insert_up,omitempty"` + + // Locked at timestamp + // Required: false + LockedAt string `url:"locked_at,omitempty" json:"locked_at,omitempty"` + + // Schedule cron expression + // Required: false + ScheduleCron string `url:"schedule_cron,omitempty" json:"schedule_cron,omitempty"` + + // Start date and time for the schedule rule + // Required: false + StartDateTime string `url:"start_date_time,omitempty" json:"start_date_time,omitempty"` +} + +// Create creates a security policy +func (i SecurityPolicies) Create(ctx context.Context, req CreateRequest) (*SecurityPolicySummary, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/security_policy/create" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := SecurityPolicySummary{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/secpolicies/delete.go b/pkg/sdn/secpolicies/delete.go new file mode 100644 index 0000000..6bbdea3 --- /dev/null +++ b/pkg/sdn/secpolicies/delete.go @@ -0,0 +1,41 @@ +package secpolicies + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct to delete security policy +type DeleteRequest struct { + // Security policy ID + // Required: true + SecurityPolicyID string `url:"security_policy_id" json:"security_policy_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete + // Required: false + Force interface{} `url:"force,omitempty" json:"force,omitempty" validate:"omitempty,isBool"` +} + +// Delete a security policy +func (i SecurityPolicies) Delete(ctx context.Context, req DeleteRequest) (bool, error) { + err := validators.ValidateRequest(req) + if err != nil { + return false, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/security_policy/delete" + + _, err = i.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/sdn/secpolicies/filter.go b/pkg/sdn/secpolicies/filter.go new file mode 100644 index 0000000..28d22f9 --- /dev/null +++ b/pkg/sdn/secpolicies/filter.go @@ -0,0 +1,42 @@ +package secpolicies + +// FilterByID returns SecurityPolicyList with specified ID. +func (agl SecurityPolicyList) FilterByID(id string) SecurityPolicyList { + predicate := func(ia SecurityPolicySummary) bool { + return ia.ID == id + } + + return agl.FilterFunc(predicate) +} + +// FilterByName returns SecurityPolicyList with specified Name. +func (agl SecurityPolicyList) FilterByName(name string) SecurityPolicyList { + predicate := func(ia SecurityPolicySummary) bool { + return ia.DisplayName == name + } + + return agl.FilterFunc(predicate) +} + +// FilterFunc allows filtering SecurityPolicyList based on a user-specified predicate. +func (agl SecurityPolicyList) FilterFunc(predicate func(SecurityPolicySummary) bool) SecurityPolicyList { + var result SecurityPolicyList + + for _, acc := range agl { + if predicate(acc) { + result = append(result, acc) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (agl SecurityPolicyList) FindOne() SecurityPolicySummary { + if len(agl) == 0 { + return SecurityPolicySummary{} + } + + return agl[0] +} diff --git a/pkg/sdn/secpolicies/filter_test.go b/pkg/sdn/secpolicies/filter_test.go new file mode 100644 index 0000000..c4020ac --- /dev/null +++ b/pkg/sdn/secpolicies/filter_test.go @@ -0,0 +1,295 @@ +package secpolicies + +import ( + "testing" +) + +var testSecurityPolicies = SecurityPolicyList{ + { + ID: "policy1", + DisplayName: "DevelopersPolicy", + Description: "First policy", + CreatedAt: "2023-01-01", + UpdatedAt: "2023-01-10", + AccessGroupID: "group1", + AccessGroupName: "Developers", + AppliedToNetObjectGroupID: "netgroup1", + Enabled: true, + StartPriority: 1, + EndPriority: 100, + VersionID: 1, + Status: Status{ + Common: "active", + Hypervisors: []HypervisorStatus{ + { + Name: "hyp1", + DisplayName: "Hypervisor1", + Status: "synced", + HypervisorStatus: "healthy", + SyncedAt: "2023-01-10T10:00:00Z", + }, + }, + }, + SecurityRules: []SecurityRule{ + { + ID: "rule1", + DisplayName: "AllowHTTP", + Description: "Allow HTTP traffic", + Action: "Allow", + Direction: "Ingress", + Enabled: true, + Priority: 10, + SecurityPolicyID: "policy1", + LogEnabled: true, + LogSeverity: "medium", + StatisticsEnabled: true, + VersionID: 1, + Filter: Filter{ + Filters: map[string]interface{}{ + "protocol": "tcp", + "port": float64(80), + }, + }, + }, + }, + }, + { + ID: "policy2", + DisplayName: "AdminsPolicy", + Description: "Second policy", + CreatedAt: "2023-01-02", + UpdatedAt: "2023-01-11", + AccessGroupID: "group2", + AccessGroupName: "Admins", + AppliedToNetObjectGroupID: "netgroup2", + Enabled: false, + StartPriority: 101, + EndPriority: 200, + VersionID: 2, + Status: Status{ + Common: "inactive", + Hypervisors: []HypervisorStatus{ + { + Name: "hyp2", + DisplayName: "Hypervisor2", + Status: "pending", + HypervisorStatus: "syncing", + SyncedAt: "2023-01-11T10:00:00Z", + }, + }, + }, + SecurityRules: []SecurityRule{ + { + ID: "rule2", + DisplayName: "DenySSH", + Description: "Deny SSH traffic", + Action: "Deny", + Direction: "Ingress", + Enabled: true, + Priority: 20, + SecurityPolicyID: "policy2", + LogEnabled: false, + LogSeverity: "high", + StatisticsEnabled: false, + VersionID: 1, + Filter: Filter{ + Filters: map[string]interface{}{ + "protocol": "tcp", + "port": float64(22), + }, + }, + }, + }, + }, + { + ID: "policy3", + DisplayName: "UsersPolicy", + Description: "Third policy", + CreatedAt: "2023-01-03", + UpdatedAt: "2023-01-12", + AccessGroupID: "group3", + AccessGroupName: "Users", + AppliedToNetObjectGroupID: "netgroup3", + Enabled: true, + StartPriority: 201, + EndPriority: 300, + VersionID: 3, + Status: Status{ + Common: "active", + Hypervisors: []HypervisorStatus{ + { + Name: "hyp3", + DisplayName: "Hypervisor3", + Status: "synced", + HypervisorStatus: "healthy", + SyncedAt: "2023-01-12T10:00:00Z", + }, + }, + }, + SecurityRules: []SecurityRule{ + { + ID: "rule3", + DisplayName: "AllowHTTPS", + Description: "Allow HTTPS traffic", + Action: "Allow", + Direction: "Egress", + Enabled: true, + Priority: 30, + SecurityPolicyID: "policy3", + LogEnabled: true, + LogSeverity: "low", + StatisticsEnabled: true, + VersionID: 1, + Filter: Filter{ + Filters: map[string]interface{}{ + "protocol": "tcp", + "port": float64(443), + }, + }, + }, + }, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testSecurityPolicies.FilterByID("policy2").FindOne() + + if actual.ID != "policy2" { + t.Fatal("actual:", actual.ID, "> expected: policy2") + } +} + +func TestFilterByDisplayName(t *testing.T) { + actual := testSecurityPolicies.FilterByName("UsersPolicy").FindOne() + + if actual.DisplayName != "UsersPolicy" { + t.Fatal("actual:", actual.DisplayName, ">> expected: UsersPolicy") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicySummary) bool { + return sp.Description == "Second policy" + }) + + if len(actual) != 1 || actual[0].ID != "policy2" { + t.Fatal("Expected 1 policy with description 'Second policy', found:", len(actual)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testSecurityPolicies.FilterByID("policy1").FindOne() + if result.ID != "policy1" { + t.Fatal("Expected policy1, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := SecurityPolicyList{} + result := emptyList.FindOne() + + if result.ID != "" || result.DisplayName != "" { + t.Fatal("Expected empty SecurityPolicySummary, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := testSecurityPolicies.FilterByID("nonexistent") + + if len(actual) != 0 { + t.Fatal("Expected 0 policies, found:", len(actual)) + } +} + +func TestFilterByDisplayNameNotFound(t *testing.T) { + actual := testSecurityPolicies.FilterByName("Nonexistent Policy") + + if len(actual) != 0 { + t.Fatal("Expected 0 policies, found:", len(actual)) + } +} + +func TestFilterByEnabled(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicySummary) bool { + return sp.Enabled + }) + + if len(actual) != 2 { + t.Fatal("Expected 2 enabled policies, found:", len(actual)) + } +} + +func TestFilterByAccessGroup(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicySummary) bool { + return sp.AccessGroupName == "Developers" + }) + + if len(actual) != 1 || actual[0].ID != "policy1" { + t.Fatal("Expected 1 policy for Developers group, found:", len(actual)) + } +} + +func TestFilterByStatus(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicySummary) bool { + return sp.Status.Common == "active" + }) + + if len(actual) != 2 { + t.Fatal("Expected 2 active policies, found:", len(actual)) + } +} + +func TestFilterByPriorityRange(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicySummary) bool { + return sp.StartPriority >= 100 && sp.EndPriority <= 200 + }) + + if len(actual) != 1 || actual[0].ID != "policy2" { + t.Fatal("Expected 1 policy in priority range 100-200, found:", len(actual)) + } +} + +func TestFilterByRuleAction(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicySummary) bool { + for _, rule := range sp.SecurityRules { + if rule.Action == "Deny" { + return true + } + } + return false + }) + + if len(actual) != 1 || actual[0].ID != "policy2" { + t.Fatal("Expected 1 policy with Deny rule, found:", len(actual)) + } +} + +func TestFilterByRuleDirection(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicySummary) bool { + for _, rule := range sp.SecurityRules { + if rule.Direction == "Egress" { + return true + } + } + return false + }) + + if len(actual) != 1 || actual[0].ID != "policy3" { + t.Fatal("Expected 1 policy with Egress rule, found:", len(actual)) + } +} + +func TestFilterByLogEnabled(t *testing.T) { + actual := testSecurityPolicies.FilterFunc(func(sp SecurityPolicySummary) bool { + for _, rule := range sp.SecurityRules { + if rule.LogEnabled { + return true + } + } + return false + }) + + if len(actual) != 2 { + t.Fatal("Expected 2 policies with log enabled rules, found:", len(actual)) + } +} diff --git a/pkg/sdn/secpolicies/get.go b/pkg/sdn/secpolicies/get.go new file mode 100644 index 0000000..0501626 --- /dev/null +++ b/pkg/sdn/secpolicies/get.go @@ -0,0 +1,47 @@ +package secpolicies + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get information about security policy +type GetRequest struct { + // ID a security policy + // Required: true + ID string `url:"security_policy_id" json:"security_policy_id" validate:"required"` +} + +// Get gets security policy +func (a SecurityPolicies) Get(ctx context.Context, req GetRequest) (*SecurityPolicySummary, error) { + res, err := a.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := SecurityPolicySummary{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil + +} + +// GetRaw gets address pool details as an array of bytes +func (a SecurityPolicies) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/security_policies/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/secpolicies/ids.go b/pkg/sdn/secpolicies/ids.go new file mode 100644 index 0000000..3974611 --- /dev/null +++ b/pkg/sdn/secpolicies/ids.go @@ -0,0 +1,10 @@ +package secpolicies + +// IDs gets array of IDs from SecurityPolicyList struct +func (spl SecurityPolicyList) IDs() []string { + res := make([]string, 0, len(spl)) + for _, c := range spl { + res = append(res, c.ID) + } + return res +} diff --git a/pkg/sdn/secpolicies/list.go b/pkg/sdn/secpolicies/list.go new file mode 100644 index 0000000..c7a207b --- /dev/null +++ b/pkg/sdn/secpolicies/list.go @@ -0,0 +1,74 @@ +package secpolicies + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of security group +type ListRequest struct { + // Display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Enabled status + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Filter by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Filter by applied to net object group ID + // Required: false + AppliedToNetObjectGroupID string `url:"applied_to_net_object_group_id,omitempty" json:"applied_to_net_object_group_id,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, enabled, created_at, updated_at, deleted_at, start_priority) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` +} + +// List of security policies +func (i SecurityPolicies) List(ctx context.Context, req ListRequest) (SecurityPolicyList, error) { + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + result := []SecurityPolicySummary{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// ListRaw gets a list of all security policies as an array of bytes +func (a SecurityPolicies) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/security_policy/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/secpolicies/models.go b/pkg/sdn/secpolicies/models.go new file mode 100644 index 0000000..f746425 --- /dev/null +++ b/pkg/sdn/secpolicies/models.go @@ -0,0 +1,126 @@ +package secpolicies + +type SecurityPolicyList []SecurityPolicySummary + +// SecurityPolicySummary provides brief information about the security policy +type SecurityPolicySummary struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Applied to network object group ID + AppliedToNetObjectGroupID string `json:"applied_to_net_object_group_id"` + + // Created time + CreatedAt string `json:"created_at"` + + // Description + Description string `json:"description"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // End priority + EndPriority int `json:"end_priority"` + + // ID + ID string `json:"id"` + + // Security rules + SecurityRules []SecurityRule `json:"security_rules"` + + // Start priority + StartPriority int `json:"start_priority"` + + // Status information + Status Status `json:"status"` + + // Version ID + VersionID uint64 `json:"version_id"` + + // Updated time + UpdatedAt string `json:"updated_at"` +} + +// Status information +type Status struct { + // Common status + Common string `json:"common"` + + // Hypervisor statuses + Hypervisors []HypervisorStatus `json:"hypervisors"` +} + +// HypervisorStatus information +type HypervisorStatus struct { + // Status + Status string `json:"status"` + + // Name + Name string `json:"name"` + + // Display name + DisplayName string `json:"display_name"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` + + // Last sync time + SyncedAt string `json:"synced_at"` +} + +// Security rules +type SecurityRule struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Action to take (Allow, Deny, etc.) + Action string `json:"action"` + + // Description + Description string `json:"description"` + + // Traffic direction (Ingress, Egress) + Direction string `json:"direction"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // Filter criteria + Filter Filter `json:"filter"` + + // ID + ID string `json:"id"` + + // Log enabled flag + LogEnabled bool `json:"log_enabled"` + + // Log severity level + LogSeverity string `json:"log_severity"` + + // Priority + Priority int `json:"priority"` + + // Security policy ID + SecurityPolicyID string `json:"security_policy_id"` + + // Statistics enabled flag + StatisticsEnabled bool `json:"statistics_enabled"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Filter represents the filter criteria for the security rule +type Filter struct { + // Filters map + Filters map[string]interface{} `json:"filters"` +} diff --git a/pkg/sdn/secpolicies/move.go b/pkg/sdn/secpolicies/move.go new file mode 100644 index 0000000..d212a88 --- /dev/null +++ b/pkg/sdn/secpolicies/move.go @@ -0,0 +1,49 @@ +package secpolicies + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct to move security policy +type MoveRequest struct { + // Security policy ID + // Required: true + SecurityPolicyID string `url:"security_policy_id" json:"security_policy_id" validate:"required"` + + // Version ID + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Security policy ID + // Required: true + InsertUp string `url:"insert_up" json:"insert_up" validate:"required"` +} + +// Move a security policy +func (i SecurityPolicies) Move(ctx context.Context, req MoveRequest) (*SecurityPolicySummary, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/security_policy/move" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPatch, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := SecurityPolicySummary{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/secpolicies/rule.go b/pkg/sdn/secpolicies/rule.go new file mode 100644 index 0000000..f75189b --- /dev/null +++ b/pkg/sdn/secpolicies/rule.go @@ -0,0 +1,10 @@ +package secpolicies + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/secpolicies/rule" +) + +// Accessing the security policies rule method group +func (r *SecurityPolicies) Rule() *rule.Rule { + return rule.New(r.client) +} diff --git a/pkg/sdn/secpolicies/rule/get.go b/pkg/sdn/secpolicies/rule/get.go new file mode 100644 index 0000000..7c30dde --- /dev/null +++ b/pkg/sdn/secpolicies/rule/get.go @@ -0,0 +1,50 @@ +package rule + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetRequest struct to get a security rules +type GetRequest struct { + // Security policy ID + // Required: true + SecurityPolicyID string `url:"security_policy_id" json:"security_policy_id" validate:"required"` + + // Security rule ID + // Required: true + SecurityRuleID string `url:"security_rule_id" json:"security_rule_id" validate:"required"` +} + +// Get a security policies +func (i Rule) Get(ctx context.Context, req GetRequest) (*SecurityRule, error) { + res, err := i.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + result := SecurityRule{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return &result, nil +} + +// GetRaw gets a security rule as an array of bytes +func (a Rule) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/security_policy/rule/get" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/secpolicies/rule/ids.go b/pkg/sdn/secpolicies/rule/ids.go new file mode 100644 index 0000000..2f03559 --- /dev/null +++ b/pkg/sdn/secpolicies/rule/ids.go @@ -0,0 +1,10 @@ +package rule + +// IDs gets array of IDs from SecurityRulesList struct +func (srl SecurityRulesList) IDs() []string { + res := make([]string, 0, len(srl)) + for _, c := range srl { + res = append(res, c.ID) + } + return res +} diff --git a/pkg/sdn/secpolicies/rule/list.go b/pkg/sdn/secpolicies/rule/list.go new file mode 100644 index 0000000..3fcff50 --- /dev/null +++ b/pkg/sdn/secpolicies/rule/list.go @@ -0,0 +1,70 @@ +package rule + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// ListRequest struct to get a list of security rules +type ListRequest struct { + // Security policy ID + // Required: true + SecurityPolicyID string `url:"security_policy_id" json:"security_policy_id" validate:"required"` + + // Display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Enabled status + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, enabled, created_at, updated_at, deleted_at, start_priority) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` +} + +// List of security policies +func (i Rule) List(ctx context.Context, req ListRequest) (SecurityRulesList, error) { + res, err := i.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + result := []SecurityRule{} + + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// ListRaw gets a list of all security rules as an array of bytes +func (a Rule) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + if err := validators.ValidateRequest(req); err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/security_policy/rule/list" + + res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/secpolicies/rule/models.go b/pkg/sdn/secpolicies/rule/models.go new file mode 100644 index 0000000..4a73bc4 --- /dev/null +++ b/pkg/sdn/secpolicies/rule/models.go @@ -0,0 +1,54 @@ +package rule + +type SecurityRulesList []SecurityRule + +// SecurityRule +type SecurityRule struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Action to take (Allow, Deny, etc.) + Action string `json:"action"` + + // Description + Description string `json:"description"` + + // Traffic direction (Ingress, Egress) + Direction string `json:"direction"` + + // Display name + DisplayName string `json:"display_name"` + + // Enabled flag + Enabled bool `json:"enabled"` + + // Filter criteria + Filter Filter `json:"filter"` + + // ID + ID string `json:"id"` + + // Log enabled flag + LogEnabled bool `json:"log_enabled"` + + // Log severity level + LogSeverity string `json:"log_severity"` + + // Priority + Priority int `json:"priority"` + + // Security policy ID + SecurityPolicyID string `json:"security_policy_id"` + + // Statistics enabled flag + StatisticsEnabled bool `json:"statistics_enabled"` + + // Version ID + VersionID uint64 `json:"version_id"` +} + +// Filter represents the filter criteria for the security rule +type Filter struct { + // Filters map + Filters map[string]interface{} `json:"filters"` +} diff --git a/pkg/sdn/secpolicies/rule/rule.go b/pkg/sdn/secpolicies/rule/rule.go new file mode 100644 index 0000000..68a48e7 --- /dev/null +++ b/pkg/sdn/secpolicies/rule/rule.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN security policies rule +package rule + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to security policies rule +type Rule struct { + client interfaces.Caller +} + +// Builder for security policies rule endpoints +func New(client interfaces.Caller) *Rule { + return &Rule{ + client, + } +} diff --git a/pkg/sdn/secpolicies/rule/serialize.go b/pkg/sdn/secpolicies/rule/serialize.go new file mode 100644 index 0000000..9f30e9b --- /dev/null +++ b/pkg/sdn/secpolicies/rule/serialize.go @@ -0,0 +1,27 @@ +package rule + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 SecurityRulesList) Serialize(params ...string) (serialization.Serialized, error) { + if len(la) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(la, prefix, indent) + } + + return json.Marshal(la) +} diff --git a/pkg/sdn/secpolicies/security_policies.go b/pkg/sdn/secpolicies/security_policies.go new file mode 100644 index 0000000..f00d239 --- /dev/null +++ b/pkg/sdn/secpolicies/security_policies.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN secirity policies +package secpolicies + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to security policies +type SecurityPolicies struct { + client interfaces.Caller +} + +// Builder for adress pools endpoints +func New(client interfaces.Caller) *SecurityPolicies { + return &SecurityPolicies{ + client, + } +} diff --git a/pkg/sdn/secpolicies/serialize.go b/pkg/sdn/secpolicies/serialize.go new file mode 100644 index 0000000..a5cc63d --- /dev/null +++ b/pkg/sdn/secpolicies/serialize.go @@ -0,0 +1,27 @@ +package secpolicies + +import ( + "encoding/json" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/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 SecurityPolicyList) Serialize(params ...string) (serialization.Serialized, error) { + if len(la) == 0 { + return []byte{}, nil + } + + if len(params) > 1 { + prefix := params[0] + indent := params[1] + + return json.MarshalIndent(la, prefix, indent) + } + + return json.Marshal(la) +} diff --git a/pkg/sdn/secpolicies/update.go b/pkg/sdn/secpolicies/update.go new file mode 100644 index 0000000..ed1f8d6 --- /dev/null +++ b/pkg/sdn/secpolicies/update.go @@ -0,0 +1,80 @@ +package secpolicies + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct to update security policy +type UpdateRequest struct { + // ID a security policy + // Required: true + SecurityPolicyID string `url:"security_policy_id" json:"security_policy_id" validate:"required"` + + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Applied to net object group ID + // Required: true + AppliedToNetObjectGroupID string `url:"applied_to_net_object_group_id" json:"applied_to_net_object_group_id" validate:"required"` + + // Description of the schedule rule + // Required: true + Description string `url:"description" json:"description"` + + // Display name of the schedule rule + // Required: true + DisplayName string `url:"display_name" json:"display_name"` + + // Enabled status of the schedule rule + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // End date and time for the schedule rule + // Required: false + EndDateTime string `url:"end_date_time,omitempty" json:"end_date_time,omitempty"` + + // Insert up reference + // Required: false + InsertUp string `url:"insert_up,omitempty" json:"insert_up,omitempty"` + + // Locked at timestamp + // Required: false + LockedAt string `url:"locked_at,omitempty" json:"locked_at,omitempty"` + + // Schedule cron expression + // Required: false + ScheduleCron string `url:"schedule_cron,omitempty" json:"schedule_cron,omitempty"` + + // Start date and time for the schedule rule + // Required: false + StartDateTime string `url:"start_date_time,omitempty" json:"start_date_time,omitempty"` +} + +// Update updates a security policy +func (i SecurityPolicies) Update(ctx context.Context, req UpdateRequest) (*SecurityPolicySummary, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/security_policy/update" + + res, err := i.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := SecurityPolicySummary{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/security_policies.go b/pkg/sdn/security_policies.go new file mode 100644 index 0000000..b38680b --- /dev/null +++ b/pkg/sdn/security_policies.go @@ -0,0 +1,10 @@ +package sdn + +import ( + sp "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/secpolicies" +) + +// Accessing the SDN method group +func (sdn *SDN) SecurityPolicies() *sp.SecurityPolicies { + return sp.New(sdn.client) +} diff --git a/pkg/sdn/segments.go b/pkg/sdn/segments.go new file mode 100644 index 0000000..97be1f9 --- /dev/null +++ b/pkg/sdn/segments.go @@ -0,0 +1,10 @@ +package sdn + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/segments" +) + +// Accessing the Segments method group +func (sdn *SDN) Segments() *segments.Segments { + return segments.New(sdn.client) +} diff --git a/pkg/sdn/segments/create.go b/pkg/sdn/segments/create.go new file mode 100644 index 0000000..0366dfd --- /dev/null +++ b/pkg/sdn/segments/create.go @@ -0,0 +1,125 @@ +package segments + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// CreateRequest struct for creating segment +type CreateRequest struct { + // Identifier of the parent access group + // Required: false + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Detailed description of the segment + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // User-friendly name for the segment + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the network is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // IPv4 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV4 string `url:"subnet_v4,omitempty" json:"subnet_v4,omitempty"` + + // IPv6 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV6 string `url:"subnet_v6,omitempty" json:"subnet_v6,omitempty"` + + // DHCP IPv4 + // Required: false + DHCPv4 *DHCPv4ConfigRequest `url:"-" json:"dhcp_v4,omitempty"` + + // DHCP IPv6 + // Required: false + DHCPv6 *DHCPv6ConfigRequest `url:"-" json:"dhcp_v6,omitempty"` + + // Segment type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` +} + +type DHCPv4ConfigRequest struct { + // DNS + // Required: false + DNS []string `url:"dns,omitempty" json:"dns,omitempty"` + + // Excluded address ranges + // Required: false + ExcludedAddressRanges []string `url:"excluded_address_ranges,omitempty" json:"excluded_address_ranges,omitempty"` + + // Gateway + // Required: true + Gateway string `url:"gateway" json:"gateway" validate:"required"` + + // Lease time + // Required: false + LeaseTime uint64 `url:"lease_time,omitempty" json:"lease_time,omitempty"` + + // Server IP + // Required: true + ServerIP string `url:"server_ip" json:"server_ip" validate:"required"` + + // Server MAC + // Required: false + ServerMAC string `url:"server_mac,omitempty" json:"server_mac,omitempty"` + + // Whether the config is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` +} + +type DHCPv6ConfigRequest struct { + // Address prefix + // Required: true + AddressPrefix string `url:"address_prefix" json:"address_prefix" validate:"required"` + + // DNS + // Required: false + DNS []string `url:"dns,omitempty" json:"dns,omitempty"` + + // Lease time + // Required: true + LeaseTime uint64 `url:"lease_time,omitempty" json:"lease_time,omitempty"` + + // Server MAC + // Required: true + ServerMAC string `url:"server_mac,omitempty" json:"server_mac,omitempty"` + + // Whether the config is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` +} + +// Create creates segment +func (s Segments) Create(ctx context.Context, req CreateRequest) (*SegmentResponse, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/segment/create" + + res, err := s.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := SegmentResponse{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/segments/delete.go b/pkg/sdn/segments/delete.go new file mode 100644 index 0000000..4ee0091 --- /dev/null +++ b/pkg/sdn/segments/delete.go @@ -0,0 +1,42 @@ +package segments + +import ( + "context" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// DeleteRequest struct for delete segment +type DeleteRequest struct { + // ID of segment + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Force delete + // Required: false + Force bool `url:"force,omitempty" json:"force,omitempty"` +} + +// Delete delete an segment +func (s Segments) Delete(ctx context.Context, req DeleteRequest) error { + err := validators.ValidateRequest(req) + if err != nil { + return validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/segment/delete" + + _, err = s.client.DecortApiCallCtype(ctx, http.MethodDelete, url, constants.MIMEJSON, req) + + if err != nil { + return err + } + + return nil +} diff --git a/pkg/sdn/segments/filter.go b/pkg/sdn/segments/filter.go new file mode 100644 index 0000000..efe8fa7 --- /dev/null +++ b/pkg/sdn/segments/filter.go @@ -0,0 +1,60 @@ +package segments + +// FilterByID returns ListSegment with specified ID. +func (list ListSegment) FilterByID(id string) ListSegment { + predicate := func(segment SegmentResponse) bool { + return segment.ID == id + } + + return list.FilterFunc(predicate) +} + +// FilterByName returns ListSegment with specified display name. +func (list ListSegment) FilterByName(name string) ListSegment { + predicate := func(segment SegmentResponse) bool { + return segment.DisplayName == name + } + + return list.FilterFunc(predicate) +} + +// FilterBySubnetIPv4 returns ListSegment with specified subnet IPv4. +func (list ListSegment) FilterBySubnetIPv4(IPv4 string) ListSegment { + predicate := func(segment SegmentResponse) bool { + return segment.SubnetV4 == IPv4 + } + + return list.FilterFunc(predicate) +} + +// FilterBySubnetIPv6 returns ListSegment with specified subnet IPv4. +func (list ListSegment) FilterBySubnetIPv6(IPv6 string) ListSegment { + predicate := func(segment SegmentResponse) bool { + return segment.SubnetV6 == IPv6 + } + + return list.FilterFunc(predicate) +} + +// FilterFunc allows filtering ListSegment based on a user-specified predicate. +func (list ListSegment) FilterFunc(predicate func(response SegmentResponse) bool) ListSegment { + var result ListSegment + + for _, item := range list { + if predicate(item) { + result = append(result, item) + } + } + + return result +} + +// FindOne returns first element. +// If none was found, returns an empty struct. +func (list ListSegment) FindOne() SegmentResponse { + if len(list) == 0 { + return SegmentResponse{} + } + + return list[0] +} diff --git a/pkg/sdn/segments/filter_test.go b/pkg/sdn/segments/filter_test.go new file mode 100644 index 0000000..517c09e --- /dev/null +++ b/pkg/sdn/segments/filter_test.go @@ -0,0 +1,153 @@ +package segments + +import ( + "testing" + "time" +) + +var testSegmentList = ListSegment{ + { + AccessGroupID: "a1b2c3d4-1234-5678-9101-abcdef123456", + AccessGroupName: "default-access-group", + CreatedAt: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC), + Description: "Test1", + DHCPv4: DHCPv4Config{}, + DHCPv6: DHCPv6Config{}, + DisplayName: "Test1", + Enabled: true, + ID: "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + LogicalPortsInfo: []EntityInfo{}, + RoutersInfo: []EntityInfo{}, + Status: Status{}, + SubnetV4: "192.168.1.0/24", + SubnetV6: "2001:db8::/64", + UpdatedAt: time.Date(2024, 1, 20, 14, 45, 0, 0, time.UTC), + VersionID: 1, + }, + { + AccessGroupID: "b2c3d4e5-2345-6789-0123-bcdefa654321", + AccessGroupName: "restricted-group", + CreatedAt: time.Date(2024, 2, 1, 9, 0, 0, 0, time.UTC), + Description: "Test2", + DHCPv4: DHCPv4Config{}, + DHCPv6: DHCPv6Config{}, + DisplayName: "Test2", + Enabled: false, + ID: "c3d4e5f6-3456-7890-1234-cdefab098765", + LogicalPortsInfo: []EntityInfo{}, + RoutersInfo: []EntityInfo{}, + Status: Status{}, + SubnetV4: "10.0.0.0/24", + SubnetV6: "2001:db8:1::/64", + UpdatedAt: time.Date(2024, 2, 5, 16, 20, 0, 0, time.UTC), + VersionID: 2, + }, + { + AccessGroupID: "d4e5f6a7-4567-8901-2345-defabc123456", + AccessGroupName: "minimal-group", + CreatedAt: time.Date(2024, 3, 10, 12, 0, 0, 0, time.UTC), + Description: "Test3", + DHCPv4: DHCPv4Config{}, + DHCPv6: DHCPv6Config{}, + DisplayName: "Test3", + Enabled: true, + ID: "e5f6a7b8-5678-9012-3456-efabcd789012", + LogicalPortsInfo: []EntityInfo{}, + RoutersInfo: []EntityInfo{}, + Status: Status{}, + SubnetV4: "192.15.1.0/24", + SubnetV6: "", + UpdatedAt: time.Date(2024, 3, 10, 12, 0, 0, 0, time.UTC), + VersionID: 1, + }, +} + +func TestFilterByID(t *testing.T) { + actual := testSegmentList.FilterByID("a1b2c3d4-e5f6-7890-abcd-ef1234567890").FindOne() + + if actual.ID != "a1b2c3d4-e5f6-7890-abcd-ef1234567890" { + t.Fatal("actual:", actual.ID, "> expected: a1b2c3d4-e5f6-7890-abcd-ef1234567890") + } +} + +func TestFilterByName(t *testing.T) { + actual := testSegmentList.FilterByName("Test1").FindOne() + + if actual.DisplayName != "Test1" { + t.Fatal("actual:", actual.DisplayName, ">> expected: Test1") + } +} + +func TestFilterBySubnetIPv4(t *testing.T) { + actual := testSegmentList.FilterBySubnetIPv4("192.168.1.0/24").FindOne() + + if actual.SubnetV4 != "192.168.1.0/24" { + t.Fatal("actual:", actual.SubnetV4, ">> expected: 192.168.1.0/24") + } +} + +func TestFilterBySubnetIPv6(t *testing.T) { + actual := testSegmentList.FilterBySubnetIPv6("2001:db8::/64").FindOne() + + if actual.SubnetV6 != "2001:db8::/64" { + t.Fatal("actual:", actual.SubnetV6, ">> expected: 2001:db8::/64") + } +} + +func TestFilterFunc(t *testing.T) { + actual := testSegmentList.FilterFunc(func(response SegmentResponse) bool { + return response.DisplayName == "Test2" + }) + + if len(actual) != 1 || actual[0].ID != "c3d4e5f6-3456-7890-1234-cdefab098765" { + t.Fatal("Expected 1 extnet with name 'Test2', found:", len(actual)) + } +} + +func TestFindOneWithResults(t *testing.T) { + result := testSegmentList.FilterByID("c3d4e5f6-3456-7890-1234-cdefab098765").FindOne() + if result.ID != "c3d4e5f6-3456-7890-1234-cdefab098765" { + t.Fatal("Expected c3d4e5f6-3456-7890-1234-cdefab098765, got:", result.ID) + } +} + +func TestFindOneEmpty(t *testing.T) { + emptyList := ListSegment{} + result := emptyList.FindOne() + + if result.ID != "" || result.DisplayName != "" { + t.Fatal("Expected empty segment, got:", result) + } +} + +func TestFilterByIDNotFound(t *testing.T) { + actual := ListSegment{}.FilterByID("nonexistent") + + if len(actual) != 0 { + t.Fatal("Expected 0 extNet, found:", len(actual)) + } +} + +func TestFilterByNameNotFound(t *testing.T) { + actual := ListSegment{}.FilterByName("Nonexistent") + + if len(actual) != 0 { + t.Fatal("Expected 0 extNet, found:", len(actual)) + } +} + +func TestFilterBySegmentIPv4NotFound(t *testing.T) { + actual := testSegmentList.FilterBySubnetIPv4("nonexistent-ip") + + if len(actual) != 0 { + t.Fatal("Expected 0 extNet, found:", len(actual)) + } +} + +func TestFilterBySubnetIPv6NotFound(t *testing.T) { + actual := testSegmentList.FilterBySubnetIPv6("nonexistent-ipv6") + + if len(actual) != 0 { + t.Fatal("Expected 0 extNet, found:", len(actual)) + } +} diff --git a/pkg/sdn/segments/get.go b/pkg/sdn/segments/get.go new file mode 100644 index 0000000..215f3af --- /dev/null +++ b/pkg/sdn/segments/get.go @@ -0,0 +1,48 @@ +package segments + +import ( + "context" + "encoding/json" + "net/http" +) + +// GetRequest struct to get information about segment +type GetRequest struct { + // ID of segment + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` + + // ID of access group + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` +} + +// Get gets segment details as a SegmentResponse struct +func (s Segments) Get(ctx context.Context, req GetRequest) (*SegmentResponse, error) { + res, err := s.GetRaw(ctx, req) + if err != nil { + return nil, err + } + + info := SegmentResponse{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets segment details as an array of bytes +func (s Segments) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) { + + //if err := validators.ValidateRequest(req); err != nil { + // return nil, validators.ValidationErrors(validators.GetErrors(err)) + //} + + url := "/sdn/segment/get" + + res, err := s.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/segments/get_faa.go b/pkg/sdn/segments/get_faa.go new file mode 100644 index 0000000..8faa4cf --- /dev/null +++ b/pkg/sdn/segments/get_faa.go @@ -0,0 +1,46 @@ +package segments + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// GetFAARequest struct to get the floating/anycast IP address of a segment +type GetFAARequest struct { + // ID of segment + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` +} + +// GetFAA gets the floating/anycast IP address info for a segment +func (s Segments) GetFAA(ctx context.Context, req GetFAARequest) (*GetFAAResponse, error) { + res, err := s.GetFAARaw(ctx, req) + if err != nil { + return nil, err + } + + info := GetFAAResponse{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetFAARaw gets the floating/anycast IP address info for a segment as an array of bytes +func (s Segments) GetFAARaw(ctx context.Context, req GetFAARequest) ([]byte, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/segment/get_faa" + + res, err := s.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/segments/ids.go b/pkg/sdn/segments/ids.go new file mode 100644 index 0000000..25490c8 --- /dev/null +++ b/pkg/sdn/segments/ids.go @@ -0,0 +1,10 @@ +package segments + +// IDs gets array of IDs from ListSegment struct +func (list ListSegment) IDs() []string { + res := make([]string, 0, len(list)) + for _, item := range list { + res = append(res, item.ID) + } + return res +} diff --git a/pkg/sdn/segments/list.go b/pkg/sdn/segments/list.go new file mode 100644 index 0000000..feb01e8 --- /dev/null +++ b/pkg/sdn/segments/list.go @@ -0,0 +1,96 @@ +package segments + +import ( + "context" + "encoding/json" + "net/http" +) + +// ListRequest struct to get a list of segments +type ListRequest struct { + // Filter by access group ID + // Required: false + AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"` + + // Filter by display name + // Required: false + DisplayName string `url:"display_name,omitempty" json:"display_name,omitempty"` + + // Filter by IP version (v4 or v6) + // Required: false + Subnet string `url:"subnet,omitempty" json:"subnet,omitempty" validate:"omitempty,ipTypes"` + + // Filter by enabled status + // Required: false + Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"` + + // Does Core currently believe that its data is synchronized with the data in the OVN? + // Required: false + IsSynced interface{} `url:"is_synced,omitempty" json:"is_synced,omitempty" validate:"omitempty,isBool"` + + // Filter by update date from + // Required: false + UpdatedFrom string `url:"updated_from,omitempty" json:"updated_from,omitempty"` + + // Filter lby update date to + // Required: false + UpdatedTo string `url:"updated_to,omitempty" json:"updated_to,omitempty"` + + // Filter by create date from + // Required: false + CreatedFrom string `url:"created_from,omitempty" json:"created_from,omitempty"` + + // Filter lby create date to + // Required: false + CreatedTo string `url:"created_to,omitempty" json:"created_to,omitempty"` + + // Page number for pagination + // Required: false + Page uint64 `url:"page,omitempty" json:"page,omitempty"` + + // Number of results per page + // Required: false + PerPage uint64 `url:"per_page,omitempty" json:"per_page,omitempty"` + + // Field to sort by (display_name, created_at, updated_at, subnet) + // Required: false + SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"` + + // Sort order (asc/desc) + // Required: false + SortOrder string `url:"sort_order,omitempty" json:"sort_order,omitempty"` + + // Filter by operation status + // Required: false + OperationStatus string `url:"operation_status,omitempty" json:"operation_status,omitempty"` +} + +// List gets list of all available segments as a ListSegment struct +func (s Segments) List(ctx context.Context, req ListRequest) (ListSegment, error) { + res, err := s.ListRaw(ctx, req) + if err != nil { + return nil, err + } + + list := ListSegment{} + + err = json.Unmarshal(res, &list) + if err != nil { + return nil, err + } + + return list, nil +} + +// ListRaw gets list of all available segments as an array of bytes +func (s Segments) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { + + //if err := validators.ValidateRequest(req); err != nil { + // return nil, validators.ValidationErrors(validators.GetErrors(err)) + //} + + url := "/sdn/segment/list" + + res, err := s.client.DecortApiCall(ctx, http.MethodGet, url, req) + return res, err +} diff --git a/pkg/sdn/segments/models.go b/pkg/sdn/segments/models.go new file mode 100644 index 0000000..da55db8 --- /dev/null +++ b/pkg/sdn/segments/models.go @@ -0,0 +1,214 @@ +package segments + +import "time" + +// List segments +type ListSegment []SegmentResponse + +// Main information about network segment +type SegmentResponse struct { + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // Access group name + AccessGroupName string `json:"access_group_name"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // Detailed description of the segment + Description string `json:"description"` + + // DHCP IPv4 + DHCPv4 DHCPv4Config `json:"dhcp_v4"` + + // DHCP IPv6 + DHCPv6 DHCPv6Config `json:"dhcp_v6"` + + // User-friendly name for the segment + DisplayName string `json:"display_name"` + + // Whether the segment is enabled + Enabled bool `json:"enabled"` + + // ID of segment + ID string `json:"id"` + + // L2 connection port info + L2ConnectionPort *L2ConnectionPort `json:"l2_connection_port,omitempty"` + + // Logical ports info + LogicalPortsInfo []EntityInfo `json:"logical_ports_info"` + + // Routers info + RoutersInfo []EntityInfo `json:"routers_info"` + + // Status + Status Status `json:"status"` + + // IPv4 subnet in CIDR notation + SubnetV4 string `json:"subnet_v4"` + + // IPv6 subnet in CIDR notation + SubnetV6 string `json:"subnet_v6"` + + // Segment type + Type string `json:"type"` + + // Update time + UpdatedAt time.Time `json:"updated_at"` + + // ID of version + VersionID uint64 `json:"version_id"` +} + +type DHCPv4Config struct { + // DNS + DNS []string `json:"dns"` + + // Excluded address ranges + ExcludedAddressRanges []string `json:"excluded_address_ranges"` + + // Gateway + Gateway string `json:"gateway"` + + // ID of config + ID string `json:"id"` + + // Lease time + LeaseTime uint64 `json:"lease_time"` + + // Server IP + ServerIP string `json:"server_ip"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Whether the config is enabled + Enabled bool `json:"enabled"` +} + +type DHCPv6Config struct { + // Address prefix + AddressPrefix string `json:"address_prefix"` + + // DNS + DNS []string `json:"dns"` + + // ID of config + ID string `json:"id"` + + // Lease time + LeaseTime uint64 `json:"lease_time"` + + // Server MAC + ServerMAC string `json:"server_mac"` + + // Whether the config is enabled + Enabled bool `json:"enabled"` +} + +type EntityInfo struct { + // User-friendly name for entity + DisplayName string `json:"display_name"` + + // ID of entity + ID string `json:"id"` +} + +type Status struct { + // Operation status + OperationStatus string `json:"operation_status"` + + // Hypervisors status + Hypervisors []HypervisorStatus `json:"hypervisors"` +} + +type HypervisorStatus struct { + // Operation status of the hypervisor + OperationStatus string `json:"operation_status"` + + // Name of hypervisor + Name string `json:"name"` + + // User-friendly name for the hypervisor + DisplayName string `json:"display_name"` + + // Hypervisor status + HypervisorStatus string `json:"hypervisor_status"` + + // Synced time + SyncedAt time.Time `json:"synced_at"` +} + +// L2ConnectionPort holds information about the L2 connection port of a segment +type L2ConnectionPort struct { + // ID of the L2 connection port + ID string `json:"id"` + + // Access group ID + AccessGroupID string `json:"access_group_id"` + + // ID of version + VersionID uint64 `json:"version_id"` + + // L2 external network details + L2ExternalNetwork L2ExternalNetwork `json:"l2_external_network"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // ID of the user who created the port + CreatedBy string `json:"created_by"` + + // Updated time + UpdatedAt time.Time `json:"updated_at"` + + // ID of the user who last updated the port + UpdatedBy string `json:"updated_by"` +} + +// L2ExternalNetwork holds information about an L2 external network +type L2ExternalNetwork struct { + // ID of the network + ID string `json:"id"` + + // User-friendly name for the network + DisplayName string `json:"display_name"` + + // Detailed description of the network + Description string `json:"description"` + + // Bridge network name + BridgeNetworkName string `json:"bridge_network_name"` + + // List of hypervisor IDs attached to the network + Hypervisors []string `json:"hypervisors"` + + // VLAN tag + VLANTag uint64 `json:"vlan_tag"` + + // ID of version + VersionID uint64 `json:"version_id"` + + // Created time + CreatedAt time.Time `json:"created_at"` + + // ID of the user who created the network + CreatedBy string `json:"created_by"` + + // Updated time + UpdatedAt time.Time `json:"updated_at"` + + // ID of the user who last updated the network + UpdatedBy string `json:"updated_by"` +} + +// GetFAAResponse holds the floating/anycast IP address info for a segment +type GetFAAResponse struct { + // IPv4 address + IPv4Address string `json:"ipv4_address"` + + // IPv6 address + IPv6Address string `json:"ipv6_address"` +} diff --git a/pkg/sdn/segments/segments.go b/pkg/sdn/segments/segments.go new file mode 100644 index 0000000..8f7dea6 --- /dev/null +++ b/pkg/sdn/segments/segments.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN segments +package segments + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to segments +type Segments struct { + client interfaces.Caller +} + +// Builder for segments endpoints +func New(client interfaces.Caller) *Segments { + return &Segments{ + client, + } +} diff --git a/pkg/sdn/segments/update.go b/pkg/sdn/segments/update.go new file mode 100644 index 0000000..038aa23 --- /dev/null +++ b/pkg/sdn/segments/update.go @@ -0,0 +1,81 @@ +package segments + +import ( + "context" + "encoding/json" + "net/http" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/constants" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/internal/validators" +) + +// UpdateRequest struct for updating segment +type UpdateRequest struct { + // ID of segment + // Required: true + SegmentID string `url:"segment_id" json:"segment_id" validate:"required"` + + // ID of version + // Required: true + VersionID uint64 `url:"version_id" json:"version_id" validate:"required"` + + // Identifier of the parent access group + // Required: true + AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"` + + // Detailed description of the segment + // Required: true + Description string `url:"description" json:"description" validate:"required"` + + // User-friendly name for the segment + // Required: true + DisplayName string `url:"display_name" json:"display_name" validate:"required"` + + // Whether the network is enabled + // Required: true + Enabled bool `url:"enabled" json:"enabled"` + + // IPv4 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV4 string `url:"subnet_v4,omitempty" json:"subnet_v4,omitempty"` + + // IPv6 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified) + // Required: false + SubnetV6 string `url:"subnet_v6,omitempty" json:"subnet_v6,omitempty"` + + // DHCP IPv4 + // Required: false + DHCPv4 *DHCPv4ConfigRequest `url:"-" json:"dhcp_v4,omitempty"` + + // DHCP IPv6 + // Required: false + DHCPv6 *DHCPv6ConfigRequest `url:"-" json:"dhcp_v6,omitempty"` + + // Segment type + // Required: false + Type string `url:"type,omitempty" json:"type,omitempty"` +} + +// Update updates segment +func (s Segments) Update(ctx context.Context, req UpdateRequest) (*SegmentResponse, error) { + err := validators.ValidateRequest(req) + if err != nil { + return nil, validators.ValidationErrors(validators.GetErrors(err)) + } + + url := "/sdn/segment/update" + + res, err := s.client.DecortApiCallCtype(ctx, http.MethodPut, url, constants.MIMEJSON, req) + if err != nil { + return nil, err + } + + info := SegmentResponse{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/sdn/version.go b/pkg/sdn/version.go new file mode 100644 index 0000000..e3c46e0 --- /dev/null +++ b/pkg/sdn/version.go @@ -0,0 +1,10 @@ +package sdn + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn/version" +) + +// Accessing the SDN method group +func (sdn *SDN) Version() *version.Version { + return version.New(sdn.client) +} diff --git a/pkg/sdn/version/get.go b/pkg/sdn/version/get.go new file mode 100644 index 0000000..bc07e57 --- /dev/null +++ b/pkg/sdn/version/get.go @@ -0,0 +1,33 @@ +package version + +import ( + "context" + "encoding/json" + "net/http" +) + +// Get gets SDN version info as a RecordVersion struct +func (v Version) Get(ctx context.Context) (*RecordVersion, error) { + res, err := v.GetRaw(ctx) + if err != nil { + return nil, err + } + + info := RecordVersion{} + + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + return &info, nil +} + +// GetRaw gets SDN version info as an array of bytes +func (v Version) GetRaw(ctx context.Context) ([]byte, error) { + + url := "/sdn/version/get" + + res, err := v.client.DecortApiCall(ctx, http.MethodGet, url, nil) + return res, err +} diff --git a/pkg/sdn/version/models.go b/pkg/sdn/version/models.go new file mode 100644 index 0000000..6ca7cb0 --- /dev/null +++ b/pkg/sdn/version/models.go @@ -0,0 +1,25 @@ +package version + +// Version info of the SDN platform +type RecordVersion struct { + // Core component version info + Core ComponentVersion `json:"core"` + + // Director component version info + Director ComponentVersion `json:"director"` +} + +// Version info of a single component +type ComponentVersion struct { + // Branch name + Branch string `json:"branch"` + + // Build time + BuildTime string `json:"build_time"` + + // Commit hash + Commit string `json:"commit"` + + // Version string + Version string `json:"version"` +} diff --git a/pkg/sdn/version/version.go b/pkg/sdn/version/version.go new file mode 100644 index 0000000..e562791 --- /dev/null +++ b/pkg/sdn/version/version.go @@ -0,0 +1,18 @@ +// API Actor API for managing SDN version +package version + +import ( + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/interfaces" +) + +// Structure for creating request to version +type Version struct { + client interfaces.Caller +} + +// Builder for version endpoints +func New(client interfaces.Caller) *Version { + return &Version{ + client, + } +} diff --git a/samples/client/client.go b/samples/client/client.go new file mode 100644 index 0000000..8048596 --- /dev/null +++ b/samples/client/client.go @@ -0,0 +1,42 @@ +package client + +import ( + "context" + "fmt" + + "errors" + + decortsdk "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/compute" +) + +type Migrator interface { + Migrate(ctxOrigin context.Context, vmUUID, to uint64) (bool, error) +} + +type migrator struct { + cfg *Config + client decortsdk.ClientInterface +} + +func NewMigrator(cfg *Config, c decortsdk.ClientInterface) Migrator { + return &migrator{ + cfg: cfg, + client: c, + } +} + +func (m *migrator) Migrate(ctxOrigin context.Context, dxVMID, nodeID uint64) (bool, error) { + req := compute.MigrateRequest{ + ComputeID: dxVMID, + TargetNodeID: nodeID, + Force: false, + } + ctx, cancel := context.WithTimeout(ctxOrigin, m.cfg.QueryTimeout) + ok, err := m.client.CloudBroker().Compute().Migrate(ctx, req) + cancel() + if err != nil { + return false, errors.Join(err, fmt.Errorf("Migrate VM %d to Node %d", dxVMID, nodeID)) + } + return ok, nil +} diff --git a/samples/client/client_test.go b/samples/client/client_test.go new file mode 100644 index 0000000..f63a540 --- /dev/null +++ b/samples/client/client_test.go @@ -0,0 +1,76 @@ +package client_test + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + decortsdk "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker/compute" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/samples/client" +) + +// Пример юнит тестирования на моках +func TestClient(t *testing.T) { + ctrl := gomock.NewController(t) + // Создаем мок инстанс интерфейса Caller + mockCaller := decortsdk.NewMockCaller(ctrl) + // Создаем мок инстанс интерфейса DecortClient + mockClient := decortsdk.NewMockDecortClient(mockCaller) + + dxVMID := uint64(100500) + VMID := "vm-100500" + + listComputes := &compute.ListComputes{ + Data: []compute.ItemCompute{ + { + InfoCompute: compute.InfoCompute{ + ID: dxVMID, + ReferenceID: VMID, + }, + }, + }} + + b, err := json.Marshal(listComputes) + assert.NoError(t, err) + // Подготавливаем мок для вызова метода CloudBroker().Compute().List() + mockCaller.EXPECT().DecortApiCall(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Eq(compute.ListRequest{})).Return(b, nil).AnyTimes() + listComputesRet, err := mockClient.CloudBroker().Compute().List(context.Background(), compute.ListRequest{}) + assert.NoError(t, err) + + assert.Equal(t, listComputes, listComputesRet) + +} + +// Пример юнит тестирования на моках +func TestMigrator(t *testing.T) { + ctrl := gomock.NewController(t) + // Создаем мок инстанс интерфейса Caller + mockCaller := decortsdk.NewMockCaller(ctrl) + // Создаем мок инстанс интерфейса interfaces.ClientInterface + mockClient := decortsdk.NewMockDecortClient(mockCaller) + + // Передаем мок инстанс интерфейса interfaces.ClientInterface в конструктор Migrator + migrator := client.NewMigrator(&client.Config{QueryTimeout: time.Second}, mockClient) + + b, err := json.Marshal(true) + assert.NoError(t, err) + + dxVMID := uint64(100500) + nodeID := uint64(100501) + + // Записываем поведение клиента + mockCaller.EXPECT().DecortApiCall(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Eq(compute.AsyncWrapperMigrateRequest{ + MigrateRequest: compute.MigrateRequest{ + ComputeID: dxVMID, + TargetNodeID: nodeID, + }, + SyncMode: true})).Return(b, nil).AnyTimes() + + ok, err := migrator.Migrate(context.Background(), dxVMID, nodeID) + assert.NoError(t, err) + assert.True(t, ok) +} diff --git a/samples/client/config.go b/samples/client/config.go new file mode 100644 index 0000000..f41270e --- /dev/null +++ b/samples/client/config.go @@ -0,0 +1,7 @@ +package client + +import "time" + +type Config struct { + QueryTimeout time.Duration +} 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..c4ef413 --- /dev/null +++ b/universal-client.go @@ -0,0 +1,39 @@ +package decortsdk + +import ( + "fmt" + "reflect" + + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/config" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudapi" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/cloudbroker" + "repository.basistech.ru/BASIS/dynamix-golang-sdk/v15/pkg/sdn" +) + +type ClientInterface interface { + CloudAPI() *cloudapi.CloudAPI + CloudBroker() *cloudbroker.CloudBroker + SDN() *sdn.SDN +} + +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 +}