Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce data.apstra_datacenter_routing_zones #101

Merged
merged 5 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions apstra/blueprint/datacenter_routing_zone.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,50 @@ func (o DatacenterRoutingZone) DataSourceAttributes() map[string]dataSourceSchem
}
}

func (o DatacenterRoutingZone) DataSourceFilterAttributes() map[string]dataSourceSchema.Attribute {
return map[string]dataSourceSchema.Attribute{
"id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Not applicable in filter context. Ignore.",
Computed: true,
},
"blueprint_id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Not applicable in filter context. Ignore.",
Computed: true,
},
"name": dataSourceSchema.StringAttribute{
MarkdownDescription: "VRF name displayed in thw Apstra web UI.",
Optional: true,
},
"vlan_id": dataSourceSchema.Int64Attribute{
MarkdownDescription: "Used for VLAN tagged Layer 3 links on external connections.",
Optional: true,
},
"had_prior_vlan_id_config": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Not applicable in filter context. Ignore.",
Computed: true,
},
"vni": dataSourceSchema.Int64Attribute{
MarkdownDescription: "VxLAN VNI associated with the routing zone.",
Optional: true,
},
"had_prior_vni_config": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Not applicable in filter context. Ignore.",
Computed: true,
},
"dhcp_servers": dataSourceSchema.SetAttribute{
MarkdownDescription: "Set of addresses of DHCP servers (IPv4 or IPv6) which must be configured " +
"in the Routing Zone. This is a list of *required* servers, not an exact-match list.",
ElementType: types.StringType,
Optional: true,
},
"routing_policy_id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Non-EVPN blueprints must use the default policy, so this field must be null. " +
"Set this attribute in an EVPN blueprint to use a non-default policy.",
Optional: true,
},
}
}

func (o DatacenterRoutingZone) ResourceAttributes() map[string]resourceSchema.Attribute {
nameRE := regexp.MustCompile("^[A-Za-z0-9_-]+$")
return map[string]resourceSchema.Attribute{
Expand Down Expand Up @@ -227,3 +271,65 @@ func (o *DatacenterRoutingZone) LoadApiDhcpServers(ctx context.Context, IPs []ne
}
o.DhcpServers = utils.SetValueOrNull(ctx, types.StringType, dhcpServers, diags)
}

func (o *DatacenterRoutingZone) Query(szResultName, policyResultName string) *apstra.PathQuery {
query := new(apstra.PathQuery)

if utils.Known(o.RoutingPolicyId) {
query.Node([]apstra.QEEAttribute{
{Key: "type", Value: apstra.QEStringVal(apstra.NodeTypeRoutingPolicy.String())},
{Key: "id", Value: apstra.QEStringVal(o.RoutingPolicyId.ValueString())},
})
query.In([]apstra.QEEAttribute{
{Key: "type", Value: apstra.QEStringVal("policy")},
})
}

query.Node(o.szNodeQueryAttributes(szResultName))

if utils.Known(o.DhcpServers) {
query.Out([]apstra.QEEAttribute{
{Key: "type", Value: apstra.QEStringVal("policy")},
})
query.Node([]apstra.QEEAttribute{
{Key: "type", Value: apstra.QEStringVal(apstra.NodeTypePolicy.String())},
{Key: "policy_type", Value: apstra.QEStringVal("dhcp_relay")},
{Key: "name", Value: apstra.QEStringVal(policyResultName)},
})
}

for _, dhcpServerVal := range o.DhcpServers.Elements() {
query.Where("lambda " +
policyResultName +
": '" +
dhcpServerVal.(types.String).ValueString() +
"' in " +
policyResultName +
".dhcp_servers")
}
return query
}

func (o *DatacenterRoutingZone) szNodeQueryAttributes(name string) []apstra.QEEAttribute {
result := []apstra.QEEAttribute{
{Key: "type", Value: apstra.QEStringVal(apstra.NodeTypeSecurityZone.String())},
}

if name != "" {
result = append(result, apstra.QEEAttribute{Key: "name", Value: apstra.QEStringVal(name)})
}

if utils.Known(o.Name) {
result = append(result, apstra.QEEAttribute{Key: "label", Value: apstra.QEStringVal(o.Name.ValueString())})
}

if utils.Known(o.Vni) {
result = append(result, apstra.QEEAttribute{Key: "vni_id", Value: apstra.QEIntVal(int(o.Vni.ValueInt64()))})
}

if utils.Known(o.VlanId) {
result = append(result, apstra.QEEAttribute{Key: "vlan_id", Value: apstra.QEIntVal(int(o.VlanId.ValueInt64()))})
}

return result
}
12 changes: 6 additions & 6 deletions apstra/data_source_datacenter_routing_zone.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,28 @@ import (
"terraform-provider-apstra/apstra/blueprint"
)

var _ datasource.DataSourceWithConfigure = &dataSourceRoutingZone{}
var _ datasource.DataSourceWithConfigure = &dataSourceDatacenterRoutingZone{}

type dataSourceRoutingZone struct {
type dataSourceDatacenterRoutingZone struct {
client *apstra.Client
}

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

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

func (o *dataSourceRoutingZone) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
func (o *dataSourceDatacenterRoutingZone) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "This resource returns details of a Routing Zone within a Datacenter Blueprint.",
Attributes: blueprint.DatacenterRoutingZone{}.DataSourceAttributes(),
}
}

func (o *dataSourceRoutingZone) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
func (o *dataSourceDatacenterRoutingZone) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if o.client == nil {
resp.Diagnostics.AddError(errDataSourceUnconfiguredSummary, errDatasourceUnconfiguredDetail)
return
Expand Down
115 changes: 115 additions & 0 deletions apstra/data_source_datacenter_routing_zones.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package tfapstra

import (
"context"
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"terraform-provider-apstra/apstra/blueprint"
)

var _ datasource.DataSourceWithConfigure = &dataSourceDatacenterRoutingZones{}

type dataSourceDatacenterRoutingZones struct {
client *apstra.Client
}

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

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

func (o *dataSourceDatacenterRoutingZones) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "This data source returns the IDs of Routing Zones within the specified Blueprint. " +
"All of the `filters` attributes are optional.",
Attributes: map[string]schema.Attribute{
"blueprint_id": schema.StringAttribute{
MarkdownDescription: "Apstra Blueprint ID.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"ids": schema.SetAttribute{
MarkdownDescription: "Set of Routing Zone IDs",
Computed: true,
ElementType: types.StringType,
},
"filters": schema.SingleNestedAttribute{
MarkdownDescription: "Routing Zone attributes used as filters",
Optional: true,
Attributes: blueprint.DatacenterRoutingZone{}.DataSourceFilterAttributes(),
},
"graph_query": schema.StringAttribute{
MarkdownDescription: "The graph datastore query used to perform the lookup.",
Computed: true,
},
},
}
}

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

type routingZones struct {
BlueprintId types.String `tfsdk:"blueprint_id"`
IDs types.Set `tfsdk:"ids"`
Filters types.Object `tfsdk:"filters"`
Query types.String `tfsdk:"graph_query"`
}

var config routingZones
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

filters := &blueprint.DatacenterRoutingZone{}
d := config.Filters.As(ctx, &filters, basetypes.ObjectAsOptions{})
resp.Diagnostics.Append(d...)
if resp.Diagnostics.HasError() {
return
}

query := filters.Query("n_security_zone", "n_policy")

queryResponse := new(struct {
Items []struct {
SecurityZone struct {
Id string `json:"id"`
} `json:"n_security_zone"`
} `json:"items"`
})

query.
SetClient(o.client).
SetBlueprintId(apstra.ObjectId(config.BlueprintId.ValueString())).
SetBlueprintType(apstra.BlueprintTypeStaging)

err := query.Do(ctx, queryResponse)
if err != nil {
resp.Diagnostics.AddError("error querying graph datastore", err.Error())
return
}

ids := make([]attr.Value, len(queryResponse.Items))
for i, item := range queryResponse.Items {
ids[i] = types.StringValue(item.SecurityZone.Id)
}

config.IDs = types.SetValueMust(types.StringType, ids)
config.Query = types.StringValue(query.String())

// set state
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
}
3 changes: 2 additions & 1 deletion apstra/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
func() datasource.DataSource { return &dataSourceRackTypes{} },
//func() datasource.DataSource { return &dataSourceTemplateL3Collapsed{} },
//func() datasource.DataSource { return &dataSourceTemplatePodBased{}},
func() datasource.DataSource { return &dataSourceRoutingZone{} },
func() datasource.DataSource { return &dataSourceDatacenterRoutingZone{} },
func() datasource.DataSource { return &dataSourceDatacenterRoutingZones{} },
func() datasource.DataSource { return &dataSourceTemplateRackBased{} },
func() datasource.DataSource { return &dataSourceTemplates{} },
func() datasource.DataSource { return &dataSourceDatacenterBlueprint{} },
Expand Down
67 changes: 67 additions & 0 deletions docs/data-sources/datacenter_routing_zones.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
page_title: "apstra_datacenter_routing_zones Data Source - terraform-provider-apstra"
subcategory: ""
description: |-
This data source returns the IDs of Routing Zones within the specified Blueprint. All of the filters attributes are optional.
---

# apstra_datacenter_routing_zones (Data Source)

This data source returns the IDs of Routing Zones within the specified Blueprint. All of the `filters` attributes are optional.

## Example Usage

```terraform
# By specifying no filters, a wide search is performed. All routing
# zones in the blueprint will match.
data "apstra_datacenter_routing_zones" "all" {
blueprint_id = "05f9d3fc-671a-4efc-8e91-5ef87b2937d3"
}

# This example performs a very narrow search. Only one (or zero!)
# routing zones can match the resulting query.
data "apstra_datacenter_routing_zones" "rzs" {
blueprint_id = "05f9d3fc-671a-4efc-8e91-5ef87b2937d3"
filters = { # all filters are optional
"name" = "customer_1"
"vlan_id" = 55
"vni" = 10055
dhcp_servers = ["192.168.5.100", "192.168.10.100"]
routing_policy_id = "vqsv3F93MBHUgg5e8ws"
}
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `blueprint_id` (String) Apstra Blueprint ID.

### Optional

- `filters` (Attributes) Routing Zone attributes used as filters (see [below for nested schema](#nestedatt--filters))

### Read-Only

- `graph_query` (String) The graph datastore query used to perform the lookup.
- `ids` (Set of String) Set of Routing Zone IDs

<a id="nestedatt--filters"></a>
### Nested Schema for `filters`

Optional:

- `dhcp_servers` (Set of String) Set of DHCP server IPv4 or IPv6 addresses of DHCP servers which must be configured in the Routing Zone. This is a list of *required* servers, not an exact-match list.
- `name` (String) VRF name displayed in thw Apstra web UI.
- `routing_policy_id` (String) Non-EVPN blueprints must use the default policy, so this field must be null. Set this attribute in an EVPN blueprint to use a non-default policy.
- `vlan_id` (Number) Used for VLAN tagged Layer 3 links on external connections.
- `vni` (Number) VxLAN VNI associated with the routing zone.

Read-Only:

- `blueprint_id` (String) Not applicable in filter context. Ignore.
- `had_prior_vlan_id_config` (Boolean) Not applicable in filter context. Ignore.
- `had_prior_vni_config` (Boolean) Not applicable in filter context. Ignore.
- `id` (String) Not applicable in filter context. Ignore.
18 changes: 18 additions & 0 deletions examples/data-sources/apstra_datacenter_routing_zones/example.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# By specifying no filters, a wide search is performed. All routing
# zones in the blueprint will match.
data "apstra_datacenter_routing_zones" "all" {
blueprint_id = "05f9d3fc-671a-4efc-8e91-5ef87b2937d3"
}

# This example performs a very narrow search. Only one (or zero!)
# routing zones can match the resulting query.
data "apstra_datacenter_routing_zones" "rzs" {
blueprint_id = "05f9d3fc-671a-4efc-8e91-5ef87b2937d3"
filters = { # all filters are optional
"name" = "customer_1"
"vlan_id" = 55
"vni" = 10055
dhcp_servers = ["192.168.5.100", "192.168.10.100"]
routing_policy_id = "vqsv3F93MBHUgg5e8ws"
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
)

// HHMMSS
//replace github.com/Juniper/apstra-go-sdk => github.com/Juniper/apstra-go-sdk v0.0.0-20230524014058-55d8ff99df88
replace github.com/Juniper/apstra-go-sdk => github.com/Juniper/apstra-go-sdk v0.0.0-20230524182305-eec4bb728c2b

require (
github.com/Masterminds/goutils v1.1.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/IBM/netaddr v1.5.0 h1:IJlFZe1+nFs09TeMB/HOP4+xBnX2iM/xgiDOgZgTJq0=
github.com/IBM/netaddr v1.5.0/go.mod h1:DDBPeYgbFzoXHjSz9Jwk7K8wmWV4+a/Kv0LqRnb8we4=
github.com/Juniper/apstra-go-sdk v0.0.0-20230524142438-5959c7771c90 h1:1WWSwbXF9USUkB42MpVJwFuZqsYJ6DNETPxJ6x5F2bw=
github.com/Juniper/apstra-go-sdk v0.0.0-20230524142438-5959c7771c90/go.mod h1:PKQO0TF/UB7vSaD3KtIlNT6VlYSuJRfPVE2loT9C/lM=
github.com/Juniper/apstra-go-sdk v0.0.0-20230524182305-eec4bb728c2b h1:hwvmXqkH8Ka/tf4AHUh++Iu5rAs6uAg2qjSO6RkbaIc=
github.com/Juniper/apstra-go-sdk v0.0.0-20230524182305-eec4bb728c2b/go.mod h1:PKQO0TF/UB7vSaD3KtIlNT6VlYSuJRfPVE2loT9C/lM=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
Expand Down