-
Notifications
You must be signed in to change notification settings - Fork 2
/
bgp_peering_generic_system.go
355 lines (325 loc) · 13.7 KB
/
bgp_peering_generic_system.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
package connectivitytemplate
import (
"bytes"
"context"
"crypto/sha1"
"encoding/json"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
apstravalidator "github.com/Juniper/terraform-provider-apstra/apstra/apstra_validator"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
dataSourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"math"
"sort"
"strings"
)
var _ Primitive = &BgpPeeringGenericSystem{}
type BgpPeeringGenericSystem struct {
Name types.String `tfsdk:"name"`
Ttl types.Int64 `tfsdk:"ttl"`
BfdEnabled types.Bool `tfsdk:"bfd_enabled"`
Password types.String `tfsdk:"password"`
KeepaliveTime types.Int64 `tfsdk:"keepalive_time"`
HoldTime types.Int64 `tfsdk:"hold_time"`
Ipv4AddressingType types.String `tfsdk:"ipv4_addressing_type"`
Ipv6AddressingType types.String `tfsdk:"ipv6_addressing_type"`
LocalAsn types.Int64 `tfsdk:"local_asn"`
NeighborAsnDynamic types.Bool `tfsdk:"neighbor_asn_dynamic"`
PeerFromLoopback types.Bool `tfsdk:"peer_from_loopback"`
PeerTo types.String `tfsdk:"peer_to"`
ChildPrimitives types.Set `tfsdk:"child_primitives"`
Primitive types.String `tfsdk:"primitive"`
}
func (o BgpPeeringGenericSystem) DataSourceAttributes() map[string]dataSourceSchema.Attribute {
ipv4AddressingTypes := []string{
apstra.CtPrimitiveIPv4ProtocolSessionAddressingNone.String(),
apstra.CtPrimitiveIPv4ProtocolSessionAddressingAddressed.String(),
}
ipv6AddressingTypes := []string{
apstra.CtPrimitiveIPv6ProtocolSessionAddressingNone.String(),
apstra.CtPrimitiveIPv6ProtocolSessionAddressingAddressed.String(),
apstra.CtPrimitiveIPv6ProtocolSessionAddressingLinkLocal.String(),
}
peerTo := []string{
apstra.CtPrimitiveBgpPeerToLoopback.String(),
apstra.CtPrimitiveBgpPeerToInterfaceOrIpEndpoint.String(),
apstra.CtPrimitiveBgpPeerToInterfaceOrSharedIpEndpoint.String(),
}
return map[string]dataSourceSchema.Attribute{
"name": dataSourceSchema.StringAttribute{
MarkdownDescription: "Primitive name displayed in the web UI",
Optional: true,
},
"ttl": dataSourceSchema.Int64Attribute{
MarkdownDescription: "BGP Time To Live. Omit to use device defaults.",
Optional: true,
Validators: []validator.Int64{int64validator.Between(1, math.MaxUint8)},
},
"bfd_enabled": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Enable BFD.",
Optional: true,
},
"password": dataSourceSchema.StringAttribute{
MarkdownDescription: "",
Optional: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"keepalive_time": dataSourceSchema.Int64Attribute{
MarkdownDescription: "BGP keepalive time (seconds).",
Optional: true,
Validators: []validator.Int64{
int64validator.Between(1, math.MaxUint16),
int64validator.AlsoRequires(path.MatchRoot("hold_time")),
},
},
"hold_time": dataSourceSchema.Int64Attribute{
MarkdownDescription: "BGP hold time (seconds).",
Optional: true,
Validators: []validator.Int64{
int64validator.Between(1, math.MaxUint16),
int64validator.AlsoRequires(path.MatchRoot("keepalive_time")),
apstravalidator.AtLeastProductOf(3, path.MatchRoot("keepalive_time")),
},
},
"ipv4_addressing_type": dataSourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("One of `%s` (or omit)",
strings.Join(ipv4AddressingTypes, "`, `"),
),
Optional: true,
Validators: []validator.String{stringvalidator.OneOf(ipv4AddressingTypes...)},
},
"ipv6_addressing_type": dataSourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("One of `%s` (or omit)",
strings.Join(ipv6AddressingTypes, "`, `"),
),
Optional: true,
Validators: []validator.String{stringvalidator.OneOf(ipv6AddressingTypes...)},
},
"local_asn": dataSourceSchema.Int64Attribute{
MarkdownDescription: "This feature is configured on a per-peer basis. It allows a " +
"router to appear to be a member of a second autonomous system (AS) by prepending " +
"a local-as AS number, in addition to its real AS number, announced to its eBGP " +
"peer, resulting in an AS path length of two.",
Optional: true,
Validators: []validator.Int64{int64validator.Between(1, math.MaxUint32)},
},
"neighbor_asn_dynamic": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Default behavior is `static`",
Optional: true,
},
"peer_from_loopback": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Enable to peer from loopback interface. Default behavior peers from physical interface.",
Optional: true,
},
"peer_to": dataSourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("One of `%s` (or omit)",
strings.Join(peerTo, "`, `"),
),
Optional: true,
Validators: []validator.String{stringvalidator.OneOf(peerTo...)},
},
"child_primitives": dataSourceSchema.SetAttribute{
MarkdownDescription: "Set of JSON strings describing Connectivity Template Primitives " +
"which are children of this Connectivity Template Primitive. Use the `primitive` " +
"attribute of other Connectivity Template Primitives data sources here.",
ElementType: types.StringType,
Validators: []validator.Set{setvalidator.SizeAtLeast(1)},
Optional: true,
},
"primitive": dataSourceSchema.StringAttribute{
MarkdownDescription: "JSON output for use in the `primitives` field of an " +
"`apstra_datacenter_connectivity_template` resource or a different Connectivity " +
"Template JsonPrimitive data source",
Computed: true,
},
}
}
func (o BgpPeeringGenericSystem) Marshal(ctx context.Context, diags *diag.Diagnostics) string {
obj := bgpPeeringGenericSystemPrototype{
Ipv4AfiEnabled: !o.Ipv4AddressingType.IsNull() && o.Ipv4AddressingType.ValueString() != apstra.CtPrimitiveIPv4ProtocolSessionAddressingNone.String(),
Ipv6AfiEnabled: !o.Ipv6AddressingType.IsNull() && o.Ipv6AddressingType.ValueString() != apstra.CtPrimitiveIPv6ProtocolSessionAddressingNone.String(),
Ttl: uint8(o.Ttl.ValueInt64()),
BfdEnabled: o.BfdEnabled.ValueBool(),
Password: o.Password.ValueStringPointer(),
Ipv4AddressingType: o.Ipv4AddressingType.ValueString(),
Ipv6AddressingType: o.Ipv6AddressingType.ValueString(),
NeighborAsnDynamic: o.NeighborAsnDynamic.ValueBool(),
PeerFromLoopback: o.PeerFromLoopback.ValueBool(),
PeerTo: o.PeerTo.ValueString(), // see below
}
if !o.KeepaliveTime.IsNull() {
t := uint16(o.KeepaliveTime.ValueInt64())
obj.KeepaliveTime = &t
}
if !o.HoldTime.IsNull() {
t := uint16(o.HoldTime.ValueInt64())
obj.HoldTime = &t
}
if obj.Ipv4AddressingType == "" { // set default for omitted attribute
obj.Ipv4AddressingType = apstra.CtPrimitiveIPv4ProtocolSessionAddressingNone.String()
}
if obj.Ipv6AddressingType == "" { // set default for omitted attribute
obj.Ipv6AddressingType = apstra.CtPrimitiveIPv4ProtocolSessionAddressingNone.String()
}
if !o.LocalAsn.IsNull() {
la := uint32(o.LocalAsn.ValueInt64())
obj.LocalAsn = &la
}
if obj.PeerTo == "" { // set default for omitted attribute
obj.PeerTo = apstra.CtPrimitiveBgpPeerToInterfaceOrIpEndpoint.String()
}
var childPrimitives []string
diags.Append(o.ChildPrimitives.ElementsAs(ctx, &childPrimitives, false)...)
if diags.HasError() {
return ""
}
// sort the childPrimitives by their SHA1 sums for easier comparison of nested strings
sort.Slice(childPrimitives, func(i, j int) bool {
sum1 := sha1.Sum([]byte(childPrimitives[i]))
sum2 := sha1.Sum([]byte(childPrimitives[j]))
return bytes.Compare(sum1[:], sum2[:]) >= 0
})
obj.ChildPrimitives = childPrimitives
data, err := json.Marshal(&obj)
if err != nil {
diags.AddError("failed marshaling BgpPeeringGenericSystem primitive data", err.Error())
return ""
}
data, err = json.Marshal(&tfCfgPrimitive{
PrimitiveType: apstra.CtPrimitivePolicyTypeNameAttachBgpOverSubinterfacesOrSvi.String(),
Label: o.Name.ValueString(),
Data: data,
})
if err != nil {
diags.AddError("failed marshaling primitive", err.Error())
return ""
}
return string(data)
}
func (o *BgpPeeringGenericSystem) loadSdkPrimitive(ctx context.Context, in apstra.ConnectivityTemplatePrimitive, diags *diag.Diagnostics) {
attributes, ok := in.Attributes.(*apstra.ConnectivityTemplatePrimitiveAttributesAttachBgpOverSubinterfacesOrSvi)
if !ok {
diags.AddError("failed loading SDK primitive due to wrong attribute type", fmt.Sprintf("unexpected type %T", in))
return
}
o.Ttl = types.Int64Value(int64(attributes.Ttl))
o.BfdEnabled = types.BoolValue(attributes.Bfd)
if attributes.Password != nil {
o.Password = types.StringValue(*attributes.Password)
} else {
o.Password = types.StringNull()
}
if attributes.Keepalive != nil {
o.KeepaliveTime = types.Int64Value(int64(*attributes.Keepalive))
} else {
o.KeepaliveTime = types.Int64Null()
}
if attributes.Holdtime != nil {
o.HoldTime = types.Int64Value(int64(*attributes.Holdtime))
} else {
o.HoldTime = types.Int64Null()
}
o.Ipv4AddressingType = types.StringValue(attributes.SessionAddressingIpv4.String())
o.Ipv6AddressingType = types.StringValue(attributes.SessionAddressingIpv6.String())
if attributes.LocalAsn != nil {
o.LocalAsn = types.Int64Value(int64(*attributes.LocalAsn))
} else {
o.LocalAsn = types.Int64Null()
}
o.NeighborAsnDynamic = types.BoolValue(attributes.NeighborAsnDynamic)
o.PeerFromLoopback = types.BoolValue(attributes.PeerFromLoopback)
o.PeerTo = types.StringValue(attributes.PeerTo.String())
o.ChildPrimitives = utils.SetValueOrNull(ctx, types.StringType, SdkPrimitivesToJsonStrings(ctx, in.Subpolicies, diags), diags)
o.Name = types.StringValue(in.Label)
}
var _ JsonPrimitive = &bgpPeeringGenericSystemPrototype{}
type bgpPeeringGenericSystemPrototype struct {
Label string `json:"label,omitempty"`
Ipv4AfiEnabled bool `json:"ipv4_afi_enabled"`
Ipv6AfiEnabled bool `json:"ipv6_afi_enabled"`
Ttl uint8 `json:"ttl"`
BfdEnabled bool `json:"bfd_enabled"`
Password *string `json:"password"`
KeepaliveTime *uint16 `json:"keepalive_time"`
HoldTime *uint16 `json:"hold_time"`
Ipv4AddressingType string `json:"ipv4_addressing_type"`
Ipv6AddressingType string `json:"ipv6_addressing_type"`
LocalAsn *uint32 `json:"local_asn"`
NeighborAsnDynamic bool `json:"neighbor_asn_dynamic"`
PeerFromLoopback bool `json:"peer_from_loopback"`
PeerTo string `json:"peer_to"`
ChildPrimitives []string `json:"child_primitives"`
}
func (o bgpPeeringGenericSystemPrototype) attributes(_ context.Context, path path.Path, diags *diag.Diagnostics) apstra.ConnectivityTemplatePrimitiveAttributes {
var err error
var ipv4AddressingType apstra.CtPrimitiveIPv4ProtocolSessionAddressing
err = ipv4AddressingType.FromString(o.Ipv4AddressingType)
if err != nil {
diags.AddAttributeError(path, fmt.Sprintf("failed parsing ipv4 addressing type %q", o.Ipv4AddressingType), err.Error())
return nil
}
var ipv6AddressingType apstra.CtPrimitiveIPv6ProtocolSessionAddressing
err = ipv6AddressingType.FromString(o.Ipv6AddressingType)
if err != nil {
diags.AddAttributeError(path, fmt.Sprintf("failed parsing ipv6 addressing type %q", o.Ipv6AddressingType), err.Error())
return nil
}
var peerTo apstra.CtPrimitiveBgpPeerTo
err = peerTo.FromString(o.PeerTo)
if err != nil {
diags.AddAttributeError(path, "failed parsing peer_to", err.Error())
return nil
}
var sessionAddressingIpv4 apstra.CtPrimitiveIPv4ProtocolSessionAddressing
err = sessionAddressingIpv4.FromString(o.Ipv4AddressingType)
if err != nil {
diags.AddAttributeError(path, "failed parsing ipv4_addressing_type", err.Error())
return nil
}
var sessionAddressingIpv6 apstra.CtPrimitiveIPv6ProtocolSessionAddressing
err = sessionAddressingIpv6.FromString(o.Ipv6AddressingType)
if err != nil {
diags.AddAttributeError(path, "failed parsing ipv6_addressing_type", err.Error())
return nil
}
return &apstra.ConnectivityTemplatePrimitiveAttributesAttachBgpOverSubinterfacesOrSvi{
Bfd: o.BfdEnabled,
Holdtime: o.HoldTime,
Ipv4Safi: o.Ipv4AfiEnabled,
Ipv6Safi: o.Ipv6AfiEnabled,
Keepalive: o.KeepaliveTime,
LocalAsn: o.LocalAsn,
NeighborAsnDynamic: o.NeighborAsnDynamic,
Password: o.Password,
PeerFromLoopback: o.PeerFromLoopback,
PeerTo: peerTo,
SessionAddressingIpv4: sessionAddressingIpv4,
SessionAddressingIpv6: sessionAddressingIpv6,
Ttl: o.Ttl,
}
}
func (o bgpPeeringGenericSystemPrototype) ToSdkPrimitive(ctx context.Context, path path.Path, diags *diag.Diagnostics) *apstra.ConnectivityTemplatePrimitive {
attributes := o.attributes(ctx, path, diags)
if diags.HasError() {
return nil
}
childPrimitives := ChildPrimitivesFromListOfJsonStrings(ctx, o.ChildPrimitives, path, diags)
if diags.HasError() {
return nil
}
return &apstra.ConnectivityTemplatePrimitive{
Id: nil, // calculated later
Label: o.Label,
Attributes: attributes,
Subpolicies: childPrimitives,
BatchId: nil, // calculated later
PipelineId: nil, // calculated later
}
}