From 2b81d3179a0b7ccc094d29f016ea5d08cc611a89 Mon Sep 17 00:00:00 2001 From: JchhatbarInfoblox Date: Thu, 9 May 2024 11:43:00 +0530 Subject: [PATCH] Added data source for ipv6 network, ipv6 network container and host record. --- infoblox/datasource_infoblox_host_record.go | 176 ++++++++++++++++++ .../datasource_infoblox_host_record_test.go | 118 ++++++++++++ ...asource_infoblox_ipv6_network_container.go | 124 ++++++++++++ infoblox/datasource_infoblox_network.go | 92 ++++++++- infoblox/datasource_infoblox_network_test.go | 46 +++++ infoblox/provider.go | 3 + 6 files changed, 555 insertions(+), 4 deletions(-) create mode 100644 infoblox/datasource_infoblox_host_record.go create mode 100644 infoblox/datasource_infoblox_host_record_test.go create mode 100644 infoblox/datasource_infoblox_ipv6_network_container.go diff --git a/infoblox/datasource_infoblox_host_record.go b/infoblox/datasource_infoblox_host_record.go new file mode 100644 index 000000000..cafb4c0a5 --- /dev/null +++ b/infoblox/datasource_infoblox_host_record.go @@ -0,0 +1,176 @@ +package infoblox + +import ( + "context" + "encoding/json" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + ibclient "github.com/infobloxopen/infoblox-go-client/v2" + "strconv" + "time" +) + +func dataSourceHostRecord() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceHostRecordRead, + Schema: map[string]*schema.Schema{ + "filters": { + Type: schema.TypeMap, + Required: true, + }, + + "results": { + Type: schema.TypeList, + Computed: true, + Description: "List of Host records matching filters", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "dns_view": { + Type: schema.TypeString, + Optional: true, + Default: defaultDNSView, + Description: "DNS view under which the zone has been created.", + }, + "fqdn": { + Type: schema.TypeString, + Required: true, + Description: "The host name for Host Record in FQDN format.", + }, + "ipv4_addr": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "IPv4 address of host record.", + }, + "ipv6_addr": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "IPv6 address of host record.", + }, + "zone": { + Type: schema.TypeString, + Computed: true, + Description: "The zone which the record belongs to.", + }, + "ttl": { + Type: schema.TypeInt, + Computed: true, + Description: "TTL attribute value for the record.", + }, + "comment": { + Type: schema.TypeString, + Computed: true, + Description: "Description of the Host-record.", + }, + "ext_attrs": { + Type: schema.TypeString, + Computed: true, + Description: "Extensible attributes of the Host-record, as a map in JSON format", + }, + }, + }, + }, + }, + } +} + +func dataSourceHostRecordRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + connector := m.(ibclient.IBConnector) + + var diags diag.Diagnostics + + n := &ibclient.HostRecord{} + n.SetReturnFields(append(n.ReturnFields(), "extattrs", "comment", "zone", "ttl")) + + filters := filterFromMap(d.Get("filters").(map[string]interface{})) + qp := ibclient.NewQueryParams(false, filters) + var res []ibclient.HostRecord + + err := connector.GetObject(n, "", qp, &res) + if err != nil { + return diag.FromErr(fmt.Errorf("failed getting Host-record: %s", err.Error())) + } + + if res == nil { + return diag.FromErr(fmt.Errorf("API returns a nil/empty ID for the Host Record")) + } + + // TODO: temporary scaffold, need to rework marshalling/unmarshalling of EAs + // (avoiding additional layer of keys ("value" key) + results := make([]interface{}, 0, len(res)) + for _, r := range res { + recordaFlat, err := flattenRecordHost(r) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to flatten Host Record: %w", err)) + } + + results = append(results, recordaFlat) + } + + err = d.Set("results", results) + if err != nil { + return diag.FromErr(err) + } + + // always run + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + + return diags +} + +func flattenRecordHost(hostRecord ibclient.HostRecord) (map[string]interface{}, error) { + var eaMap map[string]interface{} + if hostRecord.Ea != nil && len(hostRecord.Ea) > 0 { + eaMap = hostRecord.Ea + } else { + eaMap = make(map[string]interface{}) + } + + ea, err := json.Marshal(eaMap) + if err != nil { + return nil, err + } + + res := map[string]interface{}{ + "id": hostRecord.Ref, + "zone": hostRecord.Zone, + "dns_view": hostRecord.View, + "ext_attrs": string(ea), + } + + if hostRecord.Ipv4Addrs != nil { + res["ipv4_addr"] = hostRecord.Ipv4Addrs[0].Ipv4Addr + } + if hostRecord.Ipv6Addrs != nil { + res["ipv6_addr"] = hostRecord.Ipv6Addrs[0].Ipv6Addr + } + + if hostRecord.UseTtl != nil { + if !*hostRecord.UseTtl { + res["ttl"] = ttlUndef + } + } + + if hostRecord.Ttl != nil && *hostRecord.Ttl > 0 { + res["ttl"] = *hostRecord.Ttl + } else { + res["ttl"] = ttlUndef + } + + if hostRecord.Name != nil { + res["fqdn"] = *hostRecord.Name + } + + if hostRecord.Comment != nil { + res["comment"] = *hostRecord.Comment + } + + return res, nil + +} diff --git a/infoblox/datasource_infoblox_host_record_test.go b/infoblox/datasource_infoblox_host_record_test.go new file mode 100644 index 000000000..921ab6ce5 --- /dev/null +++ b/infoblox/datasource_infoblox_host_record_test.go @@ -0,0 +1,118 @@ +package infoblox + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "regexp" + "testing" +) + +func TestAccDataSourceHostRecordRead_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceHostRecordReadConfig_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.infoblox_host_record.test", "id"), + resource.TestCheckResourceAttr("data.infoblox_host_record.test", "results.0.fqdn", "testhostnameip1.test.com"), + ), + }, + }, + }) +} + +func TestAccDataSourceHostRecordRead_noResult(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceHostRecordReadConfig_noResult(), + ExpectError: regexp.MustCompile("failed getting Host-record: not found"), + }, + }, + }) +} + +func testAccDataSourceHostRecordReadConfig_basic() string { + return ` +resource "infoblox_zone_auth" "zone" { + fqdn = "test.com" +} +resource "infoblox_ip_allocation" "foo1"{ + network_view="default" + dns_view = "default" + fqdn="testhostnameip1.test.com" + ipv6_addr="2001:db8:abcd:12::1" + ipv4_addr="10.0.0.1" + ttl = 10 + comment = "IPv4 and IPv6 are allocated" + ext_attrs = jsonencode({ + Site = "Test site" + }) + depends_on = [infoblox_zone_auth.zone] + } + +data "infoblox_host_record" "test" { + filters = { + name = infoblox_ip_allocation.foo1.fqdn + } + depends_on = [infoblox_ip_allocation.foo1] +} +` +} + +func testAccDataSourceHostRecordReadConfig_noResult() string { + return ` +data "infoblox_host_record" "test" { + filters = { + name = "nonexistent.example.com" + } +} +` +} + +func TestAccDataSourceHostRecordRead_noIPv4Address(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceHostRecordReadConfig_noIPv4Address(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.infoblox_host_record.test", "id"), + resource.TestCheckResourceAttr("data.infoblox_host_record.test", "results.0.ipv4_addr", ""), + resource.TestCheckResourceAttr("data.infoblox_host_record.test", "results.0.ipv6_addr", "2001:db8:abcd:12::1"), + ), + }, + }, + }) +} + +func testAccDataSourceHostRecordReadConfig_noIPv4Address() string { + return ` +resource "infoblox_zone_auth" "zone" { + fqdn = "test.com" +} +resource "infoblox_ip_allocation" "foo1"{ + network_view="default" + dns_view = "default" + fqdn="testhostnameip1.test.com" + ipv6_addr="2001:db8:abcd:12::1" + ttl = 10 + comment = "IPv6 allocated" + ext_attrs = jsonencode({ + Site = "Test site" + }) + depends_on = [infoblox_zone_auth.zone] + } + +data "infoblox_host_record" "test" { + filters = { + name = infoblox_ip_allocation.foo1.fqdn + } + depends_on = [infoblox_ip_allocation.foo1] +} +` +} diff --git a/infoblox/datasource_infoblox_ipv6_network_container.go b/infoblox/datasource_infoblox_ipv6_network_container.go new file mode 100644 index 000000000..781667595 --- /dev/null +++ b/infoblox/datasource_infoblox_ipv6_network_container.go @@ -0,0 +1,124 @@ +package infoblox + +import ( + "context" + "encoding/json" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + ibclient "github.com/infobloxopen/infoblox-go-client/v2" +) + +func dataSourceIpv6NetworkContainer() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceIpv6NetworkContainerRead, + Schema: map[string]*schema.Schema{ + "filters": { + Type: schema.TypeMap, + Required: true, + }, + + "results": { + Type: schema.TypeList, + Computed: true, + Description: "List of networks matching filters.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "network_view": { + Type: schema.TypeString, + Optional: true, + Default: defaultNetView, + Description: "Newtwork view's name the network container belongs to.", + }, + "cidr": { + Type: schema.TypeString, + Required: true, + Description: "The CIDR value of the network container.", + }, + "comment": { + Type: schema.TypeString, + Computed: true, + Description: "Network container's description.", + }, + "ext_attrs": { + Type: schema.TypeString, + Computed: true, + Description: "The Extensible attributes for the network container.", + }, + }, + }, + }, + }, + } +} + +func dataSourceIpv6NetworkContainerRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + connector := m.(ibclient.IBConnector) + + n := &ibclient.Ipv6NetworkContainer{} + n.SetReturnFields(append(n.ReturnFields(), "extattrs")) + + filters := filterFromMap(d.Get("filters").(map[string]interface{})) + qp := ibclient.NewQueryParams(false, filters) + var res []ibclient.Ipv6NetworkContainer + + err := connector.GetObject(n, "", qp, &res) + if err != nil { + return diag.FromErr(fmt.Errorf("getting NetworkContainer failed : %w", err)) + } + + // TODO: temporary scaffold, need to rework marshalling/unmarshalling of EAs + // (avoiding additional layer of keys ("value" key) + results := make([]interface{}, 0, len(res)) + for _, nc := range res { + networkContainerFlat, err := flattenIpv6NetworkContainer(nc) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to flatten network container: %w", err)) + } + + results = append(results, networkContainerFlat) + } + + err = d.Set("results", results) + if err != nil { + return diag.FromErr(err) + } + + // always run + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + + return nil +} + +func flattenIpv6NetworkContainer(nc ibclient.Ipv6NetworkContainer) (map[string]interface{}, error) { + var eaMap map[string]interface{} + if nc.Ea != nil && len(nc.Ea) > 0 { + eaMap = nc.Ea + } else { + eaMap = make(map[string]interface{}) + } + ea, err := json.Marshal(eaMap) + if err != nil { + return nil, err + } + + res := map[string]interface{}{ + "id": nc.Ref, + "network_view": nc.NetworkView, + "cidr": nc.Network, + "ext_attrs": string(ea), + } + + if nc.Comment != nil { + res["comment"] = *nc.Comment + } + + return res, nil +} diff --git a/infoblox/datasource_infoblox_network.go b/infoblox/datasource_infoblox_network.go index ec9457687..6c52b87d6 100644 --- a/infoblox/datasource_infoblox_network.go +++ b/infoblox/datasource_infoblox_network.go @@ -11,9 +11,9 @@ import ( "time" ) -func dataSourceIPv4Network() *schema.Resource { +func dataSourceNetwork() *schema.Resource { return &schema.Resource{ - ReadContext: dataSourceIPv4NetworkRead, + //ReadContext: dataSourceIPv4NetworkRead, Schema: map[string]*schema.Schema{ "filters": { Type: schema.TypeMap, @@ -80,7 +80,7 @@ func dataSourceIPv4NetworkRead(ctx context.Context, d *schema.ResourceData, m in // (avoiding additional layer of keys ("value" key) results := make([]interface{}, 0, len(res)) for _, n := range res { - networkFlat, err := flattenNetwork(n) + networkFlat, err := flattenIpv4Network(n) if err != nil { return diag.FromErr(fmt.Errorf("failed to flatten network: %w", err)) } @@ -99,7 +99,7 @@ func dataSourceIPv4NetworkRead(ctx context.Context, d *schema.ResourceData, m in return diags } -func flattenNetwork(network ibclient.Ipv4Network) (map[string]interface{}, error) { +func flattenIpv4Network(network ibclient.Ipv4Network) (map[string]interface{}, error) { var eaMap map[string]interface{} if network.Ea != nil && len(network.Ea) > 0 { eaMap = network.Ea @@ -127,3 +127,87 @@ func flattenNetwork(network ibclient.Ipv4Network) (map[string]interface{}, error return res, nil } + +func flattenIpv6Network(network ibclient.Ipv6Network) (map[string]interface{}, error) { + var eaMap map[string]interface{} + if network.Ea != nil && len(network.Ea) > 0 { + eaMap = network.Ea + } else { + eaMap = make(map[string]interface{}) + } + ea, err := json.Marshal(eaMap) + if err != nil { + return nil, err + } + + res := map[string]interface{}{ + "id": network.Ref, + "network_view": network.NetworkView, + "ext_attrs": string(ea), + } + + if network.Network != nil { + res["cidr"] = *network.Network + } + + if network.Comment != nil { + res["comment"] = *network.Comment + } + + return res, nil +} + +func dataSourceIPv6NetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + connector := m.(ibclient.IBConnector) + + var diags diag.Diagnostics + + n := &ibclient.Ipv6Network{} + n.SetReturnFields(append(n.ReturnFields(), "extattrs")) + + filters := filterFromMap(d.Get("filters").(map[string]interface{})) + qp := ibclient.NewQueryParams(false, filters) + var res []ibclient.Ipv6Network + + err := connector.GetObject(n, "", qp, &res) + if err != nil { + return diag.FromErr(fmt.Errorf("getting network failed: %s", err)) + } + if res == nil { + return diag.FromErr(fmt.Errorf("API returns a nil/empty ID for the network")) + } + + // TODO: temporary scaffold, need to rework marshalling/unmarshalling of EAs + // (avoiding additional layer of keys ("value" key) + results := make([]interface{}, 0, len(res)) + for _, n := range res { + networkFlat, err := flattenIpv6Network(n) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to flatten network: %w", err)) + } + + results = append(results, networkFlat) + } + + err = d.Set("results", results) + if err != nil { + return diag.FromErr(err) + } + + // always run + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + + return diags +} + +func dataSourceIPv4Network() *schema.Resource { + nw := dataSourceNetwork() + nw.ReadContext = dataSourceIPv4NetworkRead + return nw +} + +func dataSourceIPv6Network() *schema.Resource { + nw := dataSourceNetwork() + nw.ReadContext = dataSourceIPv6NetworkRead + return nw +} diff --git a/infoblox/datasource_infoblox_network_test.go b/infoblox/datasource_infoblox_network_test.go index 25645b6f9..4ff387fa7 100644 --- a/infoblox/datasource_infoblox_network_test.go +++ b/infoblox/datasource_infoblox_network_test.go @@ -68,3 +68,49 @@ func TestAccDataSourceNetworkReadByEA(t *testing.T) { }, }) } + +func TestAccResourceIPv6NetworkCreate(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "infoblox_ipv6_network" "test_network" { + cidr = "2001:db8::/64" + } + `), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("infoblox_ipv6_network.test_network", "cidr", "2001:db8::/64"), + ), + }, + }, + }) +} + +func TestAccResourceIPv6NetworkUpdate(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "infoblox_ipv6_network" "test_network" { + cidr = "2001:db8::/64" + comment = "Initial comment" + } + `), + Check: resource.TestCheckResourceAttr("infoblox_ipv6_network.test_network", "comment", "Initial comment"), + }, + { + Config: fmt.Sprintf(` + resource "infoblox_ipv6_network" "test_network" { + cidr = "2001:db8::/64" + comment = "Updated comment" + } + `), + Check: resource.TestCheckResourceAttr("infoblox_ipv6_network.test_network", "comment", "Updated comment"), + }, + }, + }) +} diff --git a/infoblox/provider.go b/infoblox/provider.go index eec755557..aea6f8e10 100644 --- a/infoblox/provider.go +++ b/infoblox/provider.go @@ -212,7 +212,9 @@ func Provider() *schema.Provider { }, DataSourcesMap: map[string]*schema.Resource{ "infoblox_ipv4_network": dataSourceIPv4Network(), + "infoblox_ipv6_network": dataSourceIPv6Network(), "infoblox_ipv4_network_container": dataSourceIpv4NetworkContainer(), + "infoblox_ipv6_network_container": dataSourceIpv6NetworkContainer(), "infoblox_network_view": dataSourceNetworkView(), "infoblox_a_record": dataSourceARecord(), "infoblox_aaaa_record": dataSourceAAAARecord(), @@ -221,6 +223,7 @@ func Provider() *schema.Provider { "infoblox_txt_record": dataSourceTXTRecord(), "infoblox_mx_record": dataSourceMXRecord(), "infoblox_srv_record": dataSourceSRVRecord(), + "infoblox_host_record": dataSourceHostRecord(), "infoblox_zone_auth": dataSourceZoneAuth(), "infoblox_dns_view": dataSourceDNSView(), },