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

Deprecate filter; add filters attribute in apstra_datacenter_virtual_networks data source #454

Merged
merged 2 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion apstra/apstra_validator/at_most_n_of.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (o AtMostNOfValidator) Validate(ctx context.Context, req AtMostNOfValidator

resp.Diagnostics.Append(validatordiag.InvalidAttributeCombinationDiagnostic(
req.Path,
fmt.Sprintf("At most %d attributes out of %s must be specified, but %d matches were found",
fmt.Sprintf("At most %d attributes out of %s may be specified, but %d non-null attributes were found",
req.N, expressions, len(notNullPaths)),
))
}
Expand Down
16 changes: 0 additions & 16 deletions apstra/blueprint/datacenter_virtual_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,22 +162,6 @@ func (o DatacenterVirtualNetwork) DataSourceFilterAttributes() map[string]dataSo
"name": dataSourceSchema.StringAttribute{
MarkdownDescription: "Virtual Network Name",
Optional: true,
Validators: []validator.String{stringvalidator.AtLeastOneOf(
path.MatchRelative(),
path.MatchRoot("filter").AtName("type"),
path.MatchRoot("filter").AtName("routing_zone_id"),
path.MatchRoot("filter").AtName("vni"),
path.MatchRoot("filter").AtName("reserve_vlan"),
path.MatchRoot("filter").AtName("dhcp_service_enabled"),
path.MatchRoot("filter").AtName("ipv4_connectivity_enabled"),
path.MatchRoot("filter").AtName("ipv6_connectivity_enabled"),
path.MatchRoot("filter").AtName("ipv4_subnet"),
path.MatchRoot("filter").AtName("ipv6_subnet"),
path.MatchRoot("filter").AtName("ipv4_virtual_gateway_enabled"),
path.MatchRoot("filter").AtName("ipv6_virtual_gateway_enabled"),
path.MatchRoot("filter").AtName("ipv4_virtual_gateway"),
path.MatchRoot("filter").AtName("ipv6_virtual_gateway"),
)},
},
"type": dataSourceSchema.StringAttribute{
MarkdownDescription: "Virtual Network Type",
Expand Down
128 changes: 101 additions & 27 deletions apstra/data_source_datacenter_virtual_networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"context"
"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/blueprint"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
Expand Down Expand Up @@ -52,13 +54,49 @@ func (o *dataSourceDatacenterVirtualNetworks) Schema(_ context.Context, _ dataso
"one filter attribute must be included when this attribute is used.",
Optional: true,
Attributes: blueprint.DatacenterVirtualNetwork{}.DataSourceFilterAttributes(),
Validators: []validator.Object{
apstravalidator.AtMostNOf(1,
path.MatchRelative(),
path.MatchRoot("filters"),
),
apstravalidator.AtLeastNAttributes(
1,
"name", "type", "routing_zone_id", "vni", "reserve_vlan", "dhcp_service_enabled",
"ipv4_connectivity_enabled", "ipv6_connectivity_enabled", "ipv4_subnet", "ipv6_subnet",
"ipv4_virtual_gateway_enabled", "ipv6_virtual_gateway_enabled", "ipv4_virtual_gateway",
"ipv6_virtual_gateway",
),
},
DeprecationMessage: "The `filter` attribute is deprecated and will be removed in a future " +
"release. Please migrate your configuration to use `filters` instead.",
},
"graph_query": schema.StringAttribute{
"filters": schema.ListNestedAttribute{
MarkdownDescription: "List of filters used to select only desired node IDs. For a node " +
"to match a filter, all specified attributes must match (each attribute within a " +
"filter is AND-ed together). The returned node IDs represent the nodes matched by " +
"all of the filters together (filters are OR-ed together).",
Optional: true,
Validators: []validator.List{listvalidator.SizeAtLeast(1)},
NestedObject: schema.NestedAttributeObject{
Attributes: blueprint.DatacenterVirtualNetwork{}.DataSourceFilterAttributes(),
Validators: []validator.Object{
apstravalidator.AtLeastNAttributes(
1,
"name", "type", "routing_zone_id", "vni", "reserve_vlan", "dhcp_service_enabled",
"ipv4_connectivity_enabled", "ipv6_connectivity_enabled", "ipv4_subnet", "ipv6_subnet",
"ipv4_virtual_gateway_enabled", "ipv6_virtual_gateway_enabled", "ipv4_virtual_gateway",
"ipv6_virtual_gateway",
),
},
},
},
"graph_queries": schema.ListAttribute{
MarkdownDescription: "The graph datastore query based on `filter` used to " +
"perform the lookup. Note that the `ipv6_subnet` and `ipv6_gateway` " +
"attributes are never part of the graph query because IPv6 zero " +
"compression rules make string matches unreliable.",
Computed: true,
ElementType: types.StringType,
Computed: true,
},
},
}
Expand All @@ -69,7 +107,8 @@ func (o *dataSourceDatacenterVirtualNetworks) Read(ctx context.Context, req data
BlueprintId types.String `tfsdk:"blueprint_id"`
IDs types.Set `tfsdk:"ids"`
Filter types.Object `tfsdk:"filter"`
Query types.String `tfsdk:"graph_query"`
Filters types.List `tfsdk:"filters"`
Queries types.List `tfsdk:"graph_queries"`
}

var config virtualNetworks
Expand All @@ -78,29 +117,43 @@ func (o *dataSourceDatacenterVirtualNetworks) Read(ctx context.Context, req data
return
}

var ids []attr.Value
var query *apstra.MatchQuery // todo change to interface after SDK update
bpId := apstra.ObjectId(config.BlueprintId.ValueString())
if config.Filter.IsNull() {
if config.Filter.IsNull() && config.Filters.IsNull() {
// just pull the VN IDs via API when no filter is specified
ids = o.getAllVnIds(ctx, bpId, &resp.Diagnostics)
ids := o.getAllVnIds(ctx, bpId, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

// set the state
config.IDs = types.SetValueMust(types.StringType, ids)
} else {
// use a graph query (and some IPv6 value matching)
filter := blueprint.DatacenterVirtualNetwork{}
config.Queries = types.ListNull(types.StringType)
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
return
}

var filters []blueprint.DatacenterVirtualNetwork
if !config.Filter.IsNull() {
var filter blueprint.DatacenterVirtualNetwork
resp.Diagnostics.Append(config.Filter.As(ctx, &filter, basetypes.ObjectAsOptions{})...)
if resp.Diagnostics.HasError() {
return
}

ids, query = o.getFilteredVnIds(ctx, bpId, filter, &resp.Diagnostics)
config.IDs = types.SetValueMust(types.StringType, ids)
config.Query = types.StringValue(query.String())
filters = append(filters, filter)
}
if resp.Diagnostics.HasError() {
return

if !config.Filters.IsNull() {
resp.Diagnostics.Append(config.Filters.ElementsAs(ctx, &filters, false)...)
if resp.Diagnostics.HasError() {
return
}
}

ids, queries := o.getVnIdsWithFilters(ctx, bpId, filters, &resp.Diagnostics)
config.IDs = types.SetValueMust(types.StringType, ids)
config.Queries = types.ListValueMust(types.StringType, queries)

// set the state
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
}
Expand Down Expand Up @@ -131,8 +184,32 @@ func (o *dataSourceDatacenterVirtualNetworks) getAllVnIds(ctx context.Context, b
return result
}

// todo change returned query to interface after SDK update
func (o *dataSourceDatacenterVirtualNetworks) getFilteredVnIds(ctx context.Context, bpId apstra.ObjectId, filter blueprint.DatacenterVirtualNetwork, diags *diag.Diagnostics) ([]attr.Value, *apstra.MatchQuery) {
func (o *dataSourceDatacenterVirtualNetworks) getVnIdsWithFilters(ctx context.Context, bpId apstra.ObjectId, filters []blueprint.DatacenterVirtualNetwork, diags *diag.Diagnostics) ([]attr.Value, []attr.Value) {
queries := make([]attr.Value, len(filters))
resultMap := make(map[string]bool)
for i, filter := range filters {
ids, query := o.getVnIdsWithFilter(ctx, bpId, filter, diags)
if diags.HasError() {
return nil, nil
}

queries[i] = types.StringValue(query.String())
for _, id := range ids {
resultMap[id] = true
}
}

ids := make([]attr.Value, len(resultMap))
var i int
for id := range resultMap {
ids[i] = types.StringValue(id)
i++
}

return ids, queries
}

func (o *dataSourceDatacenterVirtualNetworks) getVnIdsWithFilter(ctx context.Context, bpId apstra.ObjectId, filter blueprint.DatacenterVirtualNetwork, diags *diag.Diagnostics) ([]string, apstra.QEQuery) {
query := filter.Query("n_virtual_network")
queryResponse := new(struct {
Items []struct {
Expand All @@ -145,13 +222,10 @@ func (o *dataSourceDatacenterVirtualNetworks) getFilteredVnIds(ctx context.Conte
})

// todo remove this type assertion when QEQuery is extended with new methods used below
query2 := query.(*apstra.MatchQuery)
query2.
SetClient(o.client).
SetBlueprintId(bpId).
SetBlueprintType(apstra.BlueprintTypeStaging)

err := query2.Do(ctx, queryResponse)
query.(*apstra.MatchQuery).SetClient(o.client)
query.(*apstra.MatchQuery).SetBlueprintId(bpId)
query.(*apstra.MatchQuery).SetBlueprintType(apstra.BlueprintTypeStaging)
err := query.Do(ctx, queryResponse)
if err != nil {
diags.AddError("error querying graph datastore", err.Error())
return nil, nil
Expand Down Expand Up @@ -218,10 +292,10 @@ func (o *dataSourceDatacenterVirtualNetworks) getFilteredVnIds(ctx context.Conte
}
}

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

return result, query2
return result, query
}
49 changes: 42 additions & 7 deletions docs/data-sources/datacenter_virtual_networks.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ data "apstra_datacenter_virtual_networks" "all" {
# )
data "apstra_datacenter_virtual_networks" "prod_unreserved_with_dhcp" {
blueprint_id = "b726704d-f80e-4733-9103-abd6ccd8752c"
filter = {
reserve_vlan = false
dhcp_service_enabled = true
routing_zone_id = apstra_datacenter_routing_zone.prod.id
}
filters = [
{
reserve_vlan = false
dhcp_service_enabled = true
routing_zone_id = "Zplm0niOFCCCfjaXkXo"
}
]
}
```

Expand All @@ -49,11 +51,12 @@ data "apstra_datacenter_virtual_networks" "prod_unreserved_with_dhcp" {

### Optional

- `filter` (Attributes) Virtual Network attributes used as filter. At least one filter attribute must be included when this attribute is used. (see [below for nested schema](#nestedatt--filter))
- `filter` (Attributes, Deprecated) Virtual Network attributes used as filter. At least one filter attribute must be included when this attribute is used. (see [below for nested schema](#nestedatt--filter))
- `filters` (Attributes List) List of filters used to select only desired node IDs. For a node to match a filter, all specified attributes must match (each attribute within a filter is AND-ed together). The returned node IDs represent the nodes matched by all of the filters together (filters are OR-ed together). (see [below for nested schema](#nestedatt--filters))

### Read-Only

- `graph_query` (String) The graph datastore query based on `filter` used to perform the lookup. Note that the `ipv6_subnet` and `ipv6_gateway` attributes are never part of the graph query because IPv6 zero compression rules make string matches unreliable.
- `graph_queries` (List of String) The graph datastore query based on `filter` used to perform the lookup. Note that the `ipv6_subnet` and `ipv6_gateway` attributes are never part of the graph query because IPv6 zero compression rules make string matches unreliable.
- `ids` (Set of String) Set of Virtual Network IDs

<a id="nestedatt--filter"></a>
Expand Down Expand Up @@ -85,3 +88,35 @@ Read-Only:

<a id="nestedatt--filter--bindings"></a>
### Nested Schema for `filter.bindings`



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

Optional:

- `dhcp_service_enabled` (Boolean) Enables a DHCP relay agent.
- `ipv4_connectivity_enabled` (Boolean) Enables IPv4 within the Virtual Network.
- `ipv4_subnet` (String) IPv4 subnet associated with the Virtual Network.
- `ipv4_virtual_gateway` (String) Specifies the IPv4 virtual gateway address within the Virtual Network.
- `ipv4_virtual_gateway_enabled` (Boolean) Controls and indicates whether the IPv4 gateway within the Virtual Network is enabled.
- `ipv6_connectivity_enabled` (Boolean) Enables IPv6 within the Virtual Network.
- `ipv6_subnet` (String) IPv6 subnet associated with the Virtual Network. Note that this attribute will not appear in the `graph_query` output because IPv6 zero compression rules are problematic for mechanisms which rely on string matching.
- `ipv6_virtual_gateway` (String) Specifies the IPv6 virtual gateway address within the Virtual Network. Note that this attribute will not appear in the `graph_query` output because IPv6 zero compression rules are problematic for mechanisms which rely on string matching.
- `ipv6_virtual_gateway_enabled` (Boolean) Controls and indicates whether the IPv6 gateway within the Virtual Network is enabled.
- `name` (String) Virtual Network Name
- `reserve_vlan` (Boolean) For use only with `vxlan` type Virtual networks when all `bindings` use the same VLAN ID. This option reserves the VLAN fabric-wide, even on switches to which the Virtual Network has not yet been deployed.
- `routing_zone_id` (String) Routing Zone ID (required when `type == vxlan`
- `type` (String) Virtual Network Type
- `vni` (Number) EVPN Virtual Network ID to be associated with this Virtual Network.

Read-Only:

- `bindings` (Attributes Map) Not applicable in filter context. Ignore. (see [below for nested schema](#nestedatt--filters--bindings))
- `blueprint_id` (String) 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.

<a id="nestedatt--filters--bindings"></a>
### Nested Schema for `filters.bindings`
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ data "apstra_datacenter_virtual_networks" "all" {
# )
data "apstra_datacenter_virtual_networks" "prod_unreserved_with_dhcp" {
blueprint_id = "b726704d-f80e-4733-9103-abd6ccd8752c"
filter = {
reserve_vlan = false
dhcp_service_enabled = true
routing_zone_id = apstra_datacenter_routing_zone.prod.id
}
filters = [
{
reserve_vlan = false
dhcp_service_enabled = true
routing_zone_id = "Zplm0niOFCCCfjaXkXo"
}
]
}