Skip to content

Commit fe6a784

Browse files
committed
feat(specs): Create spec, tests and examples for panos_virtual_router_interface
1 parent 7cf5387 commit fe6a784

File tree

4 files changed

+950
-0
lines changed

4 files changed

+950
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
resource "panos_template" "example" {
2+
location = { panorama = {} }
3+
name = "my-template"
4+
}
5+
6+
resource "panos_virtual_router" "example" {
7+
location = {
8+
template = {
9+
name = panos_template.example.name
10+
}
11+
}
12+
name = "vr-1"
13+
}
14+
15+
resource "panos_ethernet_interface" "example" {
16+
location = {
17+
template = {
18+
name = panos_template.example.name
19+
}
20+
}
21+
name = "ethernet1/1"
22+
layer3 = {
23+
ips = [{ name = "10.1.1.1/24" }]
24+
}
25+
}
26+
27+
resource "panos_virtual_router_interface" "example" {
28+
location = {
29+
template = {
30+
name = panos_template.example.name
31+
}
32+
}
33+
virtual_router = panos_virtual_router.example.name
34+
interface = panos_ethernet_interface.example.name
35+
}
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
"github.com/PaloAltoNetworks/pango/locking"
8+
vrouter "github.com/PaloAltoNetworks/pango/network/virtual_router"
9+
"github.com/PaloAltoNetworks/pango/util"
10+
"github.com/PaloAltoNetworks/pango/xmlapi"
11+
"github.com/hashicorp/terraform-plugin-framework/diag"
12+
"github.com/hashicorp/terraform-plugin-framework/resource"
13+
"github.com/hashicorp/terraform-plugin-framework/types"
14+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
15+
16+
sdkmanager "github.com/PaloAltoNetworks/terraform-provider-panos/internal/manager"
17+
)
18+
19+
type VirtualRouterInterfaceCustom struct {
20+
specifier vrouter.Specifier
21+
manager *sdkmanager.EntryObjectManager[*vrouter.Entry, vrouter.Location, *vrouter.Service]
22+
}
23+
24+
func NewVirtualRouterInterfaceCustom(provider *ProviderData) (*VirtualRouterInterfaceCustom, error) {
25+
client := provider.Client
26+
27+
specifier, _, err := vrouter.Versioning(client.Versioning())
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
manager := sdkmanager.NewEntryObjectManager[*vrouter.Entry, vrouter.Location, *vrouter.Service](
33+
client, vrouter.NewService(client), provider.MultiConfigBatchSize, specifier, vrouter.SpecMatches)
34+
35+
return &VirtualRouterInterfaceCustom{
36+
specifier: specifier,
37+
manager: manager,
38+
}, nil
39+
}
40+
41+
func terraformToSdkLocation(ctx context.Context, locationObj types.Object) (*vrouter.Location, diag.Diagnostics) {
42+
var terraformLocation VirtualRouterLocation
43+
44+
diags := locationObj.As(ctx, &terraformLocation, basetypes.ObjectAsOptions{})
45+
if diags.HasError() {
46+
return nil, diags
47+
}
48+
49+
location := &vrouter.Location{}
50+
51+
if !terraformLocation.Ngfw.IsNull() {
52+
location.Ngfw = &vrouter.NgfwLocation{}
53+
var innerLocation VirtualRouterNgfwLocation
54+
diags.Append(terraformLocation.Ngfw.As(ctx, &innerLocation, basetypes.ObjectAsOptions{})...)
55+
if diags.HasError() {
56+
return nil, diags
57+
}
58+
location.Ngfw.NgfwDevice = innerLocation.NgfwDevice.ValueString()
59+
}
60+
61+
if !terraformLocation.Template.IsNull() {
62+
location.Template = &vrouter.TemplateLocation{}
63+
var innerLocation VirtualRouterTemplateLocation
64+
diags.Append(terraformLocation.Template.As(ctx, &innerLocation, basetypes.ObjectAsOptions{})...)
65+
if diags.HasError() {
66+
return nil, diags
67+
}
68+
location.Template.PanoramaDevice = innerLocation.PanoramaDevice.ValueString()
69+
location.Template.Template = innerLocation.Name.ValueString()
70+
location.Template.NgfwDevice = innerLocation.NgfwDevice.ValueString()
71+
}
72+
73+
if !terraformLocation.TemplateStack.IsNull() {
74+
location.TemplateStack = &vrouter.TemplateStackLocation{}
75+
var innerLocation VirtualRouterTemplateStackLocation
76+
diags.Append(terraformLocation.TemplateStack.As(ctx, &innerLocation, basetypes.ObjectAsOptions{})...)
77+
if diags.HasError() {
78+
return nil, diags
79+
}
80+
location.TemplateStack.PanoramaDevice = innerLocation.PanoramaDevice.ValueString()
81+
location.TemplateStack.TemplateStack = innerLocation.Name.ValueString()
82+
location.TemplateStack.NgfwDevice = innerLocation.NgfwDevice.ValueString()
83+
}
84+
85+
return location, nil
86+
}
87+
88+
func (o *VirtualRouterInterfaceResource) ReadCustom(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
89+
var state VirtualRouterInterfaceResourceModel
90+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
91+
if resp.Diagnostics.HasError() {
92+
return
93+
}
94+
95+
location, diags := terraformToSdkLocation(ctx, state.Location)
96+
resp.Diagnostics.Append(diags...)
97+
if resp.Diagnostics.HasError() {
98+
return
99+
}
100+
101+
components := []string{}
102+
object, err := o.custom.manager.Read(ctx, *location, components, state.VirtualRouter.ValueString())
103+
if err != nil {
104+
if errors.Is(err, sdkmanager.ErrObjectNotFound) {
105+
resp.Diagnostics.AddError("Error reading data", err.Error())
106+
} else {
107+
resp.Diagnostics.AddError("Error reading entry", err.Error())
108+
}
109+
return
110+
}
111+
112+
var found bool
113+
for _, elt := range object.Interface {
114+
if elt == state.Interface.ValueString() {
115+
found = true
116+
break
117+
}
118+
}
119+
120+
if !found {
121+
resp.State.RemoveResource(ctx)
122+
}
123+
}
124+
125+
func (o *VirtualRouterInterfaceResource) CreateCustom(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
126+
var plan VirtualRouterInterfaceResourceModel
127+
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
128+
if resp.Diagnostics.HasError() {
129+
return
130+
}
131+
132+
location, diags := terraformToSdkLocation(ctx, plan.Location)
133+
resp.Diagnostics.Append(diags...)
134+
if resp.Diagnostics.HasError() {
135+
return
136+
}
137+
138+
components := []string{}
139+
xpath, err := location.XpathWithComponents(
140+
o.client.Versioning(),
141+
append(components, util.AsEntryXpath(plan.VirtualRouter.ValueString()))...)
142+
if err != nil {
143+
resp.Diagnostics.AddError("Error while generating xpath for parent resource", err.Error())
144+
return
145+
}
146+
147+
mutex := locking.GetMutex(locking.XpathLockCategory, util.AsXpath(xpath))
148+
mutex.Lock()
149+
defer mutex.Unlock()
150+
151+
object, err := o.custom.manager.Read(ctx, *location, components, plan.VirtualRouter.ValueString())
152+
153+
if err != nil {
154+
if errors.Is(err, sdkmanager.ErrObjectNotFound) {
155+
resp.Diagnostics.AddError("Parent resource missing", "Virtual router not found")
156+
} else {
157+
resp.Diagnostics.AddError("Error while reading parent resource", err.Error())
158+
}
159+
return
160+
}
161+
162+
var found bool
163+
for _, elt := range object.Interface {
164+
if elt == plan.Interface.ValueString() {
165+
found = true
166+
break
167+
}
168+
}
169+
170+
if found {
171+
resp.Diagnostics.AddError("Error while creating interface entry", "entry with a matching name already exists")
172+
return
173+
}
174+
175+
object.Interface = o.addInterface(object.Interface, plan.Interface.ValueString())
176+
_, err = o.custom.manager.Update(ctx, *location, components, object, object.Name)
177+
if err != nil {
178+
resp.Diagnostics.AddError("Error while creating interface entry", err.Error())
179+
return
180+
}
181+
182+
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
183+
}
184+
185+
func (o *VirtualRouterInterfaceResource) removeInterface(ifaces []string, needle string) []string {
186+
var result []string
187+
for _, elt := range ifaces {
188+
if elt != needle {
189+
result = append(result, elt)
190+
}
191+
}
192+
193+
return result
194+
}
195+
196+
func (o *VirtualRouterInterfaceResource) addInterface(ifaces []string, needle string) []string {
197+
var found bool
198+
var result []string
199+
200+
for _, elt := range ifaces {
201+
if elt == needle {
202+
found = true
203+
}
204+
result = append(result, elt)
205+
}
206+
207+
if !found {
208+
result = append(result, needle)
209+
}
210+
211+
return result
212+
}
213+
214+
func (o *VirtualRouterInterfaceResource) UpdateCustom(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
215+
var state, plan VirtualRouterInterfaceResourceModel
216+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
217+
if resp.Diagnostics.HasError() {
218+
return
219+
}
220+
221+
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
222+
if resp.Diagnostics.HasError() {
223+
return
224+
}
225+
226+
location, diags := terraformToSdkLocation(ctx, state.Location)
227+
resp.Diagnostics.Append(diags...)
228+
if resp.Diagnostics.HasError() {
229+
return
230+
}
231+
232+
updates := xmlapi.NewMultiConfig(2)
233+
234+
components := []string{}
235+
if state.VirtualRouter.ValueString() != plan.VirtualRouter.ValueString() {
236+
xpath, err := location.XpathWithComponents(
237+
o.client.Versioning(),
238+
append(components, util.AsEntryXpath(state.VirtualRouter.ValueString()))...)
239+
if err != nil {
240+
resp.Diagnostics.AddError("Error while creating xpath for parent resource", err.Error())
241+
return
242+
}
243+
244+
mutex := locking.GetMutex(locking.XpathLockCategory, util.AsXpath(xpath))
245+
mutex.Lock()
246+
defer mutex.Unlock()
247+
248+
object, err := o.custom.manager.Read(ctx, *location, components, state.VirtualRouter.ValueString())
249+
if err != nil {
250+
if !errors.Is(err, sdkmanager.ErrObjectNotFound) {
251+
resp.Diagnostics.AddError("Error while reading parent resource", err.Error())
252+
return
253+
}
254+
}
255+
256+
if object != nil {
257+
object.Interface = o.removeInterface(object.Interface, state.Interface.ValueString())
258+
xmlEntry, err := o.custom.specifier(object)
259+
if err != nil {
260+
resp.Diagnostics.AddError("Error while creating XML document for parent resource", err.Error())
261+
return
262+
}
263+
264+
updates.Add(&xmlapi.Config{
265+
Action: "edit",
266+
Xpath: util.AsXpath(xpath),
267+
Element: xmlEntry,
268+
Target: o.client.GetTarget(),
269+
})
270+
}
271+
}
272+
273+
xpath, err := location.XpathWithComponents(
274+
o.client.Versioning(),
275+
append(components, util.AsEntryXpath(plan.VirtualRouter.ValueString()))...)
276+
if err != nil {
277+
resp.Diagnostics.AddError("Error while creating xpath for parent resource", err.Error())
278+
return
279+
}
280+
281+
mutex := locking.GetMutex(locking.XpathLockCategory, util.AsXpath(xpath))
282+
mutex.Lock()
283+
defer mutex.Unlock()
284+
285+
object, err := o.custom.manager.Read(ctx, *location, components, plan.VirtualRouter.ValueString())
286+
if err != nil {
287+
resp.Diagnostics.AddError("Error while reading parent resource", err.Error())
288+
return
289+
}
290+
291+
object.Interface = o.removeInterface(object.Interface, state.Interface.ValueString())
292+
object.Interface = o.addInterface(object.Interface, plan.Interface.ValueString())
293+
xmlEntry, err := o.custom.specifier(object)
294+
if err != nil {
295+
resp.Diagnostics.AddError("Error while creating XML document for parent resource", err.Error())
296+
return
297+
}
298+
299+
updates.Add(&xmlapi.Config{
300+
Action: "edit",
301+
Xpath: util.AsXpath(xpath),
302+
Element: xmlEntry,
303+
Target: o.client.GetTarget(),
304+
})
305+
306+
if len(updates.Operations) > 0 {
307+
if _, _, _, err := o.client.MultiConfig(ctx, updates, false, nil); err != nil {
308+
resp.Diagnostics.AddError("Error while updating parent resource", err.Error())
309+
return
310+
}
311+
}
312+
313+
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
314+
}
315+
316+
func (o *VirtualRouterInterfaceResource) DeleteCustom(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
317+
var state VirtualRouterInterfaceResourceModel
318+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
319+
if resp.Diagnostics.HasError() {
320+
return
321+
}
322+
323+
location, diags := terraformToSdkLocation(ctx, state.Location)
324+
resp.Diagnostics.Append(diags...)
325+
if resp.Diagnostics.HasError() {
326+
return
327+
}
328+
329+
components := []string{}
330+
xpath, err := location.XpathWithComponents(
331+
o.client.Versioning(),
332+
append(components, util.AsEntryXpath(state.VirtualRouter.ValueString()))...)
333+
if err != nil {
334+
resp.Diagnostics.AddError("Error while generating xpath for parent resource", err.Error())
335+
return
336+
}
337+
338+
mutex := locking.GetMutex(locking.XpathLockCategory, util.AsXpath(xpath))
339+
mutex.Lock()
340+
defer mutex.Unlock()
341+
342+
object, err := o.custom.manager.Read(ctx, *location, components, state.VirtualRouter.ValueString())
343+
if err != nil {
344+
if !errors.Is(err, sdkmanager.ErrObjectNotFound) {
345+
resp.Diagnostics.AddError("Error while reading parent resource", err.Error())
346+
}
347+
return
348+
}
349+
350+
object.Interface = o.removeInterface(object.Interface, state.Interface.ValueString())
351+
o.custom.manager.Update(ctx, *location, components, object, object.Name)
352+
if err != nil {
353+
resp.Diagnostics.AddError("Error while deleting interface", err.Error())
354+
}
355+
356+
return
357+
}

0 commit comments

Comments
 (0)