mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-08-24 20:38:34 +00:00
add other zone types
Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
21059a6aa4
commit
bf19edb12d
191
fwprovider/cluster/sdn/zone/resource_evpn.go
Normal file
191
fwprovider/cluster/sdn/zone/resource_evpn.go
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package zone
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/config"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/sdn/zones"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.ResourceWithConfigure = &EVPNResource{}
|
||||
_ resource.ResourceWithImportState = &EVPNResource{}
|
||||
)
|
||||
|
||||
type EVPNResource struct {
|
||||
client *zones.Client
|
||||
}
|
||||
|
||||
func NewEVPNResource() resource.Resource {
|
||||
return &EVPNResource{}
|
||||
}
|
||||
|
||||
func (r *EVPNResource) Metadata(
|
||||
_ context.Context,
|
||||
req resource.MetadataRequest,
|
||||
resp *resource.MetadataResponse,
|
||||
) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sdn_zone_evpn"
|
||||
}
|
||||
|
||||
func (r *EVPNResource) Configure(
|
||||
_ context.Context,
|
||||
req resource.ConfigureRequest,
|
||||
resp *resource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg, ok := req.ProviderData.(config.Resource)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf(
|
||||
"Expected config.Resource, got: %T",
|
||||
req.ProviderData,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
r.client = cfg.Client.Cluster().SDNZones()
|
||||
}
|
||||
|
||||
func (r *EVPNResource) Create(
|
||||
ctx context.Context,
|
||||
req resource.CreateRequest,
|
||||
resp *resource.CreateResponse,
|
||||
) {
|
||||
var plan evpnModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
reqData := plan.toAPIRequestBody(ctx, &resp.Diagnostics)
|
||||
reqData.Type = ptr.Ptr(zones.TypeEVPN)
|
||||
|
||||
if err := r.client.CreateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Create SDN EVPN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *EVPNResource) Read(
|
||||
ctx context.Context,
|
||||
req resource.ReadRequest,
|
||||
resp *resource.ReadResponse,
|
||||
) {
|
||||
var state evpnModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
zone, err := r.client.GetZone(ctx, state.ID.ValueString())
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Read SDN EVPN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
readModel := &evpnModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
||||
|
||||
func (r *EVPNResource) Update(
|
||||
ctx context.Context,
|
||||
req resource.UpdateRequest,
|
||||
resp *resource.UpdateResponse,
|
||||
) {
|
||||
var plan evpnModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
reqData := plan.toAPIRequestBody(ctx, &resp.Diagnostics)
|
||||
|
||||
if err := r.client.UpdateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Update SDN EVPN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *EVPNResource) Delete(
|
||||
ctx context.Context,
|
||||
req resource.DeleteRequest,
|
||||
resp *resource.DeleteResponse,
|
||||
) {
|
||||
var state evpnModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.client.DeleteZone(ctx, state.ID.ValueString()); err != nil &&
|
||||
!errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Delete SDN EVPN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *EVPNResource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
zone, err := r.client.GetZone(ctx, req.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Zone %s does not exist", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Unable to Import SDN EVPN Zone %s", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
readModel := &evpnModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
@ -14,6 +14,8 @@ import (
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/sdn/zones"
|
||||
|
||||
proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
||||
)
|
||||
|
||||
type baseModel struct {
|
||||
@ -51,18 +53,6 @@ func (m *baseModel) importFromAPI(name string, data *zones.ZoneData, diags *diag
|
||||
m.MTU = types.Int64PointerValue(data.MTU)
|
||||
m.Nodes = stringset.NewValueString(data.Nodes, diags, stringset.WithSeparator(","))
|
||||
m.ReverseDNS = types.StringPointerValue(data.ReverseDNS)
|
||||
// m.Bridge = types.StringPointerValue(data.Bridge)
|
||||
// m.ServiceVLAN = types.Int64PointerValue(data.ServiceVLAN)
|
||||
// m.ServiceVLANProtocol = types.StringPointerValue(data.ServiceVLANProtocol)
|
||||
// m.Peers = stringset.NewValueString(data.Peers, diags, comaSeparated)
|
||||
// m.Controller = types.StringPointerValue(data.Controller)
|
||||
// m.ExitNodes = stringset.NewValueString(data.ExitNodes, diags, comaSeparated)
|
||||
// m.PrimaryExitNode = types.StringPointerValue(data.ExitNodesPrimary)
|
||||
// m.RouteTargetImport = types.StringPointerValue(data.RouteTargetImport)
|
||||
// m.VRFVXLANID = types.Int64PointerValue(data.VRFVXLANID)
|
||||
// m.ExitNodesLocalRouting = types.BoolPointerValue(ptrConversion.Int64ToBoolPtr(data.ExitNodesLocalRouting))
|
||||
// m.AdvertiseSubnets = types.BoolPointerValue(ptrConversion.Int64ToBoolPtr(data.AdvertiseSubnets))
|
||||
// m.DisableARPNDSuppression = types.BoolPointerValue(ptrConversion.Int64ToBoolPtr(data.DisableARPNDSuppression))
|
||||
}
|
||||
|
||||
func (m *baseModel) toAPIRequestBody(ctx context.Context, diags *diag.Diagnostics) *zones.ZoneRequestData {
|
||||
@ -76,18 +66,113 @@ func (m *baseModel) toAPIRequestBody(ctx context.Context, diags *diag.Diagnostic
|
||||
data.DNSZone = m.DNSZone.ValueStringPointer()
|
||||
data.Nodes = m.Nodes.ValueStringPointer(ctx, diags, stringset.WithSeparator(","))
|
||||
data.MTU = m.MTU.ValueInt64Pointer()
|
||||
// data.Bridge = m.Bridge.ValueStringPointer()
|
||||
// data.ServiceVLAN = m.ServiceVLAN.ValueInt64Pointer()
|
||||
// data.ServiceVLANProtocol = m.ServiceVLANProtocol.ValueStringPointer()
|
||||
// data.Peers = m.Peers.ValueStringPointer(ctx, diags, comaSeparated)
|
||||
// data.Controller = m.Controller.ValueStringPointer()
|
||||
// data.ExitNodes = m.ExitNodes.ValueStringPointer(ctx, diags, comaSeparated)
|
||||
// data.ExitNodesPrimary = m.PrimaryExitNode.ValueStringPointer()
|
||||
// data.RouteTargetImport = m.RouteTargetImport.ValueStringPointer()
|
||||
// data.VRFVXLANID = m.VRFVXLANID.ValueInt64Pointer()
|
||||
// data.ExitNodesLocalRouting = ptrConversion.BoolToInt64Ptr(m.ExitNodesLocalRouting.ValueBoolPointer())
|
||||
// data.AdvertiseSubnets = ptrConversion.BoolToInt64Ptr(m.AdvertiseSubnets.ValueBoolPointer())
|
||||
// data.DisableARPNDSuppression = ptrConversion.BoolToInt64Ptr(m.DisableARPNDSuppression.ValueBoolPointer())
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
type simpleModel struct {
|
||||
baseModel
|
||||
}
|
||||
|
||||
type vlanModel struct {
|
||||
baseModel
|
||||
|
||||
Bridge types.String `tfsdk:"bridge"`
|
||||
}
|
||||
|
||||
func (m *vlanModel) importFromAPI(name string, data *zones.ZoneData, diags *diag.Diagnostics) {
|
||||
m.baseModel.importFromAPI(name, data, diags)
|
||||
|
||||
m.Bridge = types.StringPointerValue(data.Bridge)
|
||||
}
|
||||
|
||||
func (m *vlanModel) toAPIRequestBody(ctx context.Context, diags *diag.Diagnostics) *zones.ZoneRequestData {
|
||||
data := m.baseModel.toAPIRequestBody(ctx, diags)
|
||||
|
||||
data.Bridge = m.Bridge.ValueStringPointer()
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
type qinqModel struct {
|
||||
vlanModel
|
||||
|
||||
ServiceVLAN types.Int64 `tfsdk:"service_vlan"`
|
||||
ServiceVLANProtocol types.String `tfsdk:"service_vlan_protocol"`
|
||||
}
|
||||
|
||||
func (m *qinqModel) importFromAPI(name string, data *zones.ZoneData, diags *diag.Diagnostics) {
|
||||
m.vlanModel.importFromAPI(name, data, diags)
|
||||
|
||||
m.ServiceVLAN = types.Int64PointerValue(data.ServiceVLAN)
|
||||
m.ServiceVLANProtocol = types.StringPointerValue(data.ServiceVLANProtocol)
|
||||
}
|
||||
|
||||
func (m *qinqModel) toAPIRequestBody(ctx context.Context, diags *diag.Diagnostics) *zones.ZoneRequestData {
|
||||
data := m.vlanModel.toAPIRequestBody(ctx, diags)
|
||||
|
||||
data.ServiceVLAN = m.ServiceVLAN.ValueInt64Pointer()
|
||||
data.ServiceVLANProtocol = m.ServiceVLANProtocol.ValueStringPointer()
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
type vxlanModel struct {
|
||||
baseModel
|
||||
|
||||
Peers stringset.Value `tfsdk:"peers"`
|
||||
}
|
||||
|
||||
func (m *vxlanModel) importFromAPI(name string, data *zones.ZoneData, diags *diag.Diagnostics) {
|
||||
m.baseModel.importFromAPI(name, data, diags)
|
||||
m.Peers = stringset.NewValueString(data.Peers, diags, stringset.WithSeparator(","))
|
||||
}
|
||||
|
||||
func (m *vxlanModel) toAPIRequestBody(ctx context.Context, diags *diag.Diagnostics) *zones.ZoneRequestData {
|
||||
data := m.baseModel.toAPIRequestBody(ctx, diags)
|
||||
|
||||
data.Peers = m.Peers.ValueStringPointer(ctx, diags, stringset.WithSeparator(","))
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
type evpnModel struct {
|
||||
baseModel
|
||||
|
||||
AdvertiseSubnets types.Bool `tfsdk:"advertise_subnets"`
|
||||
Controller types.String `tfsdk:"controller"`
|
||||
DisableARPNDSuppression types.Bool `tfsdk:"disable_arp_nd_suppression"`
|
||||
ExitNodes stringset.Value `tfsdk:"exit_nodes"`
|
||||
ExitNodesLocalRouting types.Bool `tfsdk:"exit_nodes_local_routing"`
|
||||
PrimaryExitNode types.String `tfsdk:"primary_exit_node"`
|
||||
RouteTargetImport types.String `tfsdk:"rt_import"`
|
||||
VRFVXLANID types.Int64 `tfsdk:"vrf_vxlan"`
|
||||
}
|
||||
|
||||
func (m *evpnModel) importFromAPI(name string, data *zones.ZoneData, diags *diag.Diagnostics) {
|
||||
m.baseModel.importFromAPI(name, data, diags)
|
||||
|
||||
m.AdvertiseSubnets = types.BoolPointerValue(data.AdvertiseSubnets.PointerBool())
|
||||
m.Controller = types.StringPointerValue(data.Controller)
|
||||
m.DisableARPNDSuppression = types.BoolPointerValue(data.DisableARPNDSuppression.PointerBool())
|
||||
m.ExitNodes = stringset.NewValueString(data.ExitNodes, diags, stringset.WithSeparator(","))
|
||||
m.ExitNodesLocalRouting = types.BoolPointerValue(data.ExitNodesLocalRouting.PointerBool())
|
||||
m.PrimaryExitNode = types.StringPointerValue(data.ExitNodesPrimary)
|
||||
m.RouteTargetImport = types.StringPointerValue(data.RouteTargetImport)
|
||||
m.VRFVXLANID = types.Int64PointerValue(data.VRFVXLANID)
|
||||
}
|
||||
|
||||
func (m *evpnModel) toAPIRequestBody(ctx context.Context, diags *diag.Diagnostics) *zones.ZoneRequestData {
|
||||
data := m.baseModel.toAPIRequestBody(ctx, diags)
|
||||
|
||||
data.AdvertiseSubnets = proxmoxtypes.CustomBoolPtr(m.AdvertiseSubnets.ValueBoolPointer())
|
||||
data.Controller = m.Controller.ValueStringPointer()
|
||||
data.DisableARPNDSuppression = proxmoxtypes.CustomBoolPtr(m.DisableARPNDSuppression.ValueBoolPointer())
|
||||
data.ExitNodes = m.ExitNodes.ValueStringPointer(ctx, diags, stringset.WithSeparator(","))
|
||||
data.ExitNodesLocalRouting = proxmoxtypes.CustomBoolPtr(m.ExitNodesLocalRouting.ValueBoolPointer())
|
||||
data.ExitNodesPrimary = m.PrimaryExitNode.ValueStringPointer()
|
||||
data.RouteTargetImport = m.RouteTargetImport.ValueStringPointer()
|
||||
data.VRFVXLANID = m.VRFVXLANID.ValueInt64Pointer()
|
||||
|
||||
return data
|
||||
}
|
||||
|
191
fwprovider/cluster/sdn/zone/resource_qinq.go
Normal file
191
fwprovider/cluster/sdn/zone/resource_qinq.go
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package zone
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/config"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/sdn/zones"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.ResourceWithConfigure = &QinQResource{}
|
||||
_ resource.ResourceWithImportState = &QinQResource{}
|
||||
)
|
||||
|
||||
type QinQResource struct {
|
||||
client *zones.Client
|
||||
}
|
||||
|
||||
func NewQinQResource() resource.Resource {
|
||||
return &QinQResource{}
|
||||
}
|
||||
|
||||
func (r *QinQResource) Metadata(
|
||||
_ context.Context,
|
||||
req resource.MetadataRequest,
|
||||
resp *resource.MetadataResponse,
|
||||
) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sdn_zone_qinq"
|
||||
}
|
||||
|
||||
func (r *QinQResource) Configure(
|
||||
_ context.Context,
|
||||
req resource.ConfigureRequest,
|
||||
resp *resource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg, ok := req.ProviderData.(config.Resource)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf(
|
||||
"Expected config.Resource, got: %T",
|
||||
req.ProviderData,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
r.client = cfg.Client.Cluster().SDNZones()
|
||||
}
|
||||
|
||||
func (r *QinQResource) Create(
|
||||
ctx context.Context,
|
||||
req resource.CreateRequest,
|
||||
resp *resource.CreateResponse,
|
||||
) {
|
||||
var plan qinqModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
reqData := plan.toAPIRequestBody(ctx, &resp.Diagnostics)
|
||||
reqData.Type = ptr.Ptr(zones.TypeQinQ)
|
||||
|
||||
if err := r.client.CreateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Create SDN QinQ Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *QinQResource) Read(
|
||||
ctx context.Context,
|
||||
req resource.ReadRequest,
|
||||
resp *resource.ReadResponse,
|
||||
) {
|
||||
var state qinqModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
zone, err := r.client.GetZone(ctx, state.ID.ValueString())
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Read SDN QinQ Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
readModel := &qinqModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
||||
|
||||
func (r *QinQResource) Update(
|
||||
ctx context.Context,
|
||||
req resource.UpdateRequest,
|
||||
resp *resource.UpdateResponse,
|
||||
) {
|
||||
var plan qinqModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
reqData := plan.toAPIRequestBody(ctx, &resp.Diagnostics)
|
||||
|
||||
if err := r.client.UpdateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Update SDN QinQ Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *QinQResource) Delete(
|
||||
ctx context.Context,
|
||||
req resource.DeleteRequest,
|
||||
resp *resource.DeleteResponse,
|
||||
) {
|
||||
var state qinqModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.client.DeleteZone(ctx, state.ID.ValueString()); err != nil &&
|
||||
!errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Delete SDN QinQ Zone",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *QinQResource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
zone, err := r.client.GetZone(ctx, req.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Zone %s does not exist", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Unable to Import SDN QinQ Zone %s", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
readModel := &qinqModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
65
fwprovider/cluster/sdn/zone/resource_qinq_test.go
Normal file
65
fwprovider/cluster/sdn/zone/resource_qinq_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
//go:build acceptance || all
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package zone_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/test"
|
||||
)
|
||||
|
||||
func TestAccResourceSDNZoneQinQ(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
te := test.InitEnvironment(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
steps []resource.TestStep
|
||||
}{
|
||||
{"create and update QinQ zone", []resource.TestStep{{
|
||||
Config: te.RenderConfig(`
|
||||
resource "proxmox_virtual_environment_sdn_zone_qinq" "zone_qinq" {
|
||||
id = "zoneQ"
|
||||
nodes = ["pve"]
|
||||
mtu = 1496
|
||||
bridge = "vmbr0"
|
||||
service_vlan = 100
|
||||
service_vlan_protocol = "802.1ad"
|
||||
}
|
||||
`),
|
||||
}, {
|
||||
Config: te.RenderConfig(`
|
||||
resource "proxmox_virtual_environment_sdn_zone_qinq" "zone_qinq" {
|
||||
id = "zoneQ"
|
||||
nodes = ["pve"]
|
||||
mtu = 1495
|
||||
bridge = "vmbr0"
|
||||
service_vlan = 200
|
||||
service_vlan_protocol = "802.1q"
|
||||
}
|
||||
`),
|
||||
ResourceName: "proxmox_virtual_environment_sdn_zone_qinq.zone_qinq",
|
||||
ImportStateId: "zoneQ",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
}}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: te.AccProviders,
|
||||
Steps: tt.steps,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -22,16 +22,16 @@ import (
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
|
||||
)
|
||||
|
||||
func commonAttributes(base ...map[string]schema.Attribute) map[string]schema.Attribute {
|
||||
if len(base) > 1 {
|
||||
panic("commonAttributes expects at most one base map")
|
||||
func baseAttributesWith(extraAttributes ...map[string]schema.Attribute) map[string]schema.Attribute {
|
||||
if len(extraAttributes) > 1 {
|
||||
panic("baseAttributesWith expects at most one extraAttributes map")
|
||||
}
|
||||
|
||||
if len(base) == 0 {
|
||||
base = append(base, make(map[string]schema.Attribute))
|
||||
if len(extraAttributes) == 0 {
|
||||
extraAttributes = append(extraAttributes, make(map[string]schema.Attribute))
|
||||
}
|
||||
|
||||
maps.Copy(base[0], map[string]schema.Attribute{
|
||||
maps.Copy(extraAttributes[0], map[string]schema.Attribute{
|
||||
"dns": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "DNS API server address.",
|
||||
@ -72,7 +72,7 @@ func commonAttributes(base ...map[string]schema.Attribute) map[string]schema.Att
|
||||
},
|
||||
})
|
||||
|
||||
return base[0]
|
||||
return extraAttributes[0]
|
||||
}
|
||||
|
||||
func (r *SimpleResource) Schema(
|
||||
@ -85,11 +85,11 @@ func (r *SimpleResource) Schema(
|
||||
MarkdownDescription: "Simple Zone in Proxmox SDN. It will create an isolated VNet bridge. " +
|
||||
"This bridge is not linked to a physical interface, and VM traffic is only local on each the node. " +
|
||||
"It can be used in NAT or routed setups.",
|
||||
Attributes: commonAttributes(),
|
||||
Attributes: baseAttributesWith(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *VLAN) Schema(
|
||||
func (r *VLANResource) Schema(
|
||||
_ context.Context,
|
||||
_ resource.SchemaRequest,
|
||||
resp *resource.SchemaResponse,
|
||||
@ -99,7 +99,7 @@ func (r *VLAN) Schema(
|
||||
MarkdownDescription: "VLAN Zone in Proxmox SDN. It uses an existing local Linux or OVS bridge to connect to the " +
|
||||
"node's physical interface. It uses VLAN tagging defined in the VNet to isolate the network segments. " +
|
||||
"This allows connectivity of VMs between different nodes.",
|
||||
Attributes: commonAttributes(map[string]schema.Attribute{
|
||||
Attributes: baseAttributesWith(map[string]schema.Attribute{
|
||||
"bridge": schema.StringAttribute{
|
||||
Description: "Bridge interface for VLAN.",
|
||||
MarkdownDescription: "The local bridge or OVS switch, already configured on _each_ node that allows " +
|
||||
@ -110,7 +110,7 @@ func (r *VLAN) Schema(
|
||||
}
|
||||
}
|
||||
|
||||
func (r *QinQ) Schema(
|
||||
func (r *QinQResource) Schema(
|
||||
_ context.Context,
|
||||
_ resource.SchemaRequest,
|
||||
resp *resource.SchemaResponse,
|
||||
@ -122,7 +122,7 @@ func (r *QinQ) Schema(
|
||||
"VLAN tag is defined by the VNet. Your physical network switches must support stacked VLANs for this " +
|
||||
"configuration. Due to the double stacking of tags, you need 4 more bytes for QinQ VLANs. " +
|
||||
"For example, you must reduce the MTU to 1496 if you physical interface MTU is 1500.",
|
||||
Attributes: commonAttributes(map[string]schema.Attribute{
|
||||
Attributes: baseAttributesWith(map[string]schema.Attribute{
|
||||
"bridge": schema.StringAttribute{
|
||||
Description: "A local, VLAN-aware bridge that is already configured on each local node",
|
||||
Optional: true,
|
||||
@ -145,7 +145,7 @@ func (r *QinQ) Schema(
|
||||
}
|
||||
}
|
||||
|
||||
func (r *VXLAN) Schema(
|
||||
func (r *VXLANResource) Schema(
|
||||
_ context.Context,
|
||||
_ resource.SchemaRequest,
|
||||
resp *resource.SchemaResponse,
|
||||
@ -157,7 +157,7 @@ func (r *VXLAN) Schema(
|
||||
"destination port 4789. You have to configure the underlay network yourself to enable UDP connectivity " +
|
||||
"between all peers. Because VXLAN encapsulation uses 50 bytes, the MTU needs to be 50 bytes lower than the " +
|
||||
"outgoing physical interface.",
|
||||
Attributes: commonAttributes(map[string]schema.Attribute{
|
||||
Attributes: baseAttributesWith(map[string]schema.Attribute{
|
||||
"peers": stringset.ResourceAttribute(
|
||||
"A list of IP addresses of each node in the VXLAN zone.",
|
||||
"A list of IP addresses of each node in the VXLAN zone. "+
|
||||
@ -168,7 +168,7 @@ func (r *VXLAN) Schema(
|
||||
}
|
||||
}
|
||||
|
||||
func (r *EVPN) Schema(
|
||||
func (r *EVPNResource) Schema(
|
||||
_ context.Context,
|
||||
_ resource.SchemaRequest,
|
||||
resp *resource.SchemaResponse,
|
||||
@ -177,7 +177,7 @@ func (r *EVPN) Schema(
|
||||
Description: "EVPN Zone in Proxmox SDN.",
|
||||
MarkdownDescription: "EVPN Zone in Proxmox SDN. The EVPN zone creates a routable Layer 3 network, capable of " +
|
||||
"spanning across multiple clusters.",
|
||||
Attributes: commonAttributes(map[string]schema.Attribute{
|
||||
Attributes: baseAttributesWith(map[string]schema.Attribute{
|
||||
"advertise_subnets": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Description: "Enable subnet advertisement for EVPN.",
|
||||
@ -202,6 +202,12 @@ func (r *EVPN) Schema(
|
||||
"rt_import": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "Route target import for EVPN.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.RegexMatches(
|
||||
regexp.MustCompile(`^(\d+):(\d+)$`),
|
||||
"must be in the format '<ASN>:<number>' (e.g., '65000:65000')",
|
||||
),
|
||||
},
|
||||
},
|
||||
"vrf_vxlan": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
|
@ -28,12 +28,10 @@ type SimpleResource struct {
|
||||
client *zones.Client
|
||||
}
|
||||
|
||||
// NewSimpleResource creates a new instance of the Simple resource.
|
||||
func NewSimpleResource() resource.Resource {
|
||||
return &SimpleResource{}
|
||||
}
|
||||
|
||||
// Metadata defines the name of the resource.
|
||||
func (r *SimpleResource) Metadata(
|
||||
_ context.Context,
|
||||
req resource.MetadataRequest,
|
||||
@ -72,7 +70,7 @@ func (r *SimpleResource) Create(
|
||||
req resource.CreateRequest,
|
||||
resp *resource.CreateResponse,
|
||||
) {
|
||||
var plan baseModel
|
||||
var plan simpleModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
@ -85,7 +83,7 @@ func (r *SimpleResource) Create(
|
||||
|
||||
if err := r.client.CreateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Create SDN Zone",
|
||||
"Unable to Create SDN SimpleZone",
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
@ -100,7 +98,7 @@ func (r *SimpleResource) Read(
|
||||
req resource.ReadRequest,
|
||||
resp *resource.ReadResponse,
|
||||
) {
|
||||
var state baseModel
|
||||
var state simpleModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
@ -116,7 +114,7 @@ func (r *SimpleResource) Read(
|
||||
}
|
||||
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Read SDN Zone",
|
||||
"Unable to Read SDN SimpleZone",
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
@ -133,7 +131,7 @@ func (r *SimpleResource) Update(
|
||||
req resource.UpdateRequest,
|
||||
resp *resource.UpdateResponse,
|
||||
) {
|
||||
var plan baseModel
|
||||
var plan simpleModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
@ -145,7 +143,7 @@ func (r *SimpleResource) Update(
|
||||
|
||||
if err := r.client.UpdateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Update SDN Zone",
|
||||
"Unable to Update SDN Simple Zone",
|
||||
err.Error(),
|
||||
)
|
||||
|
||||
@ -160,7 +158,7 @@ func (r *SimpleResource) Delete(
|
||||
req resource.DeleteRequest,
|
||||
resp *resource.DeleteResponse,
|
||||
) {
|
||||
var state baseModel
|
||||
var state simpleModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
@ -171,7 +169,7 @@ func (r *SimpleResource) Delete(
|
||||
if err := r.client.DeleteZone(ctx, state.ID.ValueString()); err != nil &&
|
||||
!errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Delete SDN Zone",
|
||||
"Unable to Delete SDN Simple Zone",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
@ -182,4 +180,20 @@ func (r *SimpleResource) ImportState(
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
zone, err := r.client.GetZone(ctx, req.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Zone %s does not exist", req.ID), err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Unable to Import SDN Simple Zone %s", req.ID), err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
readModel := &simpleModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ import (
|
||||
)
|
||||
|
||||
func TestAccResourceSDNZoneSimple(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
te := test.InitEnvironment(t)
|
||||
|
||||
tests := []struct {
|
||||
@ -39,6 +41,10 @@ func TestAccResourceSDNZoneSimple(t *testing.T) {
|
||||
mtu = 1495
|
||||
}
|
||||
`),
|
||||
ResourceName: "proxmox_virtual_environment_sdn_zone_simple.zone_simple",
|
||||
ImportStateId: "zoneS",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
}}},
|
||||
}
|
||||
|
||||
|
191
fwprovider/cluster/sdn/zone/resource_vlan.go
Normal file
191
fwprovider/cluster/sdn/zone/resource_vlan.go
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package zone
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/config"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/sdn/zones"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.ResourceWithConfigure = &VLANResource{}
|
||||
_ resource.ResourceWithImportState = &VLANResource{}
|
||||
)
|
||||
|
||||
type VLANResource struct {
|
||||
client *zones.Client
|
||||
}
|
||||
|
||||
func NewVLANResource() resource.Resource {
|
||||
return &VLANResource{}
|
||||
}
|
||||
|
||||
func (r *VLANResource) Metadata(
|
||||
_ context.Context,
|
||||
req resource.MetadataRequest,
|
||||
resp *resource.MetadataResponse,
|
||||
) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sdn_zone_vlan"
|
||||
}
|
||||
|
||||
func (r *VLANResource) Configure(
|
||||
_ context.Context,
|
||||
req resource.ConfigureRequest,
|
||||
resp *resource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg, ok := req.ProviderData.(config.Resource)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf(
|
||||
"Expected config.Resource, got: %T",
|
||||
req.ProviderData,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
r.client = cfg.Client.Cluster().SDNZones()
|
||||
}
|
||||
|
||||
func (r *VLANResource) Create(
|
||||
ctx context.Context,
|
||||
req resource.CreateRequest,
|
||||
resp *resource.CreateResponse,
|
||||
) {
|
||||
var plan vlanModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
reqData := plan.toAPIRequestBody(ctx, &resp.Diagnostics)
|
||||
reqData.Type = ptr.Ptr(zones.TypeVLAN)
|
||||
|
||||
if err := r.client.CreateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Create SDN VLAN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *VLANResource) Read(
|
||||
ctx context.Context,
|
||||
req resource.ReadRequest,
|
||||
resp *resource.ReadResponse,
|
||||
) {
|
||||
var state vlanModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
zone, err := r.client.GetZone(ctx, state.ID.ValueString())
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Read SDN VLAN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
readModel := &vlanModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
||||
|
||||
func (r *VLANResource) Update(
|
||||
ctx context.Context,
|
||||
req resource.UpdateRequest,
|
||||
resp *resource.UpdateResponse,
|
||||
) {
|
||||
var plan vlanModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
reqData := plan.toAPIRequestBody(ctx, &resp.Diagnostics)
|
||||
|
||||
if err := r.client.UpdateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Update SDN VLAN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *VLANResource) Delete(
|
||||
ctx context.Context,
|
||||
req resource.DeleteRequest,
|
||||
resp *resource.DeleteResponse,
|
||||
) {
|
||||
var state vlanModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.client.DeleteZone(ctx, state.ID.ValueString()); err != nil &&
|
||||
!errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Delete SDN VLAN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *VLANResource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
zone, err := r.client.GetZone(ctx, req.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Zone %s does not exist", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Unable to Import SDN VLAN Zone %s", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
readModel := &vlanModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
61
fwprovider/cluster/sdn/zone/resource_vlan_test.go
Normal file
61
fwprovider/cluster/sdn/zone/resource_vlan_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
//go:build acceptance || all
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package zone_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/test"
|
||||
)
|
||||
|
||||
func TestAccResourceSDNZoneVLAN(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
te := test.InitEnvironment(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
steps []resource.TestStep
|
||||
}{
|
||||
{"create and update VLAN zone", []resource.TestStep{{
|
||||
Config: te.RenderConfig(`
|
||||
resource "proxmox_virtual_environment_sdn_zone_vlan" "zone_vlan" {
|
||||
id = "zoneV"
|
||||
nodes = ["pve"]
|
||||
mtu = 1496
|
||||
bridge = "vmbr0"
|
||||
}
|
||||
`),
|
||||
}, {
|
||||
Config: te.RenderConfig(`
|
||||
resource "proxmox_virtual_environment_sdn_zone_vlan" "zone_vlan" {
|
||||
id = "zoneV"
|
||||
nodes = ["pve"]
|
||||
mtu = 1495
|
||||
bridge = "vmbr0"
|
||||
}
|
||||
`),
|
||||
ResourceName: "proxmox_virtual_environment_sdn_zone_vlan.zone_vlan",
|
||||
ImportStateId: "zoneV",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
}}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: te.AccProviders,
|
||||
Steps: tt.steps,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
191
fwprovider/cluster/sdn/zone/resource_vxlan.go
Normal file
191
fwprovider/cluster/sdn/zone/resource_vxlan.go
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package zone
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/config"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/sdn/zones"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.ResourceWithConfigure = &VXLANResource{}
|
||||
_ resource.ResourceWithImportState = &VXLANResource{}
|
||||
)
|
||||
|
||||
type VXLANResource struct {
|
||||
client *zones.Client
|
||||
}
|
||||
|
||||
func NewVXLANResource() resource.Resource {
|
||||
return &VXLANResource{}
|
||||
}
|
||||
|
||||
func (r *VXLANResource) Metadata(
|
||||
_ context.Context,
|
||||
req resource.MetadataRequest,
|
||||
resp *resource.MetadataResponse,
|
||||
) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sdn_zone_vxlan"
|
||||
}
|
||||
|
||||
func (r *VXLANResource) Configure(
|
||||
_ context.Context,
|
||||
req resource.ConfigureRequest,
|
||||
resp *resource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg, ok := req.ProviderData.(config.Resource)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf(
|
||||
"Expected config.Resource, got: %T",
|
||||
req.ProviderData,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
r.client = cfg.Client.Cluster().SDNZones()
|
||||
}
|
||||
|
||||
func (r *VXLANResource) Create(
|
||||
ctx context.Context,
|
||||
req resource.CreateRequest,
|
||||
resp *resource.CreateResponse,
|
||||
) {
|
||||
var plan vxlanModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
reqData := plan.toAPIRequestBody(ctx, &resp.Diagnostics)
|
||||
reqData.Type = ptr.Ptr(zones.TypeVXLAN)
|
||||
|
||||
if err := r.client.CreateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Create SDN VXLAN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *VXLANResource) Read(
|
||||
ctx context.Context,
|
||||
req resource.ReadRequest,
|
||||
resp *resource.ReadResponse,
|
||||
) {
|
||||
var state vxlanModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
zone, err := r.client.GetZone(ctx, state.ID.ValueString())
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Read SDN VXLAN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
readModel := &vxlanModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
||||
|
||||
func (r *VXLANResource) Update(
|
||||
ctx context.Context,
|
||||
req resource.UpdateRequest,
|
||||
resp *resource.UpdateResponse,
|
||||
) {
|
||||
var plan vxlanModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
reqData := plan.toAPIRequestBody(ctx, &resp.Diagnostics)
|
||||
|
||||
if err := r.client.UpdateZone(ctx, reqData); err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Update SDN VXLAN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *VXLANResource) Delete(
|
||||
ctx context.Context,
|
||||
req resource.DeleteRequest,
|
||||
resp *resource.DeleteResponse,
|
||||
) {
|
||||
var state vxlanModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.client.DeleteZone(ctx, state.ID.ValueString()); err != nil &&
|
||||
!errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to Delete SDN VXLAN Zone",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *VXLANResource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
zone, err := r.client.GetZone(ctx, req.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrResourceDoesNotExist) {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Zone %s does not exist", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Unable to Import SDN VXLAN Zone %s", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
readModel := &vxlanModel{}
|
||||
readModel.importFromAPI(zone.ID, zone, &resp.Diagnostics)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, readModel)...)
|
||||
}
|
61
fwprovider/cluster/sdn/zone/resource_vxlan_test.go
Normal file
61
fwprovider/cluster/sdn/zone/resource_vxlan_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
//go:build acceptance || all
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package zone_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/fwprovider/test"
|
||||
)
|
||||
|
||||
func TestAccResourceSDNZoneVXLAN(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
te := test.InitEnvironment(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
steps []resource.TestStep
|
||||
}{
|
||||
{"create and update VXLAN zone", []resource.TestStep{{
|
||||
Config: te.RenderConfig(`
|
||||
resource "proxmox_virtual_environment_sdn_zone_vxlan" "zone_vxlan" {
|
||||
id = "zoneX"
|
||||
nodes = ["pve"]
|
||||
mtu = 1450
|
||||
peers = ["10.0.0.1", "10.0.0.2"]
|
||||
}
|
||||
`),
|
||||
}, {
|
||||
Config: te.RenderConfig(`
|
||||
resource "proxmox_virtual_environment_sdn_zone_vxlan" "zone_vxlan" {
|
||||
id = "zoneX"
|
||||
nodes = ["pve"]
|
||||
mtu = 1440
|
||||
peers = ["10.0.0.3", "10.0.0.4"]
|
||||
}
|
||||
`),
|
||||
ResourceName: "proxmox_virtual_environment_sdn_zone_vxlan.zone_vxlan",
|
||||
ImportStateId: "zoneX",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
}}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: te.AccProviders,
|
||||
Steps: tt.steps,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -352,7 +352,9 @@ func validateResponseCode(res *http.Response) error {
|
||||
errList = append(errList, split...)
|
||||
}
|
||||
|
||||
msg = fmt.Sprintf("%s (%s)", msg, strings.Join(errList, " - "))
|
||||
if len(errList) > 0 {
|
||||
msg = fmt.Sprintf("%s (%s)", msg, strings.Join(errList, " - "))
|
||||
}
|
||||
}
|
||||
|
||||
httpError := &HTTPError{
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
package zones
|
||||
|
||||
import "github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
||||
|
||||
const (
|
||||
TypeSimple = "simple"
|
||||
TypeVLAN = "vlan"
|
||||
@ -35,14 +37,14 @@ type ZoneData struct {
|
||||
Peers *string `json:"peers,omitempty" url:"peers,omitempty"`
|
||||
|
||||
// EVPN.
|
||||
Controller *string `json:"controller,omitempty" url:"controller,omitempty"`
|
||||
VRFVXLANID *int64 `json:"vrf-vxlan,omitempty" url:"vrf-vxlan,omitempty"`
|
||||
ExitNodes *string `json:"exitnodes,omitempty" url:"exitnodes,omitempty"`
|
||||
ExitNodesPrimary *string `json:"exitnodes-primary,omitempty" url:"exitnodes-primary,omitempty"`
|
||||
ExitNodesLocalRouting *int64 `json:"exitnodes-local-routing,omitempty" url:"exitnodes-local-routing,omitempty"`
|
||||
AdvertiseSubnets *int64 `json:"advertise-subnets,omitempty" url:"advertise-subnets,omitempty"`
|
||||
DisableARPNDSuppression *int64 `json:"disable-arp-nd-suppression,omitempty" url:"disable-arp-nd-suppression,omitempty"`
|
||||
RouteTargetImport *string `json:"rt-import,omitempty" url:"rt-import,omitempty"`
|
||||
Controller *string `json:"controller,omitempty" url:"controller,omitempty"`
|
||||
VRFVXLANID *int64 `json:"vrf-vxlan,omitempty" url:"vrf-vxlan,omitempty"`
|
||||
ExitNodes *string `json:"exitnodes,omitempty" url:"exitnodes,omitempty"`
|
||||
ExitNodesPrimary *string `json:"exitnodes-primary,omitempty" url:"exitnodes-primary,omitempty"`
|
||||
ExitNodesLocalRouting *types.CustomBool `json:"exitnodes-local-routing,omitempty" url:"exitnodes-local-routing,omitempty,int"`
|
||||
AdvertiseSubnets *types.CustomBool `json:"advertise-subnets,omitempty" url:"advertise-subnets,omitempty,int"`
|
||||
DisableARPNDSuppression *types.CustomBool `json:"disable-arp-nd-suppression,omitempty" url:"disable-arp-nd-suppression,omitempty,int"`
|
||||
RouteTargetImport *string `json:"rt-import,omitempty" url:"rt-import,omitempty"`
|
||||
}
|
||||
|
||||
// ZoneRequestData wraps a ZoneData struct with optional delete instructions.
|
||||
|
Loading…
Reference in New Issue
Block a user