@@ -8,184 +8,151 @@ package zone
8
8
9
9
import (
10
10
"context"
11
- "errors"
12
- "fmt"
11
+ "regexp"
13
12
13
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
14
+ "github.com/hashicorp/terraform-plugin-framework/diag"
14
15
"github.com/hashicorp/terraform-plugin-framework/resource"
16
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
17
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
18
+ "github.com/hashicorp/terraform-plugin-framework/types"
15
19
16
- "github.com/bpg/terraform-provider-proxmox/fwprovider/config"
17
- "github.com/bpg/terraform-provider-proxmox/proxmox/api"
20
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
18
21
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/sdn/zones"
19
- "github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
22
+
23
+ proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types"
20
24
)
21
25
22
26
var (
23
27
_ resource.ResourceWithConfigure = & EVPNResource {}
24
28
_ resource.ResourceWithImportState = & EVPNResource {}
25
29
)
26
30
27
- type EVPNResource struct {
28
- client * zones.Client
31
+ type evpnModel struct {
32
+ genericModel
33
+
34
+ AdvertiseSubnets types.Bool `tfsdk:"advertise_subnets"`
35
+ Controller types.String `tfsdk:"controller"`
36
+ DisableARPNDSuppression types.Bool `tfsdk:"disable_arp_nd_suppression"`
37
+ ExitNodes stringset.Value `tfsdk:"exit_nodes"`
38
+ ExitNodesLocalRouting types.Bool `tfsdk:"exit_nodes_local_routing"`
39
+ PrimaryExitNode types.String `tfsdk:"primary_exit_node"`
40
+ RouteTargetImport types.String `tfsdk:"rt_import"`
41
+ VRFVXLANID types.Int64 `tfsdk:"vrf_vxlan"`
29
42
}
30
43
31
- func NewEVPNResource () resource.Resource {
32
- return & EVPNResource {}
44
+ func (m * evpnModel ) importFromAPI (name string , data * zones.ZoneData , diags * diag.Diagnostics ) {
45
+ m .genericModel .importFromAPI (name , data , diags )
46
+
47
+ m .AdvertiseSubnets = types .BoolPointerValue (data .AdvertiseSubnets .PointerBool ())
48
+ m .Controller = types .StringPointerValue (data .Controller )
49
+ m .DisableARPNDSuppression = types .BoolPointerValue (data .DisableARPNDSuppression .PointerBool ())
50
+ m .ExitNodes = stringset .NewValueString (data .ExitNodes , diags , stringset .WithSeparator ("," ))
51
+ m .ExitNodesLocalRouting = types .BoolPointerValue (data .ExitNodesLocalRouting .PointerBool ())
52
+ m .PrimaryExitNode = types .StringPointerValue (data .ExitNodesPrimary )
53
+ m .RouteTargetImport = types .StringPointerValue (data .RouteTargetImport )
54
+ m .VRFVXLANID = types .Int64PointerValue (data .VRFVXLANID )
33
55
}
34
56
35
- func (r * EVPNResource ) Metadata (
36
- _ context.Context ,
37
- req resource.MetadataRequest ,
38
- resp * resource.MetadataResponse ,
39
- ) {
40
- resp .TypeName = req .ProviderTypeName + "_sdn_zone_evpn"
41
- }
57
+ func (m * evpnModel ) toAPIRequestBody (ctx context.Context , diags * diag.Diagnostics ) * zones.ZoneRequestData {
58
+ data := m .genericModel .toAPIRequestBody (ctx , diags )
42
59
43
- func ( r * EVPNResource ) Configure (
44
- _ context. Context ,
45
- req resource. ConfigureRequest ,
46
- resp * resource. ConfigureResponse ,
47
- ) {
48
- if req . ProviderData == nil {
49
- return
50
- }
60
+ data . AdvertiseSubnets = proxmoxtypes . CustomBoolPtr ( m . AdvertiseSubnets . ValueBoolPointer ())
61
+ data . Controller = m . Controller . ValueStringPointer ()
62
+ data . DisableARPNDSuppression = proxmoxtypes . CustomBoolPtr ( m . DisableARPNDSuppression . ValueBoolPointer ())
63
+ data . ExitNodes = m . ExitNodes . ValueStringPointer ( ctx , diags , stringset . WithSeparator ( "," ))
64
+ data . ExitNodesLocalRouting = proxmoxtypes . CustomBoolPtr ( m . ExitNodesLocalRouting . ValueBoolPointer ())
65
+ data . ExitNodesPrimary = m . PrimaryExitNode . ValueStringPointer ()
66
+ data . RouteTargetImport = m . RouteTargetImport . ValueStringPointer ()
67
+ data . VRFVXLANID = m . VRFVXLANID . ValueInt64Pointer ()
51
68
52
- cfg , ok := req .ProviderData .(config.Resource )
53
- if ! ok {
54
- resp .Diagnostics .AddError (
55
- "Unexpected Resource Configure Type" ,
56
- fmt .Sprintf (
57
- "Expected config.Resource, got: %T" ,
58
- req .ProviderData ,
59
- ),
60
- )
61
- return
62
- }
63
-
64
- r .client = cfg .Client .Cluster ().SDNZones ()
69
+ return data
65
70
}
66
71
67
- func (r * EVPNResource ) Create (
68
- ctx context.Context ,
69
- req resource.CreateRequest ,
70
- resp * resource.CreateResponse ,
71
- ) {
72
- var plan evpnModel
73
-
74
- resp .Diagnostics .Append (req .Plan .Get (ctx , & plan )... )
75
-
76
- if resp .Diagnostics .HasError () {
77
- return
78
- }
79
-
80
- reqData := plan .toAPIRequestBody (ctx , & resp .Diagnostics )
81
- reqData .Type = ptr .Ptr (zones .TypeEVPN )
82
-
83
- if err := r .client .CreateZone (ctx , reqData ); err != nil {
84
- resp .Diagnostics .AddError (
85
- "Unable to Create SDN EVPN Zone" ,
86
- err .Error (),
87
- )
88
- return
89
- }
90
-
91
- resp .Diagnostics .Append (resp .State .Set (ctx , & plan )... )
72
+ type EVPNResource struct {
73
+ generic * genericZoneResource
92
74
}
93
75
94
- func (r * EVPNResource ) Read (
95
- ctx context.Context ,
96
- req resource.ReadRequest ,
97
- resp * resource.ReadResponse ,
98
- ) {
99
- var state evpnModel
100
-
101
- resp .Diagnostics .Append (req .State .Get (ctx , & state )... )
102
-
103
- if resp .Diagnostics .HasError () {
104
- return
105
- }
106
-
107
- zone , err := r .client .GetZone (ctx , state .ID .ValueString ())
108
- if err != nil {
109
- if errors .Is (err , api .ErrResourceDoesNotExist ) {
110
- resp .State .RemoveResource (ctx )
111
- return
112
- }
113
-
114
- resp .Diagnostics .AddError (
115
- "Unable to Read SDN EVPN Zone" ,
116
- err .Error (),
117
- )
118
- return
76
+ func NewEVPNResource () resource.Resource {
77
+ return & EVPNResource {
78
+ generic : newGenericZoneResource (zoneResourceConfig {
79
+ typeNameSuffix : "_sdn_zone_evpn" ,
80
+ zoneType : zones .TypeEVPN ,
81
+ modelFunc : func () zoneModel { return & evpnModel {} },
82
+ }).(* genericZoneResource ),
119
83
}
120
-
121
- readModel := & evpnModel {}
122
- readModel .importFromAPI (zone .ID , zone , & resp .Diagnostics )
123
- resp .Diagnostics .Append (resp .State .Set (ctx , readModel )... )
124
84
}
125
85
126
- func (r * EVPNResource ) Update (
127
- ctx context.Context ,
128
- req resource.UpdateRequest ,
129
- resp * resource.UpdateResponse ,
130
- ) {
131
- var plan evpnModel
132
-
133
- resp .Diagnostics .Append (req .Plan .Get (ctx , & plan )... )
134
-
135
- if resp .Diagnostics .HasError () {
136
- return
86
+ func (r * EVPNResource ) Schema (_ context.Context , _ resource.SchemaRequest , resp * resource.SchemaResponse ) {
87
+ resp .Schema = schema.Schema {
88
+ Description : "EVPN Zone in Proxmox SDN." ,
89
+ MarkdownDescription : "EVPN Zone in Proxmox SDN. The EVPN zone creates a routable Layer 3 network, capable of " +
90
+ "spanning across multiple clusters." ,
91
+ Attributes : genericAttributesWith (map [string ]schema.Attribute {
92
+ "advertise_subnets" : schema.BoolAttribute {
93
+ Optional : true ,
94
+ Description : "Enable subnet advertisement for EVPN." ,
95
+ },
96
+ "controller" : schema.StringAttribute {
97
+ Optional : true ,
98
+ Description : "EVPN controller address." ,
99
+ },
100
+ "disable_arp_nd_suppression" : schema.BoolAttribute {
101
+ Optional : true ,
102
+ Description : "Disable ARP/ND suppression for EVPN." ,
103
+ },
104
+ "exit_nodes" : stringset .ResourceAttribute ("List of exit nodes for EVPN." , "" ),
105
+ "exit_nodes_local_routing" : schema.BoolAttribute {
106
+ Optional : true ,
107
+ Description : "Enable local routing for EVPN exit nodes." ,
108
+ },
109
+ "primary_exit_node" : schema.StringAttribute {
110
+ Optional : true ,
111
+ Description : "Primary exit node for EVPN." ,
112
+ },
113
+ "rt_import" : schema.StringAttribute {
114
+ Optional : true ,
115
+ Description : "Route target import for EVPN." ,
116
+ Validators : []validator.String {
117
+ stringvalidator .RegexMatches (
118
+ regexp .MustCompile (`^(\d+):(\d+)$` ),
119
+ "must be in the format '<ASN>:<number>' (e.g., '65000:65000')" ,
120
+ ),
121
+ },
122
+ },
123
+ "vrf_vxlan" : schema.Int64Attribute {
124
+ Optional : true ,
125
+ Description : "VRF VXLAN-ID used for dedicated routing interconnect between VNets. It must be different " +
126
+ "than the VXLAN-ID of the VNets." ,
127
+ },
128
+ }),
137
129
}
130
+ }
138
131
139
- reqData := plan .toAPIRequestBody (ctx , & resp .Diagnostics )
140
-
141
- if err := r .client .UpdateZone (ctx , reqData ); err != nil {
142
- resp .Diagnostics .AddError (
143
- "Unable to Update SDN EVPN Zone" ,
144
- err .Error (),
145
- )
146
- return
147
- }
132
+ func (r * EVPNResource ) Metadata (ctx context.Context , req resource.MetadataRequest , resp * resource.MetadataResponse ) {
133
+ r .generic .Metadata (ctx , req , resp )
134
+ }
148
135
149
- resp .Diagnostics .Append (resp .State .Set (ctx , & plan )... )
136
+ func (r * EVPNResource ) Configure (ctx context.Context , req resource.ConfigureRequest , resp * resource.ConfigureResponse ) {
137
+ r .generic .Configure (ctx , req , resp )
150
138
}
151
139
152
- func (r * EVPNResource ) Delete (
153
- ctx context.Context ,
154
- req resource.DeleteRequest ,
155
- resp * resource.DeleteResponse ,
156
- ) {
157
- var state evpnModel
140
+ func (r * EVPNResource ) Create (ctx context.Context , req resource.CreateRequest , resp * resource.CreateResponse ) {
141
+ r .generic .Create (ctx , req , resp )
142
+ }
158
143
159
- resp .Diagnostics .Append (req .State .Get (ctx , & state )... )
144
+ func (r * EVPNResource ) Read (ctx context.Context , req resource.ReadRequest , resp * resource.ReadResponse ) {
145
+ r .generic .Read (ctx , req , resp )
146
+ }
160
147
161
- if resp . Diagnostics . HasError ( ) {
162
- return
163
- }
148
+ func ( r * EVPNResource ) Update ( ctx context. Context , req resource. UpdateRequest , resp * resource. UpdateResponse ) {
149
+ r . generic . Update ( ctx , req , resp )
150
+ }
164
151
165
- if err := r .client .DeleteZone (ctx , state .ID .ValueString ()); err != nil &&
166
- ! errors .Is (err , api .ErrResourceDoesNotExist ) {
167
- resp .Diagnostics .AddError (
168
- "Unable to Delete SDN EVPN Zone" ,
169
- err .Error (),
170
- )
171
- }
152
+ func (r * EVPNResource ) Delete (ctx context.Context , req resource.DeleteRequest , resp * resource.DeleteResponse ) {
153
+ r .generic .Delete (ctx , req , resp )
172
154
}
173
155
174
- func (r * EVPNResource ) ImportState (
175
- ctx context.Context ,
176
- req resource.ImportStateRequest ,
177
- resp * resource.ImportStateResponse ,
178
- ) {
179
- zone , err := r .client .GetZone (ctx , req .ID )
180
- if err != nil {
181
- if errors .Is (err , api .ErrResourceDoesNotExist ) {
182
- resp .Diagnostics .AddError (fmt .Sprintf ("Zone %s does not exist" , req .ID ), err .Error ())
183
- return
184
- }
185
- resp .Diagnostics .AddError (fmt .Sprintf ("Unable to Import SDN EVPN Zone %s" , req .ID ), err .Error ())
186
- return
187
- }
188
- readModel := & evpnModel {}
189
- readModel .importFromAPI (zone .ID , zone , & resp .Diagnostics )
190
- resp .Diagnostics .Append (resp .State .Set (ctx , readModel )... )
156
+ func (r * EVPNResource ) ImportState (ctx context.Context , req resource.ImportStateRequest , resp * resource.ImportStateResponse ) {
157
+ r .generic .ImportState (ctx , req , resp )
191
158
}
0 commit comments