Skip to content

Commit

Permalink
introduce datasource
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismarget-j committed Jul 19, 2023
1 parent d9da793 commit a1661ac
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 0 deletions.
84 changes: 84 additions & 0 deletions apstra/blueprint/datacenter_routing_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"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/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault"
Expand Down Expand Up @@ -132,6 +134,88 @@ func (o DatacenterRoutingPolicy) ResourceAttributes() map[string]resourceSchema.
}
}

func (o DatacenterRoutingPolicy) DataSourceAttributes() map[string]dataSourceSchema.Attribute {
nameRE := regexp.MustCompile("^[A-Za-z0-9_-]+$")
return map[string]dataSourceSchema.Attribute{
"id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Apstra graph node ID.",
Optional: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
stringvalidator.ExactlyOneOf(path.Expressions{
path.MatchRelative(),
path.MatchRoot("name"),
}...),
},
},
"name": dataSourceSchema.StringAttribute{
MarkdownDescription: "Web UI 'name' field.",
Optional: true,
Validators: []validator.String{
stringvalidator.LengthBetween(1, 18),
stringvalidator.RegexMatches(nameRE, "only underscore, dash and alphanumeric characters allowed."),
},
},
"description": dataSourceSchema.StringAttribute{
MarkdownDescription: "Web UI 'description' field.",
Computed: true,
},
"blueprint_id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Apstra Blueprint ID.",
Required: true,
},
"import_policy": dataSourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("One of '%s'",
strings.Join(utils.AllDcRoutingPolicyImportPolicy(), "', '")),
Computed: true,
},
"export_policy": dataSourceSchema.SingleNestedAttribute{
MarkdownDescription: "The export policy controls export of various types of fabric prefixes.",
Attributes: datacenterRoutingPolicyExport{}.dataSourceAttributes(),
Computed: true,
},
"expect_default_ipv4": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Default IPv4 route is expected to be imported via protocol session using this " +
"policy. Used for rendering route expectations.",
Computed: true,
},
"expect_default_ipv6": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Default IPv6 route is expected to be imported via protocol session using this " +
"policy. Used for rendering route expectations.",
Computed: true,
},
"aggregate_prefixes": dataSourceSchema.ListAttribute{
MarkdownDescription: "BGP Aggregate routes to be imported into a routing zone (VRF) on all border " +
"switches. This option can only be set on routing policies associated with routing zones, and cannot " +
"be set on per-connectivity point policies. The aggregated routes are sent to all external router " +
"peers in a SZ (VRF).",
Computed: true,
ElementType: types.StringType,
},
"extra_imports": dataSourceSchema.ListNestedAttribute{
MarkdownDescription: fmt.Sprintf("User defined import routes will be used in addition to any "+
"routes generated by the import policies. Prefixes specified here are additive to the import policy, "+
"unless 'import_policy' is set to %q, in which only these routes will be imported.",
apstra.DcRoutingPolicyImportPolicyExtraOnly),
Computed: true,
NestedObject: dataSourceSchema.NestedAttributeObject{
Attributes: prefixFilter{}.dataSourceAttributes(),
Validators: []validator.Object{prefixFilterValidator()},
},
},
"extra_exports": dataSourceSchema.ListNestedAttribute{
MarkdownDescription: "User defined export routes will be used in addition to any other routes specified " +
"in export policies. These policies are additive. To advertise only extra routes, disable all export " +
"types within 'export_policy', and only the extra prefixes specified here will be advertised.",
Computed: true,
NestedObject: dataSourceSchema.NestedAttributeObject{
Attributes: prefixFilter{}.dataSourceAttributes(),
Validators: []validator.Object{prefixFilterValidator()},
},
},
}
}

func (o *DatacenterRoutingPolicy) Request(ctx context.Context, diags *diag.Diagnostics) *apstra.DcRoutingPolicyData {
if o.ImportPolicy.IsUnknown() {
o.ImportPolicy = types.StringValue(apstra.DcRoutingPolicyImportPolicyDefaultOnly.String())
Expand Down
34 changes: 34 additions & 0 deletions apstra/blueprint/datacenter_routing_policy_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"github.com/Juniper/apstra-go-sdk/apstra"
"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/resource/schema/booldefault"
Expand Down Expand Up @@ -64,6 +65,39 @@ func (o datacenterRoutingPolicyExport) resourceAttributes() map[string]resourceS
}
}

func (o datacenterRoutingPolicyExport) dataSourceAttributes() map[string]dataSourceSchema.Attribute {
return map[string]dataSourceSchema.Attribute{
"export_loopbacks": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Exports all loopbacks within a routing zone (VRF) across spine, leaf, and L3 servers.",
Computed: true,
},
"export_spine_superspine_links": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Exports all spine-leaf (fabric) links within a VRF. EVPN routing zones do not have " +
"spine-leaf addressing, so this generated list may be empty. For routing zones of type Virtual L3 " +
"Fabric, subinterfaces between spine-leaf will be included.",
Computed: true,
},
"export_spine_leaf_links": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Exports all spine-supersine (fabric) links within the default routing zone (VRF)",
Computed: true,
},
"export_l3_edge_server_links": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Exports all leaf to L3 server links within a routing zone (VRF). This will be an " +
"empty list on a layer2 based blueprint",
Computed: true,
},
"export_l2_edge_subnets": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Exports all virtual networks (VLANs) that have L3 addresses within a routing zone (VRF).",
Computed: true,
},
"export_static_routes": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Exports all subnets in a VRF associated with static routes from all fabric systems " +
"to external routers associated with this routing policy",
Computed: true,
},
}
}

func (o datacenterRoutingPolicyExport) attrTypes() map[string]attr.Type {
return map[string]attr.Type{
"export_loopbacks": types.BoolType,
Expand Down
33 changes: 33 additions & 0 deletions apstra/blueprint/prefix_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"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/resource/schema/stringdefault"
Expand Down Expand Up @@ -62,6 +63,38 @@ func (o prefixFilter) resourceAttributes() map[string]resourceSchema.Attribute {
}
}

func (o prefixFilter) dataSourceAttributes() map[string]dataSourceSchema.Attribute {
return map[string]dataSourceSchema.Attribute{
"prefix": dataSourceSchema.StringAttribute{
MarkdownDescription: "IPv4 or IPv6 network address specified in the form of network/prefixlen.",
Computed: true,
},
"ge_mask": dataSourceSchema.Int64Attribute{
MarkdownDescription: "Match less-specific prefixes from a parent prefix, up from `ge_mask` to the prefix " +
"length of the route. Range is 0-32 for IPv4, 0-128 for IPv6. If not specified, implies the " +
"prefix-list entry should be an exact match. The option can be optionally be used in combination " +
"with `le_mask`. `ge_mask` must be longer than the subnet prefix length. If `le_mask` and `ge_mask` " +
"are both specified, then `le_mask` must be greater than `ge_mask`.",
Computed: true,
},
"le_mask": dataSourceSchema.Int64Attribute{
MarkdownDescription: "Match more-specific prefixes from a parent prefix, up until `le_mask` prefix len. " +
"Range is 0-32 for IPv4, 0-128 for IPv6. If not specified, implies the prefix-list entry should be " +
"an exact match. The option can be optionally be used in combination with `ge_mask`. `le_mask` must " +
"be longer than the subnet prefix length. If `le_mask` and `ge_mask` are both specified, then " +
"`le_mask` must be greater than `ge_mask`.",
Computed: true,
},
"action": dataSourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("If the action is %q, match the route. If the action is %q, do "+
"not match the route. For composing complex policies, all prefix-list items will be processed in the "+
"order specified, top-down. This allows the user to deny a subset of a route that may otherwise be "+
"permitted.", apstra.PrefixFilterActionPermit, apstra.PrefixFilterActionDeny),
Computed: true,
},
}
}

func (o prefixFilter) attrTypes() map[string]attr.Type {
return map[string]attr.Type{
"prefix": types.StringType,
Expand Down
100 changes: 100 additions & 0 deletions apstra/data_source_datacenter_routing_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package tfapstra

import (
"context"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
"terraform-provider-apstra/apstra/blueprint"
"terraform-provider-apstra/apstra/utils"
)

var _ datasource.DataSourceWithConfigure = &dataSourceDatacenterRoutingPolicy{}

type dataSourceDatacenterRoutingPolicy struct {
client *apstra.Client
}

func (o *dataSourceDatacenterRoutingPolicy) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_datacenter_routing_policy"
}

func (o *dataSourceDatacenterRoutingPolicy) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
o.client = DataSourceGetClient(ctx, req, resp)
}

func (o *dataSourceDatacenterRoutingPolicy) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "This resource returns details of a Datacenter Routing Policy.",
Attributes: blueprint.DatacenterRoutingPolicy{}.DataSourceAttributes(),
}
}

func (o *dataSourceDatacenterRoutingPolicy) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if o.client == nil {
resp.Diagnostics.AddError(errDataSourceUnconfiguredSummary, errDatasourceUnconfiguredDetail)
return
}

// Retrieve values from config.
var config blueprint.DatacenterRoutingPolicy
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

bpClient, err := o.client.NewTwoStageL3ClosClient(ctx, apstra.ObjectId(config.BlueprintId.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddError(fmt.Sprintf("blueprint %s not found",
config.BlueprintId), err.Error())
return
}
resp.Diagnostics.AddError("failed to create blueprint client", err.Error())
return
}

var api *apstra.DcRoutingPolicy
switch {
case !config.Id.IsNull():
api, err = bpClient.GetRoutingPolicy(ctx, apstra.ObjectId(config.Id.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("id"),
"Routing Policy not found",
fmt.Sprintf("Routing Policy with ID %s not found", config.Id))
return
}
resp.Diagnostics.AddError(
"Failed reading Routing Policy", err.Error(),
)
}
case !config.Name.IsNull():
api, err = bpClient.GetRoutingPolicyByName(ctx, config.Name.ValueString())
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("name"),
"Routing Policy not found",
fmt.Sprintf("Routing Policy with Name %s not found", config.Name))
return
}
resp.Diagnostics.AddError(
"Failed reading Routing Policy", err.Error(),
)
}
}

config.Id = types.StringValue(api.Id.String())
config.LoadApiData(ctx, api.Data, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

// set state
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
}
1 change: 1 addition & 0 deletions apstra/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
func() datasource.DataSource { return &dataSourceDatacenterBlueprint{} },
func() datasource.DataSource { return &dataSourceDatacenterPropertySet{} },
func() datasource.DataSource { return &dataSourceDatacenterPropertySets{} },
func() datasource.DataSource { return &dataSourceDatacenterRoutingPolicy{} },
func() datasource.DataSource { return &dataSourceDatacenterRoutingZone{} },
func() datasource.DataSource { return &dataSourceDatacenterRoutingZones{} },
func() datasource.DataSource { return &dataSourceDatacenterSystemNode{} },
Expand Down

0 comments on commit a1661ac

Please sign in to comment.