This commit is contained in:
2024-12-27 12:00:59 +03:00
parent de8857b1d5
commit dc39a6412e
370 changed files with 34359 additions and 612 deletions

View File

@@ -0,0 +1,71 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
Tim Tkachev, <tvtkachev@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package extnet
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceExtnetReservedIpRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
reservedList, err := utilityExtnetReservedIpCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenExtnetReservedIp(reservedList))
return nil
}
func DataSourceExtnetReservedIp() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceExtnetReservedIpRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceExtnetReservedIpSchemaMake(),
}
}

View File

@@ -133,7 +133,6 @@ func flattenRecordExtnetResource(d *schema.ResourceData, recNet *extnet.RecordEx
d.Set("routes", flattenStaticRouteList(staticRouteList))
}
func flattenExtnetExcluded(ers extnet.ListReservations) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, er := range ers {
@@ -157,6 +156,7 @@ func flattenExtnetReservations(ers extnet.ListReservations) []map[string]interfa
res := make([]map[string]interface{}, 0)
for _, er := range ers {
temp := map[string]interface{}{
"account_id": er.AccountID,
"client_type": er.ClientType,
"domain_name": er.DomainName,
"hostname": er.Hostname,
@@ -217,4 +217,30 @@ func flattenStaticRouteData(d *schema.ResourceData, route *extnet.ItemRoutes) {
d.Set("netmask", route.Netmask)
d.Set("compute_ids", route.ComputeIds)
d.Set("route_id", route.ID)
}
}
func flattenExtnetReservedIp(el []extnet.RecordReservedIP) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(el))
for _, e := range el {
reservations := make([]map[string]interface{}, 0, len(e.Reservations))
for _, r := range e.Reservations {
temp := map[string]interface{}{
"account_id": r.AccountID,
"client_type": r.ClientType,
"domain_name": r.DomainName,
"hostname": r.Hostname,
"ip": r.IP,
"mac": r.Mac,
"type": r.Type,
"vm_id": r.VMID,
}
reservations = append(reservations, temp)
}
item := map[string]interface{}{
"extnet_id": e.ExtnetID,
"reservations": reservations,
}
res = append(res, item)
}
return res
}

View File

@@ -54,6 +54,9 @@ func resourceExtnetCreate(ctx context.Context, d *schema.ResourceData, m interfa
if err := ic.ExistGID(ctx, uint64(d.Get("gid").(int)), c); err != nil {
return diag.FromErr(err)
}
if err := checkReserveIp(ctx, d, c); err != nil {
return diag.FromErr(err)
}
req := extnet.CreateRequest{
Name: d.Get("name").(string),
@@ -191,6 +194,34 @@ func resourceExtnetCreate(ctx context.Context, d *schema.ResourceData, m interfa
}
}
// for reserve IP extnet must be enabled
if d.Get("reserved_ip").(*schema.Set).Len() > 0 {
for _, reservedIP := range d.Get("reserved_ip").(*schema.Set).List() {
reservedIPMap := reservedIP.(map[string]interface{})
req := extnet.AddReserveIPRequest{
AccountID: uint64(reservedIPMap["account_id"].(int)),
ExtNetID: netID,
}
if ipCount, ok := reservedIPMap["ip_count"]; ok {
req.IPCount = uint64(ipCount.(int))
}
if reservedIPMap["ips"].(*schema.Set).Len() > 0 {
ips := reservedIPMap["ips"].(*schema.Set).List()
for i, ip := range ips {
if i >= int(req.IPCount) {
break
}
req.IPs = append(req.IPs, ip.(string))
}
}
_, err := c.CloudBroker().ExtNet().AddReserveIP(ctx, req)
if err != nil {
w.Add(err)
}
}
}
return resourceExtnetRead(ctx, d, m)
}
@@ -215,6 +246,10 @@ func resourceExtnetUpdate(ctx context.Context, d *schema.ResourceData, m interfa
log.Debugf("cloudbroker: resourceExtnetUpdate called with id %s", d.Id())
c := m.(*controller.ControllerCfg)
if err := checkReserveIp(ctx, d, c); err != nil {
return diag.FromErr(err)
}
recNet, err := utilityExtnetCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
@@ -267,6 +302,12 @@ func resourceExtnetUpdate(ctx context.Context, d *schema.ResourceData, m interfa
}
}
if d.HasChange("reserved_ip") {
if err := reservedIPsUpdate(ctx, d, c, recNet); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange("shared_with") {
if err := handleSharedWithUpdate(ctx, d, c); err != nil {
return diag.FromErr(err)
@@ -330,6 +371,8 @@ func ResourceExtnetCB() *schema.Resource {
Default: &constants.Timeout300s,
},
CustomizeDiff: validateReserveIPs,
Schema: resourceExtnetSchemaMake(),
}
}

View File

@@ -1,6 +1,9 @@
package extnet
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func dataSourceExtnetDefaultSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
@@ -474,6 +477,10 @@ func dataSourceExtnetSchemaMake() map[string]*schema.Schema {
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"client_type": {
Type: schema.TypeString,
Computed: true,
@@ -728,6 +735,30 @@ func resourceExtnetSchemaMake() map[string]*schema.Schema {
Type: schema.TypeInt,
},
},
"reserved_ip": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
},
"ip_count": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(1, 255),
},
"ips": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"ckey": {
Type: schema.TypeString,
Computed: true,
@@ -868,6 +899,10 @@ func resourceExtnetSchemaMake() map[string]*schema.Schema {
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"client_type": {
Type: schema.TypeString,
Computed: true,
@@ -905,3 +940,69 @@ func resourceExtnetSchemaMake() map[string]*schema.Schema {
},
}
}
func dataSourceExtnetReservedIpSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
},
"extnet_id": {
Type: schema.TypeInt,
Optional: true,
},
"items": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"extnet_id": {
Type: schema.TypeInt,
Computed: true,
},
"reservations": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"client_type": {
Type: schema.TypeString,
Computed: true,
},
"domain_name": {
Type: schema.TypeString,
Computed: true,
},
"hostname": {
Type: schema.TypeString,
Computed: true,
},
"ip": {
Type: schema.TypeString,
Computed: true,
},
"mac": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"vm_id": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
},
},
},
}
return res
}

View File

@@ -0,0 +1,61 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package extnet
import (
"context"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/extnet"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
)
func utilityExtnetReservedIpCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) ([]extnet.RecordReservedIP, error) {
c := m.(*controller.ControllerCfg)
req := extnet.GetReservedIP{
AccountID: uint64(d.Get("account_id").(int)),
}
if extNetID, ok := d.GetOk("extnet_id"); ok {
req.ExtNetID = uint64(extNetID.(int))
}
log.Debugf("utilityExtnetReservedIpCheckPresence")
res, err := c.CloudBroker().ExtNet().GetReservedIP(ctx, req)
if err != nil {
return nil, err
}
return res, nil
}

View File

@@ -35,6 +35,8 @@ package extnet
import (
"context"
"errors"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
log "github.com/sirupsen/logrus"
@@ -341,3 +343,255 @@ func handleMigrateUpdate(ctx context.Context, d *schema.ResourceData, c *control
return nil
}
func checkReserveIp(ctx context.Context, d *schema.ResourceData, c *controller.ControllerCfg) error {
var err error
if d.Get("reserved_ip").(*schema.Set).Len() > 0 {
reservedIPList := d.Get("reserved_ip").(*schema.Set).List()
accountMap := make(map[int]struct{}, len(reservedIPList))
for _, reservedIP := range reservedIPList {
reservedIPMap := reservedIP.(map[string]interface{})
accountId := reservedIPMap["account_id"].(int)
if _, ok := accountMap[accountId]; ok {
err = errors.Join(err, fmt.Errorf("checkReserveIp: you must have only one block with id %d", accountId))
}
accountMap[accountId] = struct{}{}
_, okCount := reservedIPMap["ip_count"]
if !okCount && reservedIPMap["ips"].(*schema.Set).Len() == 0 {
err = errors.Join(err, fmt.Errorf("checkReserveIp: either ip_count or set of ips must be specified"))
}
existErr := ic.ExistAccount(ctx, uint64(accountId), c)
if existErr != nil {
err = errors.Join(err, existErr)
}
}
}
return err
}
func reservedIPsUpdate(ctx context.Context, d *schema.ResourceData, c *controller.ControllerCfg, recNet *extnet.RecordExtNet) error {
addSet, delSet, err := differenceIPReserved(ctx, d, c)
if err != nil {
return err
}
if len(delSet) > 0 {
for _, del := range delSet {
delMap := del.(map[string]interface{})
log.Debugf("reservedIPsUpdate: removing reserved IPs for account %d", delMap["account_id"].(int))
req := extnet.DelReserveIPRequest{
AccountID: uint64(delMap["account_id"].(int)),
ExtNetID: recNet.ID,
}
if ipCount, ok := delMap["ip_count"]; ok {
req.IPCount = uint64(ipCount.(int))
}
if delIPs, ok := delMap["ips"]; ok {
ips := delIPs.(*schema.Set).List()
for _, ip := range ips {
req.IPs = append(req.IPs, ip.(string))
}
}
_, err := c.CloudBroker().ExtNet().DelReserveIP(ctx, req)
if err != nil {
return err
}
}
}
if len(addSet) > 0 {
for _, add := range addSet {
addMap := add.(map[string]interface{})
log.Debugf("reservedIPsUpdate: add reserved IPs for account %d", addMap["account_id"].(int))
req := extnet.AddReserveIPRequest{
AccountID: uint64(addMap["account_id"].(int)),
ExtNetID: recNet.ID,
}
if ipCount, ok := addMap["ip_count"]; ok {
req.IPCount = uint64(ipCount.(int))
}
if addIPs, ok := addMap["ips"]; ok {
ips := addIPs.(*schema.Set).List()
for _, ip := range ips {
req.IPs = append(req.IPs, ip.(string))
}
}
_, err := c.CloudBroker().ExtNet().AddReserveIP(ctx, req)
if err != nil {
return err
}
}
}
return nil
}
func differenceIPReserved(ctx context.Context, d *schema.ResourceData, c *controller.ControllerCfg) (addList, delList []interface{}, errs error) {
addList = make([]interface{}, 0)
delList = make([]interface{}, 0)
oldSet, newSet := d.GetChange("reserved_ip")
oldList := oldSet.(*schema.Set).List()
newList := newSet.(*schema.Set).List()
for _, oldReservedIP := range oldList {
oldMap := oldReservedIP.(map[string]interface{})
found := false
for _, newReservedIP := range newList {
newMap := newReservedIP.(map[string]interface{})
if newMap["account_id"] == oldMap["account_id"] && newMap["ip_count"] == oldMap["ip_count"] && newMap["ips"] == oldMap["ips"] {
found = true
break
}
if newMap["account_id"] == oldMap["account_id"] {
add := make(map[string]interface{}, 0)
del := make(map[string]interface{}, 0)
delSet := oldMap["ips"].(*schema.Set).Difference(newMap["ips"].(*schema.Set))
addSet := newMap["ips"].(*schema.Set).Difference(oldMap["ips"].(*schema.Set))
if delSet.Len() > 0 {
del["account_id"] = oldMap["account_id"].(int)
del["ips"] = delSet
if oldMap["ip_count"].(int)-newMap["ip_count"].(int) >= delSet.Len() {
del["ip_count"] = oldMap["ip_count"].(int) - newMap["ip_count"].(int)
} else {
del["ip_count"] = delSet.Len()
newMap["ip_count"] = newMap["ip_count"].(int) + delSet.Len()
}
} else if newMap["ip_count"].(int) < oldMap["ip_count"].(int) {
del["account_id"] = oldMap["account_id"].(int)
del["ip_count"] = oldMap["ip_count"].(int) - newMap["ip_count"].(int)
}
if addSet.Len() > 0 {
add["account_id"] = oldMap["account_id"].(int)
add["ips"] = addSet
add["ip_count"] = newMap["ip_count"].(int) - oldMap["ip_count"].(int)
if add["ip_count"].(int) < 0 {
add["ip_count"] = 0
}
if add["ip_count"].(int)-addSet.Len() < 0 {
del["account_id"] = oldMap["account_id"].(int)
if _, ok := del["ip_count"]; ok {
del["ip_count"] = del["ip_count"].(int) + addSet.Len()
} else {
del["ip_count"] = addSet.Len()
}
}
} else if newMap["ip_count"].(int)-oldMap["ip_count"].(int) > 0 {
add["account_id"] = oldMap["account_id"].(int)
add["ip_count"] = newMap["ip_count"].(int) - oldMap["ip_count"].(int)
}
if _, ok := add["account_id"]; ok {
addList = append(addList, add)
}
if _, ok := del["account_id"]; ok {
ipsLen := 0
if _, ok := del["ips"]; ok {
ipsLen = del["ips"].(*schema.Set).Len()
}
freeCount := del["ip_count"].(int) - ipsLen
if freeCount > 0 {
req := extnet.GetReservedIP{
AccountID: uint64(del["account_id"].(int)),
ExtNetID: uint64(d.Get("extnet_id").(int)),
}
resIpsList, err := c.CloudBroker().ExtNet().GetReservedIP(ctx, req)
if err != nil {
errs = errors.Join(errs, err)
}
freeIPs := getFreeIps(resIpsList[0].Reservations, newMap["ips"], del["ips"])
if _, ok := del["ips"]; !ok {
del["ips"] = schema.NewSet(schema.HashString, []interface{}{})
}
for i := 0; i < freeCount; i++ {
del["ips"].(*schema.Set).Add(freeIPs[i])
}
}
delList = append(delList, del)
}
found = true
break
}
}
if found {
continue
}
delList = append(delList, oldReservedIP)
}
for _, newReservedIP := range newList {
newMap := newReservedIP.(map[string]interface{})
found := false
for _, oldReservedIP := range oldList {
oldMap := oldReservedIP.(map[string]interface{})
if newMap["account_id"] == oldMap["account_id"] {
found = true
break
}
}
if found {
continue
}
addList = append(addList, newReservedIP)
}
if errs != nil {
d.Set("reserved_ip", oldSet)
}
return
}
// getFreeIps returns array IPs which can be deleted
func getFreeIps(reserved []extnet.Reservations, newIps, delIps interface{}) []string {
newIpsList := make([]interface{}, 0)
delIpsList := make([]interface{}, 0)
if newIps != nil {
newIpsList = newIps.(*schema.Set).List()
}
if delIps != nil {
delIpsList = delIps.(*schema.Set).List()
}
newIpsMap := make(map[string]struct{}, len(newIpsList))
delIpsMap := make(map[string]struct{}, len(delIpsList))
freeIPs := make([]string, 0)
for _, ip := range newIpsList {
newIpsMap[ip.(string)] = struct{}{}
}
for _, ip := range delIpsList {
delIpsMap[ip.(string)] = struct{}{}
}
for _, item := range reserved {
if _, ok := newIpsMap[item.IP]; ok {
continue
}
if _, ok := delIpsMap[item.IP]; ok {
continue
}
freeIPs = append(freeIPs, item.IP)
}
return freeIPs
}
func validateReserveIPs(ctx context.Context, d *schema.ResourceDiff, m interface{}) error {
list := d.Get("reserved_ip").(*schema.Set).List()
var errs error
for _, reservedIP := range list {
reservedIPMap := reservedIP.(map[string]interface{})
var countIP, ipsLen int
if _, ok := reservedIPMap["ip_count"]; ok {
countIP = reservedIPMap["ip_count"].(int)
}
if _, ok := reservedIPMap["ips"]; ok {
ipsLen = reservedIPMap["ips"].(*schema.Set).Len()
}
if ipsLen > countIP {
errs = errors.Join(errs, fmt.Errorf("for the reserved_ip block with account_id %d the count parameter must be greater than or equal to len the ips array", reservedIPMap["account_id"].(int)))
}
}
return errs
}