-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #731 from Juniper/688-support-freeform-reference-d…
…esign 688 support freeform reference design
- Loading branch information
Showing
87 changed files
with
3,923 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package blueprint | ||
|
||
import ( | ||
"context" | ||
"regexp" | ||
|
||
"github.com/Juniper/apstra-go-sdk/apstra" | ||
"github.com/Juniper/terraform-provider-apstra/apstra/utils" | ||
"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" | ||
resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
type FreeformConfigTemplate struct { | ||
Id types.String `tfsdk:"id"` | ||
BlueprintId types.String `tfsdk:"blueprint_id"` | ||
Name types.String `tfsdk:"name"` | ||
Text types.String `tfsdk:"text"` | ||
Tags types.Set `tfsdk:"tags"` | ||
} | ||
|
||
func (o FreeformConfigTemplate) DataSourceAttributes() map[string]dataSourceSchema.Attribute { | ||
return map[string]dataSourceSchema.Attribute{ | ||
"blueprint_id": dataSourceSchema.StringAttribute{ | ||
MarkdownDescription: "Apstra Blueprint ID. Used to identify " + | ||
"the Blueprint where the Config Template lives.", | ||
Required: true, | ||
Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, | ||
}, | ||
"id": dataSourceSchema.StringAttribute{ | ||
MarkdownDescription: "Populate this field to look up the Config Template by ID. Required when `name` is omitted.", | ||
Optional: true, | ||
Computed: true, | ||
Validators: []validator.String{ | ||
stringvalidator.LengthAtLeast(1), | ||
stringvalidator.ExactlyOneOf(path.Expressions{ | ||
path.MatchRelative(), | ||
path.MatchRoot("name"), | ||
}...), | ||
}, | ||
}, | ||
"name": dataSourceSchema.StringAttribute{ | ||
MarkdownDescription: "Populate this field to look up an imported Config Template by Name. Required when `id` is omitted.", | ||
Optional: true, | ||
Computed: true, | ||
Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, | ||
}, | ||
"text": dataSourceSchema.StringAttribute{ | ||
MarkdownDescription: "Configuration Jinja2 template text", | ||
Computed: true, | ||
}, | ||
"tags": dataSourceSchema.SetAttribute{ | ||
MarkdownDescription: "Set of Tag labels", | ||
ElementType: types.StringType, | ||
Computed: true, | ||
}, | ||
} | ||
} | ||
|
||
func (o FreeformConfigTemplate) ResourceAttributes() map[string]resourceSchema.Attribute { | ||
return map[string]resourceSchema.Attribute{ | ||
"blueprint_id": resourceSchema.StringAttribute{ | ||
MarkdownDescription: "Apstra Blueprint ID.", | ||
Required: true, | ||
Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, | ||
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, | ||
}, | ||
"id": resourceSchema.StringAttribute{ | ||
MarkdownDescription: "ID of the Config Template.", | ||
Computed: true, | ||
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, | ||
}, | ||
"name": resourceSchema.StringAttribute{ | ||
MarkdownDescription: "Config Template name as shown in the Web UI. Must end with `.jinja`.", | ||
Required: true, | ||
Validators: []validator.String{ | ||
stringvalidator.LengthAtLeast(7), | ||
stringvalidator.RegexMatches(regexp.MustCompile(".jinja$"), "must end with '.jinja'"), | ||
}, | ||
}, | ||
"text": resourceSchema.StringAttribute{ | ||
MarkdownDescription: "Configuration Jinja2 template text", | ||
Required: true, | ||
Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, | ||
}, | ||
"tags": resourceSchema.SetAttribute{ | ||
MarkdownDescription: "Set of Tag labels", | ||
ElementType: types.StringType, | ||
Optional: true, | ||
Validators: []validator.Set{setvalidator.SizeAtLeast(1)}, | ||
}, | ||
} | ||
} | ||
|
||
func (o *FreeformConfigTemplate) Request(ctx context.Context, diags *diag.Diagnostics) *apstra.ConfigTemplateData { | ||
var tags []string | ||
diags.Append(o.Tags.ElementsAs(ctx, &tags, false)...) | ||
if diags.HasError() { | ||
return nil | ||
} | ||
|
||
return &apstra.ConfigTemplateData{ | ||
Label: o.Name.ValueString(), | ||
Text: o.Text.ValueString(), | ||
Tags: tags, | ||
} | ||
} | ||
|
||
func (o *FreeformConfigTemplate) LoadApiData(ctx context.Context, in *apstra.ConfigTemplateData, diags *diag.Diagnostics) { | ||
o.Name = types.StringValue(in.Label) | ||
o.Text = types.StringValue(in.Text) | ||
o.Tags = utils.SetValueOrNull(ctx, types.StringType, in.Tags, diags) // safe to ignore diagnostic here | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package blueprint | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net" | ||
"strings" | ||
|
||
"github.com/Juniper/apstra-go-sdk/apstra" | ||
"github.com/Juniper/terraform-provider-apstra/apstra/utils" | ||
"github.com/hashicorp/terraform-plugin-framework-nettypes/cidrtypes" | ||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator" | ||
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" | ||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" | ||
"github.com/hashicorp/terraform-plugin-framework/attr" | ||
dataSourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
type freeformEndpoint struct { | ||
InterfaceName types.String `tfsdk:"interface_name"` | ||
InterfaceId types.String `tfsdk:"interface_id"` | ||
TransformationId types.Int64 `tfsdk:"transformation_id"` | ||
Ipv4Address cidrtypes.IPv4Prefix `tfsdk:"ipv4_address"` | ||
Ipv6Address cidrtypes.IPv6Prefix `tfsdk:"ipv6_address"` | ||
Tags types.Set `tfsdk:"tags"` | ||
} | ||
|
||
func (o freeformEndpoint) attrTypes() map[string]attr.Type { | ||
return map[string]attr.Type{ | ||
"interface_name": types.StringType, | ||
"interface_id": types.StringType, | ||
"transformation_id": types.Int64Type, | ||
"ipv4_address": cidrtypes.IPv4PrefixType{}, | ||
"ipv6_address": cidrtypes.IPv6PrefixType{}, | ||
"tags": types.SetType{ElemType: types.StringType}, | ||
} | ||
} | ||
|
||
func (o freeformEndpoint) DatasourceAttributes() map[string]dataSourceSchema.Attribute { | ||
return map[string]dataSourceSchema.Attribute{ | ||
"interface_name": dataSourceSchema.StringAttribute{ | ||
Computed: true, | ||
MarkdownDescription: "The interface name, as found in the associated Device Profile, e.g. `xe-0/0/0`", | ||
}, | ||
"interface_id": dataSourceSchema.StringAttribute{ | ||
Computed: true, | ||
MarkdownDescription: "Graph node ID of the associated interface", | ||
}, | ||
"transformation_id": dataSourceSchema.Int64Attribute{ | ||
Computed: true, | ||
MarkdownDescription: "ID # of the transformation in the Device Profile", | ||
}, | ||
"ipv4_address": dataSourceSchema.StringAttribute{ | ||
Computed: true, | ||
MarkdownDescription: "Ipv4 address of the interface in CIDR notation", | ||
CustomType: cidrtypes.IPv4PrefixType{}, | ||
}, | ||
"ipv6_address": dataSourceSchema.StringAttribute{ | ||
Computed: true, | ||
MarkdownDescription: "Ipv6 address of the interface in CIDR notation", | ||
CustomType: cidrtypes.IPv6PrefixType{}, | ||
}, | ||
"tags": dataSourceSchema.SetAttribute{ | ||
MarkdownDescription: "Set of Tags applied to the interface", | ||
Computed: true, | ||
ElementType: types.StringType, | ||
}, | ||
} | ||
} | ||
|
||
func (o freeformEndpoint) ResourceAttributes() map[string]resourceSchema.Attribute { | ||
return map[string]resourceSchema.Attribute{ | ||
"interface_name": resourceSchema.StringAttribute{ | ||
Required: true, | ||
MarkdownDescription: "The interface name, as found in the associated Device Profile, e.g. `xe-0/0/0`", | ||
Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, | ||
}, | ||
"interface_id": resourceSchema.StringAttribute{ | ||
Computed: true, | ||
MarkdownDescription: "Graph node ID of the associated interface", | ||
}, | ||
"transformation_id": resourceSchema.Int64Attribute{ | ||
Required: true, | ||
MarkdownDescription: "ID # of the transformation in the Device Profile", | ||
Validators: []validator.Int64{int64validator.AtLeast(1)}, | ||
}, | ||
"ipv4_address": resourceSchema.StringAttribute{ | ||
Optional: true, | ||
MarkdownDescription: "Ipv4 address of the interface in CIDR notation", | ||
CustomType: cidrtypes.IPv4PrefixType{}, | ||
}, | ||
"ipv6_address": resourceSchema.StringAttribute{ | ||
Optional: true, | ||
MarkdownDescription: "Ipv6 address of the interface in CIDR notation", | ||
CustomType: cidrtypes.IPv6PrefixType{}, | ||
}, | ||
"tags": resourceSchema.SetAttribute{ | ||
MarkdownDescription: "Set of Tags applied to the interface", | ||
Optional: true, | ||
ElementType: types.StringType, | ||
Validators: []validator.Set{setvalidator.SizeAtLeast(1)}, | ||
}, | ||
} | ||
} | ||
|
||
func (o *freeformEndpoint) request(ctx context.Context, systemId string, diags *diag.Diagnostics) *apstra.FreeformEndpoint { | ||
var ipNet4, ipNet6 *net.IPNet | ||
if !o.Ipv4Address.IsNull() { | ||
var ip4 net.IP | ||
ip4, ipNet4, _ = net.ParseCIDR(o.Ipv4Address.ValueString()) | ||
ipNet4.IP = ip4 | ||
} | ||
if !o.Ipv6Address.IsNull() { | ||
var ip6 net.IP | ||
ip6, ipNet6, _ = net.ParseCIDR(o.Ipv6Address.ValueString()) | ||
ipNet6.IP = ip6 | ||
} | ||
|
||
var tags []string | ||
diags.Append(o.Tags.ElementsAs(ctx, &tags, false)...) | ||
|
||
return &apstra.FreeformEndpoint{ | ||
SystemId: apstra.ObjectId(systemId), | ||
Interface: apstra.FreeformInterface{ | ||
Data: &apstra.FreeformInterfaceData{ | ||
IfName: o.InterfaceName.ValueString(), | ||
TransformationId: int(o.TransformationId.ValueInt64()), | ||
Ipv4Address: ipNet4, | ||
Ipv6Address: ipNet6, | ||
Tags: tags, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (o *freeformEndpoint) loadApiData(ctx context.Context, in apstra.FreeformEndpoint, diags *diag.Diagnostics) { | ||
if in.Interface.Id == nil { | ||
diags.AddError( | ||
fmt.Sprintf("api returned nil interface Id for system %s", in.SystemId), | ||
"interface IDs should always be populated", | ||
) | ||
return | ||
} | ||
|
||
o.InterfaceName = types.StringValue(in.Interface.Data.IfName) | ||
o.InterfaceId = types.StringValue(in.Interface.Id.String()) | ||
o.TransformationId = types.Int64Value(int64(in.Interface.Data.TransformationId)) | ||
o.Ipv4Address = cidrtypes.NewIPv4PrefixValue(in.Interface.Data.Ipv4Address.String()) | ||
if strings.Contains(o.Ipv4Address.ValueString(), "nil") { | ||
o.Ipv4Address = cidrtypes.NewIPv4PrefixNull() | ||
} | ||
o.Ipv6Address = cidrtypes.NewIPv6PrefixValue(in.Interface.Data.Ipv6Address.String()) | ||
if strings.Contains(o.Ipv6Address.ValueString(), "nil") { | ||
o.Ipv6Address = cidrtypes.NewIPv6PrefixNull() | ||
} | ||
o.Tags = utils.SetValueOrNull(ctx, types.StringType, in.Interface.Data.Tags, diags) | ||
} | ||
|
||
func newFreeformEndpointMap(ctx context.Context, in [2]apstra.FreeformEndpoint, diags *diag.Diagnostics) types.Map { | ||
endpoints := make(map[string]freeformEndpoint, len(in)) | ||
for i := range in { | ||
var endpoint freeformEndpoint | ||
endpoint.loadApiData(ctx, in[i], diags) | ||
endpoints[in[i].SystemId.String()] = endpoint | ||
} | ||
if diags.HasError() { | ||
return types.MapNull(types.ObjectType{AttrTypes: freeformEndpoint{}.attrTypes()}) | ||
} | ||
|
||
return utils.MapValueOrNull(ctx, types.ObjectType{AttrTypes: freeformEndpoint{}.attrTypes()}, endpoints, diags) | ||
} |
Oops, something went wrong.