diff --git a/README.md b/README.md index 06bd90776..3e036a4e8 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,59 @@ -# Terraform Provider for Infoblox capture + +# Terraform Provider for Infoblox +Terraform provider plugin to integrate with Infoblox Network Identity Operating System [NIOS]. -## Requirements -* [Terraform](https://www.terraform.io/downloads.html) 0.14.x -* [Go](https://golang.org/doc/install) 1.15.x or 1.16.x (to build the provider plugin) -* CNA License need to be installed on NIOS. If CNA is not installed then following default EA's should be added in NIOS side: - * VM Name :: String Type - * VM ID :: String Type - * Tenant ID :: String Type - * CMP Type :: String Type - * Cloud API Owned :: List Type (Values True, False) - * Network Name :: String Type +The latest version of Infoblox NIOS provider is [v1.1.1](https://github.com/infobloxopen/terraform-provider-infoblox/releases/tag/v1.1.1) ## Building the Provider - +* Install and set apt environment variables [Golang](https://golang.org/doc/install) 1.16.x +* Clone the repo and build it ```sh $ git clone https://github.com/infobloxopen/terraform-provider-infoblox $ cd terraform-provider-infoblox $ make build ``` -## Using the Provider -If you're building the provider, follow the instructions to [install it as a plugin](https://www.terraform.io/docs/plugins/basics.html#installing-a-plugin). After the build is complete, copy the `terraform-provider-infoblox` binary into the same path as your terraform binary. After placing it into your plugins directory, run `terraform init` to initialize it. - ## Developing the Provider -If you wish to work on the provider, you'll first need Go installed on your machine (version 1.15.x or 1.16.x is required). +If you wish to work on the provider, follow the above steps to build it. -To compile the provider, run the following steps: -```sh -$ make build -... -$ ./terraform-provider-infoblox -... -``` -To test the provider, you can simply run `make test`. +To test the provider and to run the full suite of acceptance tests run below commands accordingly, ```sh $ make test -``` - -In order to run the full suite of acceptance tests `make testacc`. -```sh $ make testacc ``` + +## Using the Provider +* To use the plugin install v0.14.x [Terraform](https://www.terraform.io/downloads.html) +* If you're building the provider, follow the instructions to [install it as a plugin](https://www.terraform.io/docs/cli/config/config-file.html#development-overrides-for-provider-developers). +* Once the build is completed, set the `terraform-provider-infoblox` binary file location appropriately in in `dev_overrides`. + +## NIOS Requirements +* Plugin can be used without a CNA license and does not mandate to specify any EAs. + +* If Cloud Network Automation[CNA] License is installed on NIOS and has a Cloud Platform[CP] member attached. Make sure to have below mandatory EAs in .tf file if non mandatory cloud EAs(like "VM Name", "VM ID", "Network Name") are used. + * Tenant ID :: String Type + * CMP Type :: String Type + * Cloud API Owned :: List Type (Values True, False) + ## Features of Provider ### Resource +Create, Update and Delete of below resources is supported along with comment and EAs fields. +* IPv4 & IPv6 Network Container +* IPv4 & IPv6 Network +* Allocation & Deallocation of IPv4 or IPv6 address. +* Association & Disassociation of IPv4 or IPv6 address for a VM. + +Below resources are still under development and incomplete. * Creation & Deletion of Network View in NIOS appliance -* Creation & Deletion of IPv4 Network with comment field in NIOS appliance -* Allocation & Deallocation of IPv4 address from an IPv4 Network -* Association & Disassociation of IPv4 Address for a VM * Creation and Deletion of A, CNAME, Host, and PTR records ### Data Source -* Supports Data Source for IPv4 Network -* Support Data Source for A and CNAME records +Data Sources for below records are supported. +* IPv4 Network +* A Record +* CNAME Record ## Disclaimer To use the provider for DNS purposes, a parent (i.e. zone) must already exist. The plugin does not support the creation of zones. -while running acceptance tests create a 10.0.0.0/24 network under default network view and create a reservation for 10.0.0.2 IP +While running acceptance tests create a 10.0.0.0/24 network under default network view and create a reservation for 10.0.0.2 IP diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..90a9516a1 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,3 @@ + +* The Terraform configuration files under `v0.14` path are written and tested on Terraform v0.14 version and are actively maintained. +* The terraform configuration files under `archived` directory are of older version and are not maintained actively. \ No newline at end of file diff --git a/examples/AWS/ARecord/aws.tf b/examples/archived/AWS/ARecord/aws.tf similarity index 100% rename from examples/AWS/ARecord/aws.tf rename to examples/archived/AWS/ARecord/aws.tf diff --git a/examples/AWS/ARecord/infoblox.tf b/examples/archived/AWS/ARecord/infoblox.tf similarity index 100% rename from examples/AWS/ARecord/infoblox.tf rename to examples/archived/AWS/ARecord/infoblox.tf diff --git a/examples/AWS/CNAMERecord/aws.tf b/examples/archived/AWS/CNAMERecord/aws.tf similarity index 100% rename from examples/AWS/CNAMERecord/aws.tf rename to examples/archived/AWS/CNAMERecord/aws.tf diff --git a/examples/AWS/CNAMERecord/infoblox.tf b/examples/archived/AWS/CNAMERecord/infoblox.tf similarity index 100% rename from examples/AWS/CNAMERecord/infoblox.tf rename to examples/archived/AWS/CNAMERecord/infoblox.tf diff --git a/examples/AWS/FixedAddress/aws.tf b/examples/archived/AWS/FixedAddress/aws.tf similarity index 100% rename from examples/AWS/FixedAddress/aws.tf rename to examples/archived/AWS/FixedAddress/aws.tf diff --git a/examples/AWS/FixedAddress/infoblox.tf b/examples/archived/AWS/FixedAddress/infoblox.tf similarity index 100% rename from examples/AWS/FixedAddress/infoblox.tf rename to examples/archived/AWS/FixedAddress/infoblox.tf diff --git a/examples/AWS/NextAvailableNetwork/aws.tf b/examples/archived/AWS/NextAvailableNetwork/aws.tf similarity index 100% rename from examples/AWS/NextAvailableNetwork/aws.tf rename to examples/archived/AWS/NextAvailableNetwork/aws.tf diff --git a/examples/AWS/NextAvailableNetwork/infoblox.tf b/examples/archived/AWS/NextAvailableNetwork/infoblox.tf similarity index 100% rename from examples/AWS/NextAvailableNetwork/infoblox.tf rename to examples/archived/AWS/NextAvailableNetwork/infoblox.tf diff --git a/examples/AWS/PTRRecord/aws.tf b/examples/archived/AWS/PTRRecord/aws.tf similarity index 100% rename from examples/AWS/PTRRecord/aws.tf rename to examples/archived/AWS/PTRRecord/aws.tf diff --git a/examples/AWS/PTRRecord/infoblox.tf b/examples/archived/AWS/PTRRecord/infoblox.tf similarity index 100% rename from examples/AWS/PTRRecord/infoblox.tf rename to examples/archived/AWS/PTRRecord/infoblox.tf diff --git a/examples/AWS/README.md b/examples/archived/AWS/README.md similarity index 100% rename from examples/AWS/README.md rename to examples/archived/AWS/README.md diff --git a/examples/Azure/NextAvailableNetwork/azure.tf b/examples/archived/Azure/NextAvailableNetwork/azure.tf similarity index 100% rename from examples/Azure/NextAvailableNetwork/azure.tf rename to examples/archived/Azure/NextAvailableNetwork/azure.tf diff --git a/examples/Azure/NextAvailableNetwork/infoblox.tf b/examples/archived/Azure/NextAvailableNetwork/infoblox.tf similarity index 100% rename from examples/Azure/NextAvailableNetwork/infoblox.tf rename to examples/archived/Azure/NextAvailableNetwork/infoblox.tf diff --git a/examples/Azure/README.md b/examples/archived/Azure/README.md similarity index 100% rename from examples/Azure/README.md rename to examples/archived/Azure/README.md diff --git a/examples/Azure/infoblox.tf b/examples/archived/Azure/infoblox.tf similarity index 100% rename from examples/Azure/infoblox.tf rename to examples/archived/Azure/infoblox.tf diff --git a/examples/Azure/vm.tf b/examples/archived/Azure/vm.tf similarity index 100% rename from examples/Azure/vm.tf rename to examples/archived/Azure/vm.tf diff --git a/examples/VMware/FixedAddress/infoblox.tf b/examples/archived/VMware/FixedAddress/infoblox.tf similarity index 100% rename from examples/VMware/FixedAddress/infoblox.tf rename to examples/archived/VMware/FixedAddress/infoblox.tf diff --git a/examples/VMware/FixedAddress/vm.tf b/examples/archived/VMware/FixedAddress/vm.tf similarity index 100% rename from examples/VMware/FixedAddress/vm.tf rename to examples/archived/VMware/FixedAddress/vm.tf diff --git a/examples/VMware/HostRecord/infoblox.tf b/examples/archived/VMware/HostRecord/infoblox.tf similarity index 100% rename from examples/VMware/HostRecord/infoblox.tf rename to examples/archived/VMware/HostRecord/infoblox.tf diff --git a/examples/VMware/HostRecord/vm.tf b/examples/archived/VMware/HostRecord/vm.tf similarity index 100% rename from examples/VMware/HostRecord/vm.tf rename to examples/archived/VMware/HostRecord/vm.tf diff --git a/examples/VMware/NextAvailableNetwork/infoblox.tf b/examples/archived/VMware/NextAvailableNetwork/infoblox.tf similarity index 100% rename from examples/VMware/NextAvailableNetwork/infoblox.tf rename to examples/archived/VMware/NextAvailableNetwork/infoblox.tf diff --git a/examples/VMware/NextAvailableNetwork/vm.tf b/examples/archived/VMware/NextAvailableNetwork/vm.tf similarity index 100% rename from examples/VMware/NextAvailableNetwork/vm.tf rename to examples/archived/VMware/NextAvailableNetwork/vm.tf diff --git a/examples/VMware/README.md b/examples/archived/VMware/README.md similarity index 100% rename from examples/VMware/README.md rename to examples/archived/VMware/README.md diff --git a/examples/VMware/multiple/infoblox.tf b/examples/archived/VMware/multiple/infoblox.tf similarity index 100% rename from examples/VMware/multiple/infoblox.tf rename to examples/archived/VMware/multiple/infoblox.tf diff --git a/examples/VMware/multiple/vm.tf b/examples/archived/VMware/multiple/vm.tf similarity index 100% rename from examples/VMware/multiple/vm.tf rename to examples/archived/VMware/multiple/vm.tf diff --git a/examples/datasources/azurerm/README.md b/examples/archived/datasources/azurerm/README.md similarity index 100% rename from examples/datasources/azurerm/README.md rename to examples/archived/datasources/azurerm/README.md diff --git a/examples/datasources/azurerm/main.tf b/examples/archived/datasources/azurerm/main.tf similarity index 100% rename from examples/datasources/azurerm/main.tf rename to examples/archived/datasources/azurerm/main.tf diff --git a/examples/v0.14/AWS/AllocationAndAssociation/aws.tf b/examples/v0.14/AWS/AllocationAndAssociation/aws.tf new file mode 100644 index 000000000..cbf7b8073 --- /dev/null +++ b/examples/v0.14/AWS/AllocationAndAssociation/aws.tf @@ -0,0 +1,57 @@ +# Region being used to create the resources +provider "aws" { + region = "us-west-1" +} + +# Create a Virtual Private Cloud +resource "aws_vpc" "vpc" { + cidr_block = "10.0.0.0/16" + # Allocates /56 IPv6 CIDR block From Amazon Global Unicast Address to VPC + assign_generated_ipv6_cidr_block = true + tags = { + Name = "tf-vpc" + } +} + +# Create a Subnet +resource "aws_subnet" "subnet" { + vpc_id = aws_vpc.vpc.id + cidr_block = infoblox_ipv4_network.ipv4_network.cidr + ipv6_cidr_block = infoblox_ipv6_network.ipv6_network.cidr + availability_zone = "us-west-1a" + assign_ipv6_address_on_creation = false + map_public_ip_on_launch = false + + tags = { + Name = "tf-subnet" + Subnet = "tf-subnet" + } +} + + +#Create Network Interface +resource "aws_network_interface" "ni" { + subnet_id = aws_subnet.subnet.id + private_ips = [infoblox_ipv4_allocation.ipv4_allocation.ip_addr] + ipv6_addresses = [infoblox_ipv6_allocation.ipv6_allocation.ip_addr] + + tags = { + Name = "tf-ni" + } +} + +# Create AWS Instance +resource "aws_instance" "ec2-instance" { + # This ami is for us-west-1, change to Amazon Linux AMI for your region + ami = "ami-03130878b60947df3" + instance_type = "t2.micro" + + network_interface { + network_interface_id = aws_network_interface.ni.id + device_index = 0 + } + + tags = { + Name = "tf-ec2-instance" + } +} diff --git a/examples/v0.14/AWS/AllocationAndAssociation/infoblox.tf b/examples/v0.14/AWS/AllocationAndAssociation/infoblox.tf new file mode 100644 index 000000000..485808d1e --- /dev/null +++ b/examples/v0.14/AWS/AllocationAndAssociation/infoblox.tf @@ -0,0 +1,167 @@ +terraform { + # Required providers block for Terraform v0.14.7 + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + infoblox = { + source = "terraform-providers/infoblox" + version = ">= 1.0" + } + } +} + +# Create a network container in Infoblox Grid +resource "infoblox_ipv4_network_container" "IPv4_nw_c" { + network_view_name="default" + + cidr = aws_vpc.vpc.cidr_block + comment = "tf IPv4 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_network_container" "IPv6_nw_c" { + network_view_name="default" + + cidr = aws_vpc.vpc.ipv6_cidr_block + comment = "tf IPv6 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + + +# Allocate a network in Infoblox Grid under provided parent CIDR +resource "infoblox_ipv4_network" "ipv4_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv4_network_container.IPv4_nw_c.cidr + allocate_prefix_len = 24 + reserve_ip = 2 + + comment = "tf IPv4 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_network" "ipv6_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv6_network_container.IPv6_nw_c.cidr + allocate_prefix_len = 64 + reserve_ipv6 = 3 + + comment = "tf IPv6 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} + + +# Allocate IP from network +resource "infoblox_ipv4_allocation" "ipv4_allocation"{ + network_view_name= "default" + cidr = infoblox_ipv4_network.ipv4_network.cidr + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + comment = "tf IPv4 allocation" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + "VM Name" = "tf-ec2-instance" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_allocation" "ipv6_allocation" { + network_view_name= "default" + cidr = infoblox_ipv6_network.ipv6_network.cidr + duid = "00:00:00:00:00:00:00:00" + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + comment = "tf IPv6 allocation" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + "VM Name" = "tf-ec2-instance-ipv6" + Location = "Test loc." + Site = "Test site" + }) +} + + +# Update Grid with VM data +resource "infoblox_ipv4_association" "ipv4_associate"{ + network_view_name = "default" + cidr = infoblox_ipv4_network.ipv4_network.cidr + ip_addr = infoblox_ipv4_allocation.ipv4_allocation.ip_addr + mac_addr = aws_network_interface.ni.mac_address + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + comment = "tf IPv4 Association" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + "VM Name" = "tf-ec2-instance" + "VM ID" = aws_instance.ec2-instance.id + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_association" "ipv6_associate"{ + network_view_name = "default" + cidr = infoblox_ipv6_network.ipv6_network.cidr + ip_addr = infoblox_ipv6_allocation.ipv6_allocation.ip_addr + duid = aws_network_interface.ni.mac_address + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + comment = "tf IPv6 Association" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + "VM Name" = "tf-ec2-instance-ipv6" + "VM ID" = aws_instance.ec2-instance.id + Location = "Test loc." + Site = "Test site" + }) +} diff --git a/examples/v0.14/AWS/Network/aws.tf b/examples/v0.14/AWS/Network/aws.tf new file mode 100644 index 000000000..0f6ed5ecc --- /dev/null +++ b/examples/v0.14/AWS/Network/aws.tf @@ -0,0 +1,29 @@ +# Region being used to create the resources +provider "aws" { + region = "us-west-1" +} + +# Create a Virtual Private Cloud +resource "aws_vpc" "vpc" { + cidr_block = "10.0.0.0/16" + # Allocates /56 IPv6 CIDR block From Amazon Global Unicast Address to VPC + assign_generated_ipv6_cidr_block = true + tags = { + Name = "tf-vpc" + } +} + +# Create a Subnet +resource "aws_subnet" "subnet" { + vpc_id = aws_vpc.vpc.id + cidr_block = infoblox_ipv4_network.ipv4_network.cidr + ipv6_cidr_block = infoblox_ipv6_network.ipv6_network.cidr + availability_zone = "us-west-1a" + assign_ipv6_address_on_creation = false + map_public_ip_on_launch = false + + tags = { + Name = "tf-subnet" + Subnet = "tf-subnet" + } +} \ No newline at end of file diff --git a/examples/v0.14/AWS/Network/infoblox.tf b/examples/v0.14/AWS/Network/infoblox.tf new file mode 100644 index 000000000..7af198e20 --- /dev/null +++ b/examples/v0.14/AWS/Network/infoblox.tf @@ -0,0 +1,72 @@ +terraform { + # Required providers block for Terraform v0.14.7 + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + infoblox = { + source = "terraform-providers/infoblox" + version = ">= 1.0" + } + } +} + +# Create a network container in Infoblox Grid +resource "infoblox_ipv4_network_container" "IPv4_nw_c" { + network_view_name="default" + + cidr = aws_vpc.vpc.cidr_block + comment = "tf IPv4 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Location" = "Test loc." + "Site" = "Test site" + }) +} + +resource "infoblox_ipv6_network_container" "IPv6_nw_c" { + network_view_name="default" + + cidr = aws_vpc.vpc.ipv6_cidr_block + comment = "tf IPv6 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Location" = "Test loc." + "Site" = "Test site" + }) +} + + +# Allocate a network in Infoblox Grid under provided parent CIDR +resource "infoblox_ipv4_network" "ipv4_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv4_network_container.IPv4_nw_c.cidr + allocate_prefix_len = 24 + reserve_ip = 2 + + comment = "tf IPv4 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + "Location" = "Test loc." + "Site" = "Test site" + }) +} + +resource "infoblox_ipv6_network" "ipv6_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv6_network_container.IPv6_nw_c.cidr + allocate_prefix_len = 64 + reserve_ipv6 = 3 + + comment = "tf IPv6 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + "Location" = "Test loc." + "Site" = "Test site" + }) +} \ No newline at end of file diff --git a/examples/v0.14/AWS/NetworkContainer/aws.tf b/examples/v0.14/AWS/NetworkContainer/aws.tf new file mode 100644 index 000000000..78077fca6 --- /dev/null +++ b/examples/v0.14/AWS/NetworkContainer/aws.tf @@ -0,0 +1,14 @@ +# Region being used to create the resources +provider "aws" { + region = "us-west-1" +} + +# Create a Virtual Private Cloud +resource "aws_vpc" "vpc" { + cidr_block = "10.0.0.0/16" + # Allocates /56 IPv6 CIDR block From Amazon Global Unicast Address to VPC + assign_generated_ipv6_cidr_block = true + tags = { + Name = "tf-vpc" + } +} \ No newline at end of file diff --git a/examples/v0.14/AWS/NetworkContainer/infoblox.tf b/examples/v0.14/AWS/NetworkContainer/infoblox.tf new file mode 100644 index 000000000..99b925277 --- /dev/null +++ b/examples/v0.14/AWS/NetworkContainer/infoblox.tf @@ -0,0 +1,36 @@ +terraform { + # Required providers block for Terraform v0.14.7 + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + infoblox = { + source = "terraform-providers/infoblox" + version = ">= 1.0" + } + } +} + +# Create a network container in Infoblox Grid +resource "infoblox_ipv4_network_container" "IPv4_nw_c" { + network_view_name="default" + cidr = aws_vpc.vpc.cidr_block + comment = "tf IPv4 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Location" = "Test loc." + "Site" = "Test site" + }) +} + +resource "infoblox_ipv6_network_container" "IPv6_nw_c" { + network_view_name="default" + cidr = aws_vpc.vpc.ipv6_cidr_block + comment = "tf IPv6 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Location" = "Test loc." + "Site" = "Test site" + }) +} \ No newline at end of file diff --git a/examples/v0.14/AWS/NextAvailableNetwork/aws.tf b/examples/v0.14/AWS/NextAvailableNetwork/aws.tf index ef4393176..f88c247c7 100644 --- a/examples/v0.14/AWS/NextAvailableNetwork/aws.tf +++ b/examples/v0.14/AWS/NextAvailableNetwork/aws.tf @@ -1,11 +1,13 @@ # Region being used to create the resources provider "aws" { - region = "us-east-1" + region = "us-west-1" } # Create a Virtual Private Cloud resource "aws_vpc" "vpc" { - cidr_block = "20.0.0.0/16" + cidr_block = "10.0.0.0/16" + # Allocates /56 IPv6 CIDR block From Amazon Global Unicast Address to VPC + assign_generated_ipv6_cidr_block = true tags = { Name = "tf-vpc" } @@ -14,39 +16,14 @@ resource "aws_vpc" "vpc" { # Create a Subnet resource "aws_subnet" "subnet" { vpc_id = aws_vpc.vpc.id - cidr_block = infoblox_network.ib_network.cidr - availability_zone = "us-east-1b" + cidr_block = infoblox_ipv4_network.ipv4_network.cidr + ipv6_cidr_block = infoblox_ipv6_network.ipv6_network.cidr + availability_zone = "us-west-1a" + assign_ipv6_address_on_creation = false + map_public_ip_on_launch = false tags = { Name = "tf-subnet" Subnet = "tf-subnet" } -} - -# Create Network Interface -resource "aws_network_interface" "ni" { - subnet_id = aws_subnet.subnet.id - private_ips = [infoblox_ip_allocation.ib_ip_allocation.ip_addr] - - tags = { - Name = "tf-ni" - } -} - -# Create AWS Instance -resource "aws_instance" "ec2-instance" { - # This ami is for us-west-1, change to Amazon Linux AMI for your region - #ami = "ami-03130878b60947df3" - ami = "ami-0915bcb5fa77e4892" - instance_type = "t2.micro" - - network_interface { - network_interface_id = aws_network_interface.ni.id - device_index = 0 - } - - tags = { - Name = infoblox_ip_allocation.ib_ip_allocation.vm_name - } -} - +} \ No newline at end of file diff --git a/examples/v0.14/AWS/NextAvailableNetwork/infoblox.tf b/examples/v0.14/AWS/NextAvailableNetwork/infoblox.tf index 95ab8dcea..1fca53c0c 100644 --- a/examples/v0.14/AWS/NextAvailableNetwork/infoblox.tf +++ b/examples/v0.14/AWS/NextAvailableNetwork/infoblox.tf @@ -1,4 +1,3 @@ -# Creates next available network from a given parent CIDR in NIOS grid terraform { # Required providers block for Terraform v0.14.7 required_providers { @@ -13,32 +12,61 @@ terraform { } } +# Create a network container in Infoblox Grid +resource "infoblox_ipv4_network_container" "IPv4_nw_c" { + network_view_name="default" + + cidr = aws_vpc.vpc.cidr_block + comment = "tf IPv4 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Location" = "Test loc." + "Site" = "Test site" + }) +} + +resource "infoblox_ipv6_network_container" "IPv6_nw_c" { + network_view_name="default" + + cidr = aws_vpc.vpc.ipv6_cidr_block + comment = "tf IPv6 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Location" = "Test loc." + "Site" = "Test site" + }) +} + + # Allocate a network in Infoblox Grid under provided parent CIDR -resource "infoblox_network" "ib_network"{ +resource "infoblox_ipv4_network" "ipv4_network"{ network_view_name = "default" - network_name = "tf-network" - tenant_id = "tf-AWS-tenant" + + parent_cidr = infoblox_ipv4_network_container.IPv4_nw_c.cidr allocate_prefix_len = 24 - parent_cidr = "20.0.0.0/16" reserve_ip = 2 -} -# Allocate IP from network -resource "infoblox_ip_allocation" "ib_ip_allocation"{ - network_view_name= "default" - vm_name = "tf-ec2-instance" - cidr = infoblox_network.ib_network.cidr - tenant_id = "tf-AWS-tenant" + comment = "tf IPv4 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + "Location" = "Test loc." + "Site" = "Test site" + }) } -# Update Grid with VM data -resource "infoblox_ip_association" "ib_ip_associate"{ +resource "infoblox_ipv6_network" "ipv6_network"{ network_view_name = "default" - vm_name = infoblox_ip_allocation.ib_ip_allocation.vm_name - cidr = infoblox_network.ib_network.cidr - mac_addr = aws_network_interface.ni.mac_address - ip_addr = infoblox_ip_allocation.ib_ip_allocation.ip_addr - vm_id = aws_instance.ec2-instance.id - tenant_id = "tf-AWS-tenant" -} + parent_cidr = infoblox_ipv6_network_container.IPv6_nw_c.cidr + allocate_prefix_len = 64 + reserve_ipv6 = 3 + + comment = "tf IPv6 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + "Location" = "Test loc." + "Site" = "Test site" + }) +} \ No newline at end of file diff --git a/examples/v0.14/AWS/README.md b/examples/v0.14/AWS/README.md index 3f4098a29..910258957 100644 --- a/examples/v0.14/AWS/README.md +++ b/examples/v0.14/AWS/README.md @@ -10,7 +10,10 @@ Install AWS CLI. Use AWS Configure command to configure Access Key ID and Secret ``` #Using the templates for below use cases. +- NetworkContainer : Create IPv4/IPv6 Network Containers +- Network : Create IPv4/IPv6 Network - NextAvailableNetwork : Get next available network from a given parent CIDR of a prefix length. +- AllocationAndAssociation : Assign an IPv4 and IPv6 address to an AWS instance and get its MAC address synced at NIOS ### Note ``` diff --git a/examples/v0.14/Azure/NetworkContainer/IPv4/azure.tf b/examples/v0.14/Azure/NetworkContainer/IPv4/azure.tf new file mode 100644 index 000000000..0953ffd17 --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/IPv4/azure.tf @@ -0,0 +1,83 @@ +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "rg1" { + name = "${local.res_prefix}_rg1" + location = "ukwest" +} + +resource "azurerm_virtual_network" "vnet1" { + location = "ukwest" + resource_group_name = azurerm_resource_group.rg1.name + + name = "${local.res_prefix}_vnet1" + address_space = [infoblox_ipv4_network_container.v4nc_1.cidr] +} + +resource "azurerm_subnet" "net1" { + resource_group_name = azurerm_resource_group.rg1.name + virtual_network_name = azurerm_virtual_network.vnet1.name + + name = "${local.res_prefix}_net1" + address_prefixes =[infoblox_ipv4_network.subnet1.cidr] +} + +resource "azurerm_public_ip" "pub_addr1" { + resource_group_name = azurerm_resource_group.rg1.name + location = "ukwest" + + name = "${local.res_prefix}_pub_addr1" + allocation_method = "Dynamic" + domain_name_label = "a132" +} + +resource "azurerm_network_interface" "ni1" { + resource_group_name = azurerm_resource_group.rg1.name + location = "ukwest" + + name = "${local.res_prefix}_ni1" + ip_configuration { + name = "${local.res_prefix}_ipconfiguration1" + subnet_id = azurerm_subnet.net1.id + private_ip_address_allocation = "Static" + private_ip_address =infoblox_ipv4_allocation.alloc1.ip_addr + + public_ip_address_id = azurerm_public_ip.pub_addr1.id + } +} + +resource "azurerm_virtual_machine" "vm1" { + resource_group_name = azurerm_resource_group.rg1.name + location = "ukwest" + + name = "${local.res_prefix}_vm1" + network_interface_ids = [azurerm_network_interface.ni1.id] + primary_network_interface_id = azurerm_network_interface.ni1.id + vm_size = "Standard_A6" + delete_os_disk_on_termination = true + delete_data_disks_on_termination = true + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "main_storage" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "ubuntu" + admin_username = "ubuntu" + admin_password="JKLhdsa&^52128" + } + + os_profile_linux_config { + disable_password_authentication = false + } +} diff --git a/examples/v0.14/Azure/NetworkContainer/IPv4/infoblox.tf b/examples/v0.14/Azure/NetworkContainer/IPv4/infoblox.tf new file mode 100644 index 000000000..4d180e07b --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/IPv4/infoblox.tf @@ -0,0 +1,43 @@ +resource "infoblox_network_view" "nv1" { + tenant_id = local.tenant_id + network_view_name=local.net_view +} + +resource "infoblox_ipv4_network_container" "v4nc_1" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr = "10.0.0.0/16" + comment = "new network container" + extensible_attributes = jsonencode({ + "Location" = "Test loc." + "Site" = "Test site" + "Tenant ID" = local.tenant_id + }) +} + +resource "infoblox_ipv4_network" "subnet1"{ + network_view_name=infoblox_network_view.nv1.network_view_name + allocate_prefix_len = 24 + parent_cidr = infoblox_ipv4_network_container.v4nc_1.cidr + reserve_ip=3 + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "Network Name" = "${local.res_prefix}_subnet1" + }) +} + +resource "infoblox_ipv4_allocation" "alloc1" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr=infoblox_ipv4_network.subnet1.cidr + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "VM Name" = "${local.res_prefix}_vm1" + }) +} diff --git a/examples/v0.14/Azure/NetworkContainer/IPv4/locals.tf b/examples/v0.14/Azure/NetworkContainer/IPv4/locals.tf new file mode 120000 index 000000000..1b032e65b --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/IPv4/locals.tf @@ -0,0 +1 @@ +../locals.tf \ No newline at end of file diff --git a/examples/v0.14/Azure/NetworkContainer/IPv4/terraform.tf b/examples/v0.14/Azure/NetworkContainer/IPv4/terraform.tf new file mode 120000 index 000000000..fdbf0eba0 --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/IPv4/terraform.tf @@ -0,0 +1 @@ +../terraform.tf \ No newline at end of file diff --git a/examples/v0.14/Azure/NetworkContainer/IPv6/azure.tf b/examples/v0.14/Azure/NetworkContainer/IPv6/azure.tf new file mode 100644 index 000000000..c1ae1fc61 --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/IPv6/azure.tf @@ -0,0 +1,102 @@ +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "rg1" { + name = "${local.res_prefix}_rg1" + location = "ukwest" +} + +resource "azurerm_virtual_network" "vnet1" { + location = "ukwest" + resource_group_name = azurerm_resource_group.rg1.name + + name = "${local.res_prefix}_vnet1" + address_space = [ + infoblox_ipv4_network_container.nc_1.cidr, + infoblox_ipv6_network_container.nc_2.cidr] +} + +resource "azurerm_subnet" "net1" { + resource_group_name = azurerm_resource_group.rg1.name + virtual_network_name = azurerm_virtual_network.vnet1.name + + name = "${local.res_prefix}_net1" + address_prefixes =[ + infoblox_ipv4_network.subnet1.cidr, + infoblox_ipv6_network.subnet2.cidr] +} + +resource "azurerm_public_ip" "pub_addr1" { + resource_group_name = azurerm_resource_group.rg1.name + location = "ukwest" + + name = "${local.res_prefix}_pub_addr1" + allocation_method = "Dynamic" + domain_name_label = "a132" + ip_version = "IPv4" +} + +resource "azurerm_network_interface" "ni1" { + resource_group_name = azurerm_resource_group.rg1.name + location = "ukwest" + + name = "${local.res_prefix}_ni1" + + ip_configuration { + name = "${local.res_prefix}_ipconfiguration1" + subnet_id = azurerm_subnet.net1.id + private_ip_address_allocation = "Static" + private_ip_address_version = "IPv6" + private_ip_address = infoblox_ipv6_allocation.alloc2.ip_addr + } + + ip_configuration { + primary = true + name = "${local.res_prefix}_ipconfiguration2" + subnet_id = azurerm_subnet.net1.id + private_ip_address_allocation = "Static" + private_ip_address = infoblox_ipv4_allocation.alloc1.ip_addr + public_ip_address_id = azurerm_public_ip.pub_addr1.id + } +} + +resource "azurerm_virtual_machine" "vm1" { + resource_group_name = azurerm_resource_group.rg1.name + location = "ukwest" + + name = "${local.res_prefix}_vm1" + network_interface_ids = [azurerm_network_interface.ni1.id] + primary_network_interface_id = azurerm_network_interface.ni1.id + vm_size = "Standard_A6" + delete_os_disk_on_termination = true + delete_data_disks_on_termination = true + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "main_storage" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "ubuntu" + admin_username = "ubuntu" + admin_password="JKLhdsa&^52128" + } + + os_profile_linux_config { + disable_password_authentication = false + } +} + +locals { + vm_mac_addr = azurerm_network_interface.ni1.mac_address + vm_id = azurerm_virtual_machine.vm1.id +} diff --git a/examples/v0.14/Azure/NetworkContainer/IPv6/infoblox.tf b/examples/v0.14/Azure/NetworkContainer/IPv6/infoblox.tf new file mode 100644 index 000000000..244b6957a --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/IPv6/infoblox.tf @@ -0,0 +1,147 @@ +resource "infoblox_network_view" "nv1" { + tenant_id = local.tenant_id + network_view_name=local.net_view +} + +resource "infoblox_ipv4_network_container" "nc_1" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr = "10.0.0.0/16" + comment = "new network container" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "Location" = "Test loc." + "Site" = "Test site" + }) +} + +resource "infoblox_ipv4_network" "subnet1"{ + network_view_name=infoblox_network_view.nv1.network_view_name + allocate_prefix_len = 24 + parent_cidr = infoblox_ipv4_network_container.nc_1.cidr + reserve_ip=3 + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "Network Name" = "${local.res_prefix}_subnet1" + }) +} + +resource "infoblox_ipv4_allocation" "alloc1" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr=infoblox_ipv4_network.subnet1.cidr + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + }) +} + +resource "infoblox_ipv4_association" "assoc1"{ + network_view_name=infoblox_network_view.nv1.network_view_name + cidr = infoblox_ipv4_allocation.alloc1.cidr + mac_addr = local.vm_mac_addr + ip_addr = infoblox_ipv4_allocation.alloc1.ip_addr + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "VM Name" = "${local.res_prefix}_vm1" + "VM ID" = local.vm_id + }) +} + +resource "infoblox_ipv6_network_container" "nc_2" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr = "fc00::/56" + comment = "new network container" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "Location" = "Test loc." + "Site" = "Test site" + }) +} + +resource "infoblox_ipv6_network" "subnet2"{ + network_view_name=infoblox_network_view.nv1.network_view_name + allocate_prefix_len = 64 + parent_cidr = infoblox_ipv6_network_container.nc_2.cidr + reserve_ip=10 + + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "Network Name" = "${local.res_prefix}_subnet2" + }) +} + +locals { + ipv6_reserved_ips = 10 +} + +resource "infoblox_ipv6_allocation" "alloc_reserved" { + count=local.ipv6_reserved_ips + + network_view_name=infoblox_network_view.nv1.network_view_name + cidr=infoblox_ipv6_network.subnet2.cidr + duid = format("00:%.2x", count.index) + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + }) +} + +resource "infoblox_ipv6_allocation" "alloc2" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr=infoblox_ipv6_network.subnet2.cidr + duid = format("00:%.2x", local.ipv6_reserved_ips) + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + }) + + depends_on = [infoblox_ipv6_allocation.alloc_reserved] +} + +resource "infoblox_ipv6_association" "assoc2"{ + network_view_name=infoblox_network_view.nv1.network_view_name + cidr = infoblox_ipv6_allocation.alloc2.cidr + mac_addr = local.vm_mac_addr + ip_addr = infoblox_ipv6_allocation.alloc2.ip_addr + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "VM Name" = "${local.res_prefix}_vm1" + "VM ID" = local.vm_id + }) +} diff --git a/examples/v0.14/Azure/NetworkContainer/IPv6/locals.tf b/examples/v0.14/Azure/NetworkContainer/IPv6/locals.tf new file mode 120000 index 000000000..1b032e65b --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/IPv6/locals.tf @@ -0,0 +1 @@ +../locals.tf \ No newline at end of file diff --git a/examples/v0.14/Azure/NetworkContainer/IPv6/terraform.tf b/examples/v0.14/Azure/NetworkContainer/IPv6/terraform.tf new file mode 120000 index 000000000..fdbf0eba0 --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/IPv6/terraform.tf @@ -0,0 +1 @@ +../terraform.tf \ No newline at end of file diff --git a/examples/v0.14/Azure/NetworkContainer/locals.tf b/examples/v0.14/Azure/NetworkContainer/locals.tf new file mode 100644 index 000000000..44917699c --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/locals.tf @@ -0,0 +1,8 @@ +locals { + res_prefix = "terraform_example1" + tenant_id = "${local.res_prefix}_tenant" + + # net_view = "default" + # ... or (as a non-standard example) + net_view = "${local.res_prefix}_netview" +} diff --git a/examples/v0.14/Azure/NetworkContainer/terraform.tf b/examples/v0.14/Azure/NetworkContainer/terraform.tf new file mode 100644 index 000000000..5a64bed52 --- /dev/null +++ b/examples/v0.14/Azure/NetworkContainer/terraform.tf @@ -0,0 +1,13 @@ +terraform { + # Required providers block for Terraform v0.14 + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 2.50.0" + } + infoblox = { + source = "terraform-providers/infoblox" + version = "~> 1.1.0" + } + } +} diff --git a/examples/v0.14/Azure/NextAvailableNetwork/azure.tf b/examples/v0.14/Azure/NextAvailableNetwork/azure.tf index 158e53f20..0fb8d0356 100644 --- a/examples/v0.14/Azure/NextAvailableNetwork/azure.tf +++ b/examples/v0.14/Azure/NextAvailableNetwork/azure.tf @@ -12,7 +12,7 @@ resource "azurerm_virtual_network" "vnet1" { resource_group_name = azurerm_resource_group.rg1.name name = "${local.res_prefix}_vnet1" - address_space = [local.parent_cidr] + address_space = [infoblox_ipv4_network_container.v4nc_1.cidr] } resource "azurerm_subnet" "net1" { @@ -20,7 +20,7 @@ resource "azurerm_subnet" "net1" { virtual_network_name = azurerm_virtual_network.vnet1.name name = "${local.res_prefix}_net1" - address_prefixes =[infoblox_network.subnet1.cidr] + address_prefixes =[infoblox_ipv4_network.subnet1.cidr] } resource "azurerm_subnet" "net2" { @@ -28,7 +28,7 @@ resource "azurerm_subnet" "net2" { virtual_network_name = azurerm_virtual_network.vnet1.name name = "${local.res_prefix}_net2" - address_prefixes =[infoblox_network.subnet2.cidr] + address_prefixes =[infoblox_ipv4_network.subnet2.cidr] } resource "azurerm_public_ip" "pub_addr1" { @@ -49,7 +49,7 @@ resource "azurerm_network_interface" "ni1" { name = "${local.res_prefix}_ipconfiguration1" subnet_id = azurerm_subnet.net1.id private_ip_address_allocation = "Static" - private_ip_address =infoblox_ip_allocation.alloc1.ip_addr + private_ip_address =infoblox_ipv4_allocation.alloc1.ip_addr public_ip_address_id = azurerm_public_ip.pub_addr1.id } @@ -64,7 +64,7 @@ resource "azurerm_network_interface" "ni2" { name = "${local.res_prefix}_ipconfiguration2" subnet_id = azurerm_subnet.net2.id private_ip_address_allocation = "Static" - private_ip_address =infoblox_ip_allocation.alloc2.ip_addr + private_ip_address =infoblox_ipv4_allocation.alloc2.ip_addr } } @@ -72,7 +72,7 @@ resource "azurerm_virtual_machine" "vm1" { resource_group_name = azurerm_resource_group.rg1.name location = "ukwest" - name = infoblox_ip_allocation.alloc1.vm_name + name = "${local.res_prefix}_vm1" network_interface_ids = [azurerm_network_interface.ni1.id, azurerm_network_interface.ni2.id] primary_network_interface_id = azurerm_network_interface.ni1.id vm_size = "Standard_A6" diff --git a/examples/v0.14/Azure/NextAvailableNetwork/infoblox.tf b/examples/v0.14/Azure/NextAvailableNetwork/infoblox.tf index e7ae6942c..0136a7901 100644 --- a/examples/v0.14/Azure/NextAvailableNetwork/infoblox.tf +++ b/examples/v0.14/Azure/NextAvailableNetwork/infoblox.tf @@ -1,35 +1,79 @@ -resource "infoblox_network" "subnet1"{ +resource "infoblox_network_view" "nv1" { + tenant_id = local.tenant_id network_view_name=local.net_view - tenant_id=local.tenant_id +} + +resource "infoblox_ipv4_network_container" "v4nc_1" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr = "10.0.0.0/16" + comment = "new network container" + + extensible_attributes = jsonencode({ + "Location" = "Test loc." + "Site" = "Test site" + "Tenant ID" = local.tenant_id + }) +} - network_name="${local.res_prefix}_subnet1" +resource "infoblox_ipv4_network" "subnet1"{ + network_view_name=infoblox_network_view.nv1.network_view_name allocate_prefix_len = 24 - parent_cidr = local.parent_cidr + parent_cidr = infoblox_ipv4_network_container.v4nc_1.cidr reserve_ip=3 -} -resource "infoblox_network" "subnet2"{ - network_view_name=local.net_view - tenant_id=local.tenant_id + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "Network Name" = "${local.res_prefix}_subnet1" + "TestEA1" = "text3" + "TestEA2" = 7 + }) +} - network_name="${local.res_prefix}_subnet2" +resource "infoblox_ipv4_network" "subnet2"{ + network_view_name=infoblox_network_view.nv1.network_view_name allocate_prefix_len = 24 - parent_cidr = local.parent_cidr + parent_cidr = infoblox_ipv4_network_container.v4nc_1.cidr reserve_ip=3 + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "Network Name" = "${local.res_prefix}_subnet2" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) } -resource "infoblox_ip_allocation" "alloc1" { - network_view_name=local.net_view - tenant_id=local.tenant_id +resource "infoblox_ipv4_allocation" "alloc1" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr=infoblox_ipv4_network.subnet1.cidr - vm_name="${local.res_prefix}_vm1" - cidr=infoblox_network.subnet1.cidr + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "VM Name" = "${local.res_prefix}_vm1" + }) } -resource "infoblox_ip_allocation" "alloc2" { - network_view_name=local.net_view - tenant_id=local.tenant_id +resource "infoblox_ipv4_allocation" "alloc2" { + network_view_name=infoblox_network_view.nv1.network_view_name + cidr=infoblox_ipv4_network.subnet2.cidr + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" - vm_name="${local.res_prefix}_vm1" - cidr=infoblox_network.subnet2.cidr + extensible_attributes = jsonencode({ + "Tenant ID" = local.tenant_id + "VM Name" = "${local.res_prefix}_vm1" + }) } diff --git a/examples/v0.14/Azure/NextAvailableNetwork/locals.tf b/examples/v0.14/Azure/NextAvailableNetwork/locals.tf index 21e79aab5..44917699c 100644 --- a/examples/v0.14/Azure/NextAvailableNetwork/locals.tf +++ b/examples/v0.14/Azure/NextAvailableNetwork/locals.tf @@ -5,9 +5,4 @@ locals { # net_view = "default" # ... or (as a non-standard example) net_view = "${local.res_prefix}_netview" - # This network view must exist in the Grid. - - # This CIDR must belong to an existing network *container* - # in the network view specified above. - parent_cidr = "10.0.0.0/16" } diff --git a/examples/v0.14/README.md b/examples/v0.14/README.md new file mode 100644 index 000000000..782a3ed94 --- /dev/null +++ b/examples/v0.14/README.md @@ -0,0 +1,3 @@ +# While using IPv6 Features make sure to consider the following +* IPv6 Allocation and Association through Fixed Address/Host Record mandates unique DUID in .tf file. +* For all the cloud providers(AWS, Azure and VMWare), DUID will be updated with MAC address of the interface in NIOS. diff --git a/examples/v0.14/VMWare/AllocationAndAssociation/infoblox.tf b/examples/v0.14/VMWare/AllocationAndAssociation/infoblox.tf new file mode 100644 index 000000000..4cacad88e --- /dev/null +++ b/examples/v0.14/VMWare/AllocationAndAssociation/infoblox.tf @@ -0,0 +1,150 @@ +# Create a network container in Infoblox Grid +resource "infoblox_ipv4_network_container" "IPv4_nw_c" { + network_view_name="default" + + cidr = "10.0.0.0/16" + comment = "tf IPv4 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_network_container" "IPv6_nw_c" { + network_view_name="default" + + cidr = "2001:1890:1959:2710::/62" + comment = "tf IPv6 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + +# Allocate a network in Infoblox Grid under provided parent CIDR +resource "infoblox_ipv4_network" "ipv4_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv4_network_container.IPv4_nw_c.cidr + allocate_prefix_len = 24 + reserve_ip = 2 + + comment = "tf IPv4 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_network" "ipv6_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv6_network_container.IPv6_nw_c.cidr + allocate_prefix_len = 64 + reserve_ipv6 = 3 + + comment = "tf IPv6 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} + +# Allocate IP from network +resource "infoblox_ipv4_allocation" "ipv4_allocation"{ + network_view_name= "default" + cidr = infoblox_ipv4_network.ipv4_network.cidr + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + comment = "tf IPv4 allocation" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + "VM Name" = "tf-vmware-ipv4" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_allocation" "ipv6_allocation" { + network_view_name= "default" + cidr = infoblox_ipv6_network.ipv6_network.cidr + duid = "00:00:00:00:00:00:00:00" + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + comment = "tf IPv6 allocation" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + "VM Name" = "tf-vmware-ipv6" + Location = "Test loc." + Site = "Test site" + }) +} + +# Update Grid with VM data +resource "infoblox_ipv4_association" "ipv4_associate"{ + network_view_name = "default" + cidr = infoblox_ipv4_network.ipv4_network.cidr + ip_addr = infoblox_ipv4_allocation.ipv4_allocation.ip_addr + mac_addr = vsphere_virtual_machine.vm_ipv4.network_interface[0].mac_address + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + comment = "tf IPv4 Association" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + "VM Name" = vsphere_virtual_machine.vm_ipv4.name + "VM ID" = vsphere_virtual_machine.vm_ipv4.id + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_association" "ipv6_associate"{ + network_view_name = "default" + cidr = infoblox_ipv6_network.ipv6_network.cidr + ip_addr = infoblox_ipv6_allocation.ipv6_allocation.ip_addr + duid = vsphere_virtual_machine.vm_ipv6.network_interface[0].mac_address + host_name = "test" + + #Create Host Record with DNS and DHCP flags + #dns_view="default" + #zone="aws.com" + #enable_dns = "false" + #enable_dhcp = "false" + + comment = "tf IPv6 Association" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + "VM Name" = vsphere_virtual_machine.vm_ipv6.name + "VM ID" = vsphere_virtual_machine.vm_ipv6.id + Location = "Test loc." + Site = "Test site" + }) +} diff --git a/examples/v0.14/VMWare/AllocationAndAssociation/versions.tf b/examples/v0.14/VMWare/AllocationAndAssociation/versions.tf new file mode 100644 index 000000000..0a7dd1c3b --- /dev/null +++ b/examples/v0.14/VMWare/AllocationAndAssociation/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + infoblox = { + source = "terraform-providers/infoblox" + version = ">= 1.0" + } + vsphere = { + source = "hashicorp/vsphere" + version = "1.12.0" + } + } +} diff --git a/examples/v0.14/VMWare/AllocationAndAssociation/vm.tf b/examples/v0.14/VMWare/AllocationAndAssociation/vm.tf new file mode 100644 index 000000000..7505efe3f --- /dev/null +++ b/examples/v0.14/VMWare/AllocationAndAssociation/vm.tf @@ -0,0 +1,111 @@ +provider "vsphere" { + allow_unverified_ssl = true +} + +data "vsphere_datacenter" "dc"{ + name = "Blr-Devlab" +} + +data "vsphere_datastore" "datastore" { + name = "datastore_44" + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_resource_pool" "pool" { + name = "Blr-Cloud/Resources" + datacenter_id = data.vsphere_datacenter.dc.id + +} + +data "vsphere_network" "network" { + name = "VM Network" + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_virtual_machine" "template" { + name = "WebTinyCentOS65x86-tcpdump" + datacenter_id = data.vsphere_datacenter.dc.id +} + +# Create VM in vsphere +resource "vsphere_virtual_machine" "vm_ipv4" { + name = lookup(jsondecode(infoblox_ipv4_allocation.ipv4_allocation.extensible_attributes), "VM Name") + resource_pool_id = data.vsphere_resource_pool.pool.id + datastore_id = data.vsphere_datastore.datastore.id + num_cpus = 2 + memory = 1024 + guest_id = data.vsphere_virtual_machine.template.guest_id + scsi_type = data.vsphere_virtual_machine.template.scsi_type + + network_interface { + network_id = data.vsphere_network.network.id + adapter_type = data.vsphere_virtual_machine.template.network_interface_types[0] + } + + disk { + label = "disk0" + size = data.vsphere_virtual_machine.template.disks.0.size + eagerly_scrub = data.vsphere_virtual_machine.template.disks.0.eagerly_scrub + thin_provisioned = data.vsphere_virtual_machine.template.disks.0.thin_provisioned + } + + clone { + template_uuid = data.vsphere_virtual_machine.template.id + + customize { + linux_options { + host_name = "terraform-test1" + domain = "test.internal" + } + + network_interface { + ipv4_address = infoblox_ipv4_allocation.ipv4_allocation.ip_addr + ipv4_netmask = 24 + } + + ipv4_gateway = infoblox_ipv4_network.ipv4_network.gateway + } + } +} + +resource "vsphere_virtual_machine" "vm_ipv6" { + name = lookup(jsondecode(infoblox_ipv6_allocation.ipv6_allocation.extensible_attributes), "VM Name") + resource_pool_id = data.vsphere_resource_pool.pool.id + datastore_id = data.vsphere_datastore.datastore.id + wait_for_guest_net_timeout = 0 + #wait_for_guest_ip_timeout = 5 + num_cpus = 2 + memory = 1024 + guest_id = data.vsphere_virtual_machine.template.guest_id + scsi_type = data.vsphere_virtual_machine.template.scsi_type + + network_interface { + network_id = data.vsphere_network.network.id + adapter_type = data.vsphere_virtual_machine.template.network_interface_types[0] + } + + disk { + label = "disk0" + size = data.vsphere_virtual_machine.template.disks.0.size + eagerly_scrub = data.vsphere_virtual_machine.template.disks.0.eagerly_scrub + thin_provisioned = data.vsphere_virtual_machine.template.disks.0.thin_provisioned + } + + clone { + template_uuid = data.vsphere_virtual_machine.template.id + + customize { + linux_options { + host_name = "terraform-test1" + domain = "test.internal" + } + + network_interface { + ipv6_address = infoblox_ipv6_allocation.ipv6_allocation.ip_addr + ipv6_netmask = 64 + } + + ipv6_gateway = infoblox_ipv6_network.ipv6_network.gateway + } + } +} diff --git a/examples/v0.14/VMWare/Network/IPv4/infoblox.tf b/examples/v0.14/VMWare/Network/IPv4/infoblox.tf new file mode 100644 index 000000000..adfe1f4d7 --- /dev/null +++ b/examples/v0.14/VMWare/Network/IPv4/infoblox.tf @@ -0,0 +1,17 @@ +# Create a network in Infoblox Griby passing the CIDR +resource "infoblox_ipv4_network" "ipv4_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv4_network_container.IPv4_nw_c.cidr + allocate_prefix_len = 24 + reserve_ip = 2 + + comment = "tf IPv4 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} + diff --git a/examples/v0.14/VMWare/Network/IPv4/versions.tf b/examples/v0.14/VMWare/Network/IPv4/versions.tf new file mode 100644 index 000000000..0a7dd1c3b --- /dev/null +++ b/examples/v0.14/VMWare/Network/IPv4/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + infoblox = { + source = "terraform-providers/infoblox" + version = ">= 1.0" + } + vsphere = { + source = "hashicorp/vsphere" + version = "1.12.0" + } + } +} diff --git a/examples/v0.14/VMWare/Network/IPv4/vm.tf b/examples/v0.14/VMWare/Network/IPv4/vm.tf new file mode 100644 index 000000000..f04d7b193 --- /dev/null +++ b/examples/v0.14/VMWare/Network/IPv4/vm.tf @@ -0,0 +1,23 @@ +provider "vsphere" { + allow_unverified_ssl = true +} + +data"vsphere_datacenter" "dc"{ + name = "vRA-DC" +} + +data "vsphere_datastore" "datastore" { + name = "datastore_44" + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_resource_pool" "pool" { + name = "Blr-Cloud/Resources" + datacenter_id = data.vsphere_datacenter.dc.id + +} + +data "vsphere_network" "network" { + name = "VM Network" + datacenter_id = data.vsphere_datacenter.dc.id +} diff --git a/examples/v0.14/VMWare/Network/IPv6/infoblox.tf b/examples/v0.14/VMWare/Network/IPv6/infoblox.tf new file mode 100644 index 000000000..0ef0dfbcb --- /dev/null +++ b/examples/v0.14/VMWare/Network/IPv6/infoblox.tf @@ -0,0 +1,16 @@ +# Create an IPv6 Network in Infoblox Grid when CIDR is passed +resource "infoblox_ipv6_network" "ipv6_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv6_network_container.IPv6_nw_c.cidr + allocate_prefix_len = 64 + reserve_ipv6 = 3 + + comment = "tf IPv6 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} diff --git a/examples/v0.14/VMWare/Network/IPv6/versions.tf b/examples/v0.14/VMWare/Network/IPv6/versions.tf new file mode 100644 index 000000000..0a7dd1c3b --- /dev/null +++ b/examples/v0.14/VMWare/Network/IPv6/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + infoblox = { + source = "terraform-providers/infoblox" + version = ">= 1.0" + } + vsphere = { + source = "hashicorp/vsphere" + version = "1.12.0" + } + } +} diff --git a/examples/v0.14/VMWare/Network/IPv6/vm.tf b/examples/v0.14/VMWare/Network/IPv6/vm.tf new file mode 100644 index 000000000..f04d7b193 --- /dev/null +++ b/examples/v0.14/VMWare/Network/IPv6/vm.tf @@ -0,0 +1,23 @@ +provider "vsphere" { + allow_unverified_ssl = true +} + +data"vsphere_datacenter" "dc"{ + name = "vRA-DC" +} + +data "vsphere_datastore" "datastore" { + name = "datastore_44" + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_resource_pool" "pool" { + name = "Blr-Cloud/Resources" + datacenter_id = data.vsphere_datacenter.dc.id + +} + +data "vsphere_network" "network" { + name = "VM Network" + datacenter_id = data.vsphere_datacenter.dc.id +} diff --git a/examples/v0.14/VMWare/NetworkContainer/IPv4/infoblox.tf b/examples/v0.14/VMWare/NetworkContainer/IPv4/infoblox.tf new file mode 100644 index 000000000..bbb5e1642 --- /dev/null +++ b/examples/v0.14/VMWare/NetworkContainer/IPv4/infoblox.tf @@ -0,0 +1,13 @@ +# Create an IPv4 network container in Infoblox Grid +resource "infoblox_ipv4_network_container" "IPv4_nw_c" { + network_view_name="default" + + cidr = "10.0.0.0/16" + comment = "tf IPv4 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + diff --git a/examples/v0.14/VMWare/NetworkContainer/IPv4/versions.tf b/examples/v0.14/VMWare/NetworkContainer/IPv4/versions.tf new file mode 100644 index 000000000..0a7dd1c3b --- /dev/null +++ b/examples/v0.14/VMWare/NetworkContainer/IPv4/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + infoblox = { + source = "terraform-providers/infoblox" + version = ">= 1.0" + } + vsphere = { + source = "hashicorp/vsphere" + version = "1.12.0" + } + } +} diff --git a/examples/v0.14/VMWare/NetworkContainer/IPv4/vm.tf b/examples/v0.14/VMWare/NetworkContainer/IPv4/vm.tf new file mode 100644 index 000000000..f04d7b193 --- /dev/null +++ b/examples/v0.14/VMWare/NetworkContainer/IPv4/vm.tf @@ -0,0 +1,23 @@ +provider "vsphere" { + allow_unverified_ssl = true +} + +data"vsphere_datacenter" "dc"{ + name = "vRA-DC" +} + +data "vsphere_datastore" "datastore" { + name = "datastore_44" + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_resource_pool" "pool" { + name = "Blr-Cloud/Resources" + datacenter_id = data.vsphere_datacenter.dc.id + +} + +data "vsphere_network" "network" { + name = "VM Network" + datacenter_id = data.vsphere_datacenter.dc.id +} diff --git a/examples/v0.14/VMWare/NetworkContainer/IPv6/infoblox.tf b/examples/v0.14/VMWare/NetworkContainer/IPv6/infoblox.tf new file mode 100644 index 000000000..d5deadadd --- /dev/null +++ b/examples/v0.14/VMWare/NetworkContainer/IPv6/infoblox.tf @@ -0,0 +1,57 @@ +# Create a network container in Infoblox Grid +resource "infoblox_ipv4_network_container" "IPv4_nw_c" { + network_view_name="default" + + cidr = "10.0.0.0/16" + comment = "tf IPv4 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_network_container" "IPv6_nw_c" { + network_view_name="default" + + cidr = "2001:1890:1959:2710::/62" + comment = "tf IPv6 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + +# Allocate a network in Infoblox Grid under provided parent CIDR +resource "infoblox_ipv4_network" "ipv4_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv4_network_container.IPv4_nw_c.cidr + allocate_prefix_len = 24 + reserve_ip = 2 + + comment = "tf IPv4 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_network" "ipv6_network"{ + network_view_name = "default" + + parent_cidr = infoblox_ipv6_network_container.IPv6_nw_c.cidr + allocate_prefix_len = 64 + reserve_ipv6 = 3 + + comment = "tf IPv6 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} diff --git a/examples/v0.14/VMWare/NetworkContainer/IPv6/versions.tf b/examples/v0.14/VMWare/NetworkContainer/IPv6/versions.tf new file mode 100644 index 000000000..0a7dd1c3b --- /dev/null +++ b/examples/v0.14/VMWare/NetworkContainer/IPv6/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + infoblox = { + source = "terraform-providers/infoblox" + version = ">= 1.0" + } + vsphere = { + source = "hashicorp/vsphere" + version = "1.12.0" + } + } +} diff --git a/examples/v0.14/VMWare/NetworkContainer/IPv6/vm.tf b/examples/v0.14/VMWare/NetworkContainer/IPv6/vm.tf new file mode 100644 index 000000000..f04d7b193 --- /dev/null +++ b/examples/v0.14/VMWare/NetworkContainer/IPv6/vm.tf @@ -0,0 +1,23 @@ +provider "vsphere" { + allow_unverified_ssl = true +} + +data"vsphere_datacenter" "dc"{ + name = "vRA-DC" +} + +data "vsphere_datastore" "datastore" { + name = "datastore_44" + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_resource_pool" "pool" { + name = "Blr-Cloud/Resources" + datacenter_id = data.vsphere_datacenter.dc.id + +} + +data "vsphere_network" "network" { + name = "VM Network" + datacenter_id = data.vsphere_datacenter.dc.id +} diff --git a/examples/v0.14/VMWare/NextAvailableNetwork/infoblox.tf b/examples/v0.14/VMWare/NextAvailableNetwork/infoblox.tf index bee583bdd..d5deadadd 100644 --- a/examples/v0.14/VMWare/NextAvailableNetwork/infoblox.tf +++ b/examples/v0.14/VMWare/NextAvailableNetwork/infoblox.tf @@ -1,30 +1,57 @@ +# Create a network container in Infoblox Grid +resource "infoblox_ipv4_network_container" "IPv4_nw_c" { + network_view_name="default" + + cidr = "10.0.0.0/16" + comment = "tf IPv4 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + +resource "infoblox_ipv6_network_container" "IPv6_nw_c" { + network_view_name="default" + + cidr = "2001:1890:1959:2710::/62" + comment = "tf IPv6 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + Location = "Test loc." + Site = "Test site" + }) +} + # Allocate a network in Infoblox Grid under provided parent CIDR -resource "infoblox_network" "demo_network"{ +resource "infoblox_ipv4_network" "ipv4_network"{ network_view_name = "default" - network_name = "test_network" - tenant_id = "test" + + parent_cidr = infoblox_ipv4_network_container.IPv4_nw_c.cidr allocate_prefix_len = 24 - parent_cidr = "10.0.0.0/16" - reserve_ip = 2 -} + reserve_ip = 2 -# Allocate IP from network -resource "infoblox_ip_allocation" "demo_allocation"{ - network_view_name = "default" - vm_name = "terraform-demo" - cidr = infoblox_network.demo_network.cidr - tenant_id = "test" + comment = "tf IPv4 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv4-tf-network" + Location = "Test loc." + Site = "Test site" + }) } -# Update Grid with VM data -resource "infoblox_ip_association" "demo_associate"{ +resource "infoblox_ipv6_network" "ipv6_network"{ network_view_name = "default" - vm_name = infoblox_ip_allocation.demo_allocation.vm_name - cidr = infoblox_network.demo_network.cidr - mac_addr = vsphere_virtual_machine.vm.network_interface.0.mac_address - ip_addr = infoblox_ip_allocation.demo_allocation.ip_addr - vm_id = vsphere_virtual_machine.vm.id - tenant_id ="test" -} + parent_cidr = infoblox_ipv6_network_container.IPv6_nw_c.cidr + allocate_prefix_len = 64 + reserve_ipv6 = 3 + comment = "tf IPv6 network" + extensible_attributes = jsonencode({ + "Tenant ID" = "tf-plugin" + "Network Name" = "ipv6-tf-network" + Location = "Test loc." + Site = "Test site" + }) +} diff --git a/examples/v0.14/VMWare/NextAvailableNetwork/vm.tf b/examples/v0.14/VMWare/NextAvailableNetwork/vm.tf index fbb21ff78..61a9f9f31 100644 --- a/examples/v0.14/VMWare/NextAvailableNetwork/vm.tf +++ b/examples/v0.14/VMWare/NextAvailableNetwork/vm.tf @@ -33,6 +33,7 @@ data "vsphere_virtual_machine" "template" { datacenter_id = data.vsphere_datacenter.dc.id } +/* resource "vsphere_virtual_machine" "vm" { name = infoblox_ip_allocation.demo_allocation.vm_name resource_pool_id = data.vsphere_resource_pool.pool.id @@ -72,4 +73,4 @@ resource "vsphere_virtual_machine" "vm" { } } } - +*/ diff --git a/examples/v0.14/VMWare/README.md b/examples/v0.14/VMWare/README.md index 1e6f9ffc8..96393dc7a 100644 --- a/examples/v0.14/VMWare/README.md +++ b/examples/v0.14/VMWare/README.md @@ -13,12 +13,16 @@ export INFOBLOX_USERNAME="${username}" #Using the templates for different combinations. +- NetworkContainer : Create IPv4/IPv6 Network Containers +- Network : Create IPv4/IPv6 Network - To get next available network from a given parent CIDR of a prefix length use templates from NextAvailableNetwork. ### Note ``` A parent network container has to be in existence before requesting next available network from it. + +There are no datasources to obtain CIDR values from vsphere. Hence, the values have to be entered explicitly in infoblox.tf files. ``` # Running the Resource diff --git a/infoblox/datasource_infoblox_a_record.go b/infoblox/datasource_infoblox_a_record.go index 0a2bfbfde..7efe1bd7f 100644 --- a/infoblox/datasource_infoblox_a_record.go +++ b/infoblox/datasource_infoblox_a_record.go @@ -66,14 +66,16 @@ func dataSourceARecordRead(d *schema.ResourceData, m interface{}) error { connector := m.(*ibclient.Connector) - search_data := ibclient.NewRecordA( - ibclient.RecordA{ - Ipv4Addr: ip_addr, - Name: fqdn, - Zone: zone, - View: dnsView, - }) - err := connector.GetObject(search_data, "", &records) + aRec := ibclient.NewEmptyRecordA() + sf := map[string]string{ + "ipv4addr": ip_addr, + "name": fqdn, + "zone": zone, + "view": dnsView, + } + queryParams := ibclient.NewQueryParams(false, sf) + + err := connector.GetObject(aRec, "", queryParams, &records) d.SetId("") if err != nil { return fmt.Errorf("Read A record failed: %s", err) diff --git a/infoblox/datasource_infoblox_cname_record.go b/infoblox/datasource_infoblox_cname_record.go index da65e1efe..941ef6bb9 100644 --- a/infoblox/datasource_infoblox_cname_record.go +++ b/infoblox/datasource_infoblox_cname_record.go @@ -66,14 +66,15 @@ func dataSourceCNameRecordRead(d *schema.ResourceData, m interface{}) error { connector := m.(*ibclient.Connector) - search_data := ibclient.NewRecordCNAME( - ibclient.RecordCNAME{ - Canonical: canonical, - Name: fqdn, - Zone: zone, - View: dnsView, - }) - err := connector.GetObject(search_data, "", &records) + cnRec := ibclient.NewRecordCNAME(ibclient.RecordCNAME{}) + sf := map[string]string{ + "canonical": canonical, + "name": fqdn, + "zone": zone, + "view": dnsView, + } + queryParams := ibclient.NewQueryParams(false, sf) + err := connector.GetObject(cnRec, "", queryParams, &records) d.SetId("") if err != nil { return fmt.Errorf("Read CNAME record failed: %s", err) diff --git a/infoblox/datasource_infoblox_network.go b/infoblox/datasource_infoblox_network.go index 4607fbf53..cc38c78f0 100644 --- a/infoblox/datasource_infoblox_network.go +++ b/infoblox/datasource_infoblox_network.go @@ -44,7 +44,7 @@ func dataSourceNetworkRead(d *schema.ResourceData, m interface{}) error { objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) - obj, err := objMgr.GetNetwork(networkViewName, cidr, nil) + obj, err := objMgr.GetNetwork(networkViewName, cidr, false, nil) if err != nil { return fmt.Errorf("Getting Network block from network (%s) failed : %s", cidr, err) } diff --git a/infoblox/provider.go b/infoblox/provider.go index 541ba599d..1f5e3b423 100644 --- a/infoblox/provider.go +++ b/infoblox/provider.go @@ -8,6 +8,8 @@ import ( ibclient "github.com/infobloxopen/infoblox-go-client" ) +const defaultTenantId = "default_tenant" + //Provider returns a terraform.ResourceProvider. func Provider() terraform.ResourceProvider { return &schema.Provider{ @@ -63,13 +65,18 @@ func Provider() terraform.ResourceProvider { }, }, ResourcesMap: map[string]*schema.Resource{ - "infoblox_network": resourceNetwork(), - "infoblox_network_view": resourceNetworkView(), - "infoblox_ip_allocation": resourceIPAllocation(), - "infoblox_ip_association": resourceIPAssociation(), - "infoblox_a_record": resourceARecord(), - "infoblox_cname_record": resourceCNAMERecord(), - "infoblox_ptr_record": resourcePTRRecord(), + "infoblox_network_view": resourceNetworkView(), + "infoblox_ipv4_network_container": resourceIPv4NetworkContainer(), + "infoblox_ipv6_network_container": resourceIPv6NetworkContainer(), + "infoblox_ipv4_network": resourceIPv4Network(), + "infoblox_ipv6_network": resourceIPv6Network(), + "infoblox_ipv4_allocation": resourceIPv4Allocation(), + "infoblox_ipv6_allocation": resourceIPv6Allocation(), + "infoblox_ipv4_association": resourceIPv4Association(), + "infoblox_ipv6_association": resourceIPv6Association(), + "infoblox_a_record": resourceARecord(), + "infoblox_cname_record": resourceCNAMERecord(), + "infoblox_ptr_record": resourcePTRRecord(), }, DataSourcesMap: map[string]*schema.Resource{ "infoblox_network": dataSourceNetwork(), diff --git a/infoblox/resource_infoblox_ip_allocation.go b/infoblox/resource_infoblox_ip_allocation.go index f0b342d6f..e666631a5 100644 --- a/infoblox/resource_infoblox_ip_allocation.go +++ b/infoblox/resource_infoblox_ip_allocation.go @@ -1,228 +1,500 @@ package infoblox import ( + "encoding/json" "fmt" + "github.com/hashicorp/terraform/helper/schema" - "github.com/infobloxopen/infoblox-go-client" - "log" + ibclient "github.com/infobloxopen/infoblox-go-client" ) func resourceIPAllocation() *schema.Resource { return &schema.Resource{ - Create: resourceIPAllocationRequest, - Read: resourceIPAllocationGet, - Update: resourceIPAllocationUpdate, - Delete: resourceIPAllocationRelease, - Schema: map[string]*schema.Schema{ - "network_view_name": &schema.Schema{ + "network_view_name": { Type: schema.TypeString, Optional: true, Default: "default", - Description: "Network view name available in Nios server.", - }, - "vm_name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - Description: "The name of the VM.", - }, - "cidr": &schema.Schema{ - Type: schema.TypeString, - Required: true, - Description: "The address in cidr format.", + Description: "Network view name of NIOS server.", }, - "zone": &schema.Schema{ - Type: schema.TypeString, + "enable_dns": { + Type: schema.TypeBool, Optional: true, - Description: "Zone under which host record has to be created.", + Default: true, + Description: "flag that defines if the host record is to be used for DNS Purposes.", }, - "enable_dns": &schema.Schema{ + "enable_dhcp": { Type: schema.TypeBool, Optional: true, Default: false, - Description: "flag that defines if the host reocrd is used for DNS or IPAM Purposes.", + Description: "flag that defines if the host record is to be used for IPAM Purposes.", }, - "dns_view": &schema.Schema{ + "dns_view": { Type: schema.TypeString, Optional: true, Description: "Dns View under which the zone has been created.", }, - "ip_addr": &schema.Schema{ + "zone": { + Type: schema.TypeString, + Optional: true, + Description: "Zone under which host record has to be created.", + }, + "cidr": { + Type: schema.TypeString, + Required: true, + Description: "The address in cidr format.", + }, + "ip_addr": { Type: schema.TypeString, Optional: true, - Description: "IP address your instance in cloud.For static allocation ,set the field with valid IP. For dynamic allocation, leave this field empty.", + Description: "IP address of cloud instance. Set a valid IP for static allocation and leave empty if dynamically allocated.", Computed: true, }, - "mac_addr": &schema.Schema{ + "mac_addr": { Type: schema.TypeString, Optional: true, - Description: "mac address of your instance in cloud.", + Description: "MAC Address of cloud instance.", }, - "vm_id": &schema.Schema{ + "duid": { Type: schema.TypeString, Optional: true, - Description: "instance id.", + Description: "DHCP unique identifier for IPv6.", }, - "tenant_id": &schema.Schema{ + "host_name": { Type: schema.TypeString, Required: true, - Description: "Unique identifier of your tenant in cloud.", + Description: "The host name", + }, + "comment": { + Type: schema.TypeString, + Default: "", + Optional: true, + Description: "A description of the IP allocation.", + }, + "extensible_attributes": { + Type: schema.TypeString, + Default: "", + Optional: true, + Description: "The Extensible attributes of the network container to be added/updated, as a map in JSON format", }, }, } } -func resourceIPAllocationRequest(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning to request a next free IP from a required network block", resourceIPAllocationIDString(d)) - +func resourceIPAllocationRequest(d *schema.ResourceData, m interface{}, isIPv6 bool) error { networkViewName := d.Get("network_view_name").(string) - //This is for record Name - recordName := d.Get("vm_name").(string) - ipAddr := d.Get("ip_addr").(string) + dnsView := d.Get("dns_view").(string) + enableDns := d.Get("enable_dns").(bool) + enableDhcp := d.Get("enable_dhcp").(bool) + zone := d.Get("zone").(string) + hostName := d.Get("host_name").(string) + cidr := d.Get("cidr").(string) + ipAddr := d.Get("ip_addr").(string) macAddr := d.Get("mac_addr").(string) - //This is for EA's - vmName := d.Get("vm_name").(string) - vmID := d.Get("vm_id").(string) - tenantID := d.Get("tenant_id").(string) - zone := d.Get("zone").(string) - enableDns := d.Get("enable_dns").(bool) - dnsView := d.Get("dns_view").(string) + duid := d.Get("duid").(string) + + comment := d.Get("comment").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } - connector := m.(*ibclient.Connector) ZeroMacAddr := "00:00:00:00:00:00" - //fqdn - name := recordName + "." + zone - ea := make(ibclient.EA) - if vmName != "" { - ea["VM Name"] = vmName + connector := m.(*ibclient.Connector) + + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + } + } + + if hostName == "" { + return fmt.Errorf("'host_name' is mandatory to be passed for Allocation of IP") } - if vmID != "" { - ea["VM ID"] = vmID + + var recFQDN string + if len(zone) > 0 { + recFQDN = hostName + "." + zone + } else { + recFQDN = hostName } + if macAddr == "" { macAddr = ZeroMacAddr } + objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) - if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) { - hostAddressObj, err := objMgr.CreateHostRecord(enableDns, name, networkViewName, dnsView, cidr, ipAddr, macAddr, ea) - if err != nil { - return fmt.Errorf("Error allocating IP from network block(%s): %s", cidr, err) + if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) && enableDns { + if isIPv6 { + Obj, err := objMgr.CreateHostRecord( + enableDns, + enableDhcp, + recFQDN, + networkViewName, + dnsView, + "", cidr, + "", ipAddr, + "", duid, + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf( + "Error in allocating an IPv6 address and creating a host record in cidr %s: %s", cidr, err.Error()) + } + d.Set("ip_addr", Obj.Ipv6Addrs[0].Ipv6Addr) + d.SetId(Obj.Ref) + } else { + Obj, err := objMgr.CreateHostRecord( + enableDns, + enableDhcp, + recFQDN, + networkViewName, + dnsView, + cidr, "", + ipAddr, "", + macAddr, "", + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf( + "Error in allocating an IPv4 address and creating a host record in cidr %s: %s", cidr, err.Error()) + } + d.Set("ip_addr", Obj.Ipv4Addrs[0].Ipv4Addr) + d.SetId(Obj.Ref) + } + + } else if enableDhcp || !enableDns { + // default value of enableDns is true. When user sets enableDhcp as true and does not pass a enableDns flag + enableDns = false + + if isIPv6 { + Obj, err := objMgr.CreateHostRecord( + enableDns, + enableDhcp, + recFQDN, + networkViewName, + dnsView, + "", cidr, + "", ipAddr, + "", duid, + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf( + "Error in allocating an IPv6 address and creating a host record in cidr %s: %s", cidr, err.Error()) + } + d.Set("ip_addr", Obj.Ipv6Addrs[0].Ipv6Addr) + d.SetId(Obj.Ref) + } else { + Obj, err := objMgr.CreateHostRecord( + enableDns, + enableDhcp, + recFQDN, + networkViewName, + dnsView, + cidr, "", + ipAddr, "", + macAddr, "", + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf( + "Error in allocating an IPv4 address and creating a host record in cidr %s: %s", cidr, err.Error()) + } + d.Set("ip_addr", Obj.Ipv4Addrs[0].Ipv4Addr) + d.SetId(Obj.Ref) } - d.Set("ip_addr", hostAddressObj.Ipv4Addrs[0].Ipv4Addr) - d.SetId(hostAddressObj.Ref) + } else { - fixedAddressObj, err := objMgr.AllocateIP(networkViewName, cidr, ipAddr, macAddr, recordName, ea) - if err != nil { - return fmt.Errorf("Error allocating IP from network block(%s): %s", cidr, err) + if isIPv6 { + Obj, err := objMgr.AllocateIP(networkViewName, cidr, ipAddr, isIPv6, duid, hostName, comment, extAttrs) + if err != nil { + return fmt.Errorf("Error allocating IP from network block %s: %s", cidr, err.Error()) + } + d.Set("ip_addr", Obj.IPv6Address) + d.SetId(Obj.Ref) + } else { + Obj, err := objMgr.AllocateIP(networkViewName, cidr, ipAddr, isIPv6, macAddr, hostName, comment, extAttrs) + if err != nil { + return fmt.Errorf("Error allocating IP from network block %s: %s", cidr, err.Error()) + } + d.Set("ip_addr", Obj.IPv4Address) + d.SetId(Obj.Ref) } - d.Set("ip_addr", fixedAddressObj.IPAddress) - d.SetId(fixedAddressObj.Ref) } - log.Printf("[DEBUG] %s:completing Request of IP from required network block", resourceIPAllocationIDString(d)) - return resourceIPAllocationGet(d, m) + return nil } -func resourceIPAllocationGet(d *schema.ResourceData, m interface{}) error { - - log.Printf("[DEBUG] %s:Reading the required IP from network block", resourceIPAllocationIDString(d)) +func resourceIPAllocationGet(d *schema.ResourceData, m interface{}, isIPv6 bool) error { - tenantID := d.Get("tenant_id").(string) - cidr := d.Get("cidr").(string) - zone := d.Get("zone").(string) dnsView := d.Get("dns_view").(string) - connector := m.(*ibclient.Connector) + zone := d.Get("zone").(string) + cidr := d.Get("cidr").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break + } + } + + connector := m.(*ibclient.Connector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) { obj, err := objMgr.GetHostRecordByRef(d.Id()) if err != nil { - return fmt.Errorf("Error getting IP from network block(%s): %s", cidr, err) + return fmt.Errorf("Error getting IP from network block %s: %s", cidr, err.Error()) } d.SetId(obj.Ref) } else { obj, err := objMgr.GetFixedAddressByRef(d.Id()) if err != nil { - return fmt.Errorf("Error getting IP from network block(%s): %s", cidr, err) + return fmt.Errorf("Error getting IP from network block %s: %s", cidr, err.Error()) } d.SetId(obj.Ref) } - log.Printf("[DEBUG] %s: Completed Reading IP from the network block", resourceIPAllocationIDString(d)) return nil } -func resourceIPAllocationUpdate(d *schema.ResourceData, m interface{}) error { +func resourceIPAllocationUpdate(d *schema.ResourceData, m interface{}, isIPv6 bool) error { - match_client := "MAC_ADDRESS" - - log.Printf("[DEBUG] %s: Updating the Parameters of the allocated IP in the specified network block", resourceIPAllocationIDString(d)) + enableDns := d.Get("enable_dns").(bool) + enableDhcp := d.Get("enable_dhcp").(bool) + dnsView := d.Get("dns_view").(string) + zone := d.Get("zone").(string) + hostName := d.Get("host_name").(string) + duid := d.Get("duid").(string) macAddr := d.Get("mac_addr").(string) - tenantID := d.Get("tenant_id").(string) - vmID := d.Get("vm_id").(string) - vmName := d.Get("vm_name").(string) - zone := d.Get("zone").(string) - dnsView := d.Get("dns_view").(string) - connector := m.(*ibclient.Connector) + comment := d.Get("comment").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + } + } + + if hostName == "" { + return fmt.Errorf("'hostName' is mandatory to be passed for Allocation of IP") + } + + var recFQDN string + if len(zone) > 0 { + recFQDN = hostName + "." + zone + } else { + recFQDN = hostName + } + + connector := m.(*ibclient.Connector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) - if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) { + if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) && enableDns { hostRecordObj, _ := objMgr.GetHostRecordByRef(d.Id()) - IPAddrObj, _ := objMgr.GetIpAddressFromHostRecord(*hostRecordObj) - obj, err := objMgr.UpdateHostRecord(d.Id(), IPAddrObj, macAddr, vmID, vmName) - if err != nil { - return fmt.Errorf("Error updating IP from network block having reference (%s): %s", d.Id(), err) + + if isIPv6 { + IPAddrObj := hostRecordObj.Ipv6Addr + obj, err := objMgr.UpdateHostRecord( + d.Id(), + enableDns, + enableDhcp, + recFQDN, + "", IPAddrObj, + "", duid, + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf( + "Error updating Host record of IPv6 from network block having reference %s: %s", d.Id(), err.Error()) + } + d.SetId(obj.Ref) + } else { + IPAddrObj := hostRecordObj.Ipv4Addr + obj, err := objMgr.UpdateHostRecord( + d.Id(), + enableDns, + enableDhcp, + recFQDN, + IPAddrObj, "", + macAddr, "", + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf( + "Error updating Host record of IPv4 from network block having reference %s: %s", d.Id(), err.Error()) + } + d.SetId(obj.Ref) + } + } else if enableDhcp || !enableDns { + hostRecordObj, _ := objMgr.GetHostRecordByRef(d.Id()) + + // default value of enableDns is true. When user sets enableDhcp as true and does not pass a enableDns flag + enableDns = false + + if isIPv6 { + IPAddrObj := hostRecordObj.Ipv6Addr + obj, err := objMgr.UpdateHostRecord( + d.Id(), + enableDns, + enableDhcp, + recFQDN, + "", IPAddrObj, + "", duid, + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf( + "Error updating Host record of IPv6 from network block having reference %s: %s", d.Id(), err.Error()) + } + d.SetId(obj.Ref) + } else { + IPAddrObj := hostRecordObj.Ipv4Addr + obj, err := objMgr.UpdateHostRecord( + d.Id(), + enableDns, + enableDhcp, + recFQDN, + IPAddrObj, "", + macAddr, "", + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf( + "Error updating Host record of IPv4 from network block having reference %s: %s", d.Id(), err.Error()) + } + d.SetId(obj.Ref) } - d.SetId(obj) } else { - obj, err := objMgr.UpdateFixedAddress(d.Id(), match_client, macAddr, vmID, vmName) + var macOrDuid string + match_client := "MAC_ADDRESS" + if isIPv6 { + macOrDuid = duid + match_client = "" + } else { + macOrDuid = macAddr + } + obj, err := objMgr.UpdateFixedAddress(d.Id(), hostName, match_client, macOrDuid, comment, extAttrs) if err != nil { - return fmt.Errorf("Error updating IP from network block having reference (%s): %s", d.Id(), err) + return fmt.Errorf("Error updating IP from network block having reference %s: %s", d.Id(), err.Error()) } d.SetId(obj.Ref) } - log.Printf("[DEBUG] %s: Updation of Parameters of allocated IP complete in the specified network block", resourceIPAllocationIDString(d)) - return resourceIPAllocationGet(d, m) + return nil } -func resourceIPAllocationRelease(d *schema.ResourceData, m interface{}) error { +func resourceIPAllocationRelease(d *schema.ResourceData, m interface{}, isIPv6 bool) error { - log.Printf("[DEBUG] %s: Beginning Release of an allocated IP in the specified network block", resourceIPAllocationIDString(d)) - - tenantID := d.Get("tenant_id").(string) - zone := d.Get("zone").(string) dnsView := d.Get("dns_view").(string) - connector := m.(*ibclient.Connector) + zone := d.Get("zone").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValue := range extAttrs { + if attrName == "Tenant ID" { + tenantID = attrValue.(string) + break + } + } + connector := m.(*ibclient.Connector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) + if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) { _, err := objMgr.DeleteHostRecord(d.Id()) if err != nil { - return fmt.Errorf("Error Releasing IP from network block having reference (%s): %s", d.Id(), err) + return fmt.Errorf("Error Releasing IP from network block having reference %s: %s", d.Id(), err.Error()) } } else { _, err := objMgr.DeleteFixedAddress(d.Id()) if err != nil { - return fmt.Errorf("Error Releasing IP from network block having reference (%s): %s", d.Id(), err) + return fmt.Errorf("Error Releasing IP from network block having reference %s: %s", d.Id(), err.Error()) } } d.SetId("") - log.Printf("[DEBUG] %s: Finishing Release of allocated IP in the specified network block", resourceIPAllocationIDString(d)) return nil } -type resourceIPAllocationIDStringInterface interface { - Id() string +// Code snippet for IPv4 IP Allocation +func resourceIPv4AllocationRequest(d *schema.ResourceData, m interface{}) error { + return resourceIPAllocationRequest(d, m, false) } -func resourceIPAllocationIDString(d resourceIPAllocationIDStringInterface) string { - id := d.Id() - if id == "" { - id = "" - } - return fmt.Sprintf("infoblox_ip_allocation (ID = %s)", id) +func resourceIPv4AllocationGet(d *schema.ResourceData, m interface{}) error { + return resourceIPAllocationGet(d, m, false) +} + +func resourceIPv4AllocationUpdate(d *schema.ResourceData, m interface{}) error { + return resourceIPAllocationUpdate(d, m, false) +} + +func resourceIPv4AllocationRelease(d *schema.ResourceData, m interface{}) error { + return resourceIPAllocationRelease(d, m, false) +} + +func resourceIPv4Allocation() *schema.Resource { + ipv4Allocation := resourceIPAllocation() + ipv4Allocation.Create = resourceIPv4AllocationRequest + ipv4Allocation.Read = resourceIPv4AllocationGet + ipv4Allocation.Update = resourceIPv4AllocationUpdate + ipv4Allocation.Delete = resourceIPv4AllocationRelease + + return ipv4Allocation +} + +// Code snippet for IPv6 IP allocation +func resourceIPv6AllocationRequest(d *schema.ResourceData, m interface{}) error { + return resourceIPAllocationRequest(d, m, true) +} + +func resourceIPv6AllocationGet(d *schema.ResourceData, m interface{}) error { + return resourceIPAllocationGet(d, m, true) +} + +func resourceIPv6AllocationUpdate(d *schema.ResourceData, m interface{}) error { + return resourceIPAllocationUpdate(d, m, true) +} + +func resourceIPv6AllocationRelease(d *schema.ResourceData, m interface{}) error { + return resourceIPAllocationRelease(d, m, true) +} + +func resourceIPv6Allocation() *schema.Resource { + ipv6Allocation := resourceIPAllocation() + ipv6Allocation.Create = resourceIPv6AllocationRequest + ipv6Allocation.Read = resourceIPv6AllocationGet + ipv6Allocation.Update = resourceIPv6AllocationUpdate + ipv6Allocation.Delete = resourceIPv6AllocationRelease + + return ipv6Allocation } diff --git a/infoblox/resource_infoblox_ip_allocation_test.go b/infoblox/resource_infoblox_ip_allocation_test.go index 74436cd02..a502e3931 100644 --- a/infoblox/resource_infoblox_ip_allocation_test.go +++ b/infoblox/resource_infoblox_ip_allocation_test.go @@ -2,29 +2,394 @@ package infoblox import ( "fmt" + "reflect" + "sort" + "strings" + "testing" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/infobloxopen/infoblox-go-client" - "testing" + ibclient "github.com/infobloxopen/infoblox-go-client" +) + +var NotFoundTexts = []string{"404 Not Found", "not found"} + +const ( + eaListTypeString = iota + eaListTypeInt ) -func TestAccResourceIPAllocation(t *testing.T) { +var testAccresourceIPv4AllocationCreate = fmt.Sprintf(` +resource "infoblox_ipv4_allocation" "foo"{ + network_view_name="%s" + cidr="10.0.0.0/24" + ip_addr="10.0.0.1" + comment = "10.0.0.1 IP is allocated" + extensible_attributes = jsonencode({ + "VM Name" = "tf-ec2-instance" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) + }`, testNetView) + +var testAccresourceIPv4AllocationUpdate = fmt.Sprintf(` +resource "infoblox_ipv4_allocation" "foo"{ + network_view_name="%s" + cidr="10.0.0.0/24" + ip_addr="10.0.0.1" + comment = "10.0.0.1 IP is allocated updated" + extensible_attributes = jsonencode({ + "VM Name" = "tf-ec2-instance" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc. updated" + TestEA1 = "text3" + TestEA2 = 7 + }) + }`, testNetView) + +var testAccresourceIPv6AllocationCreate = fmt.Sprintf(` + resource "infoblox_ipv6_allocation" "foo"{ + network_view_name="%s" + cidr="2001:db8:abcd:12::/64" + ip_addr="2001:db8:abcd:12::1" + duid="11:22:33:44:55:66" + comment = "2001:db8:abcd:12::1 IP is allocated" + extensible_attributes = jsonencode({ + "VM Name" = "tf-ec2-instance-ipv6" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) + }`, testNetView) + +var testAccresourceIPv6AllocationUpdate = fmt.Sprintf(` + resource "infoblox_ipv6_allocation" "foo"{ + network_view_name="%s" + cidr="2001:db8:abcd:12::/64" + ip_addr="2001:db8:abcd:12::1" + duid="11:22:33:44:55:66" + comment = "2001:db8:abcd:12::1 IP is allocated updated" + extensible_attributes = jsonencode({ + "VM Name" = "tf-ec2-instance-ipv6" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc. updated" + TestEA1 = "text3" + TestEA2 = 7 + }) + }`, testNetView) + +func isNotFoundError(err error) bool { + if _, notFoundErr := err.(*ibclient.NotFoundError); notFoundErr { + return true + } + + // TODO: uncomment when infoblox-go-client will handle NotFoundError separately. + //return false + + errText := err.Error() + for _, text := range NotFoundTexts { + if strings.Contains(errText, text) { + return true + } + } + + return false +} + +func typesEqual(a, b interface{}) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) +} + +func sortList(s interface{}) error { + switch slice := s.(type) { + case []int: + sort.Ints(slice) + case []string: + stringSlice := s.([]string) + sort.Strings(stringSlice) + default: + return fmt.Errorf("expected value is of an unsupported type") + } + return nil +} + +func validateValues(actual, expected interface{}) (bool, error) { + switch expTyped := expected.(type) { + case int: + av := actual.(int) + if expTyped != av { + return false, nil + } + case bool: + av := actual.(bool) + if expTyped != av { + return false, nil + } + case string: + av := actual.(string) + if expTyped != av { + return false, nil + } + default: + return false, fmt.Errorf("expected value '%+v' is of an unsupported type", expected) + } + + return true, nil +} + +func validateEAs(actualEAs, expectedEAs ibclient.EA) error { + for eaKey, expEaVal := range expectedEAs { + actEaVal, found := actualEAs[eaKey] + if !found { + return fmt.Errorf( + "a value for extensible attribute '%s' not found, but expected to exist", eaKey) + } + + if !typesEqual(actEaVal, expEaVal) { + return fmt.Errorf("actual and expected values for extensible attribute '%s' have unequal types", eaKey) + } + + reflActEaVal := reflect.ValueOf(actEaVal) + switch reflActEaVal.Kind() { + case reflect.Slice: + var eaListType int + + switch actEaVal.(type) { + case []int: + eaListType = eaListTypeInt + case []string: + eaListType = eaListTypeString + default: + return fmt.Errorf("unsupported type for 'extensible_attributes' field value: %+v", actEaVal) + } + + reflExpEaVal := reflect.ValueOf(expEaVal) + if reflActEaVal.Len() != reflExpEaVal.Len() { + return fmt.Errorf( + "the value of extensible attribute '%s' is not equal to the expected one", eaKey) + } + numItems := reflExpEaVal.Len() + if numItems == 0 { + return nil + } + if err := sortList(actEaVal.(interface{})); err != nil { + return err + } + if err := sortList(expEaVal.(interface{})); err != nil { + return err + } + + getElemFunc := func(slice interface{}, idx int) interface{} { + switch eaListType { + case eaListTypeInt: + return slice.([]int)[idx] + case eaListTypeString: + return slice.([]string)[idx] + default: + panic("unexpected slice item's type") + } + } + + for i := 0; i < numItems; i++ { + expVal := getElemFunc(expEaVal, i) + actVal := getElemFunc(actEaVal, i) + equal, err := validateValues(actVal, expVal) + if err != nil { + return err + } + if !equal { + return fmt.Errorf( + "the value for extensible attribute '%v' is '%v' but expected to be '%v'", + eaKey, actEaVal, expEaVal) + } + return nil + } + return nil + default: + equal, err := validateValues(actEaVal, expEaVal) + if err != nil { + return err + } + if !equal { + return fmt.Errorf( + "the value for extensible attribute '%v' is '%v' but expected to be '%v'", + eaKey, actEaVal, expEaVal) + } + } + } + + return nil +} + +func validateIPAllocation( + resourceName string, + expectedValue *ibclient.FixedAddress) resource.TestCheckFunc { + return func(s *terraform.State) error { + res, found := s.RootModule().Resources[resourceName] + if !found { + return fmt.Errorf("not found: %s", resourceName) + } + + id := res.Primary.ID + if id == "" { + return fmt.Errorf("ID is not set") + } + + connector := testAccProvider.Meta().(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager( + connector, + "terraform_test", + "terraform_test_tenant") + ipAlloc, err := objMgr.GetFixedAddressByRef(id) + if err != nil { + if isNotFoundError(err) { + if expectedValue == nil { + return nil + } + return fmt.Errorf("object with ID '%s' not found, but expected to exist", id) + } + } + expNv := expectedValue.NetviewName + if ipAlloc.NetviewName != expNv { + return fmt.Errorf( + "the value of 'network_view_name' field is '%s', but expected '%s'", + ipAlloc.NetviewName, expNv) + } + + expComment := expectedValue.Comment + if ipAlloc.Comment != expComment { + return fmt.Errorf( + "the value of 'comment' field is '%s', but expected '%s'", + ipAlloc.Comment, expComment) + } + + expIPv4Address := expectedValue.IPv4Address + expIPv6Address := expectedValue.IPv6Address + if ipAlloc.IPv4Address != expIPv4Address || ipAlloc.IPv6Address != expIPv6Address { + return fmt.Errorf( + "the value of 'IPv4Address' field is '%s', but expected '%s'or 'IPv6Address' field is '%s', but expected %s", + ipAlloc.IPv4Address, expIPv4Address, ipAlloc.IPv6Address, expIPv6Address) + } + + // the rest is about extensible attributes + expectedEAs := expectedValue.Ea + if expectedEAs == nil && ipAlloc.Ea != nil { + return fmt.Errorf( + "the object with ID '%s' has 'extensible_attributes' field, but it is not expected to exist", id) + } + if expectedEAs != nil && ipAlloc.Ea == nil { + return fmt.Errorf( + "the object with ID '%s' has no 'extensible_attributes' field, but it is expected to exist", id) + } + if expectedEAs == nil { + return nil + } + + return validateEAs(ipAlloc.Ea, expectedEAs) + } +} + +func TestAcc_resourceIPAllocation_ipv4(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIPAllocationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccresourceIPv4AllocationCreate, + Check: validateIPAllocation( + "infoblox_ipv4_allocation.foo", + &ibclient.FixedAddress{ + NetviewName: testNetView, + Cidr: "10.0.0.0/24", + Comment: "10.0.0.1 IP is allocated", + IPv4Address: "10.0.0.1", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "VM Name": "tf-ec2-instance", + "Location": "Test loc.", + "Site": "Test site", + "TestEA1": []string{"text1", "text2"}, + "TestEA2": []int{4, 5}, + }, + }, + ), + }, + { + Config: testAccresourceIPv4AllocationUpdate, + Check: validateIPAllocation( + "infoblox_ipv4_allocation.foo", + &ibclient.FixedAddress{ + NetviewName: testNetView, + Cidr: "10.0.0.0/24", + Comment: "10.0.0.1 IP is allocated updated", + IPv4Address: "10.0.0.1", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "VM Name": "tf-ec2-instance", + "Location": "Test loc. updated", + // lists which contain ony one element are reduced by NIOS to a single-value element + "TestEA1": "text3", + "TestEA2": 7, + }, + }, + ), + }, + }, + }) +} +func TestAcc_resourceIPAllocation_ipv6(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckIPAllocationDestroy, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccresourceIPAllocationCreate, - Check: resource.ComposeTestCheckFunc( - testAccIPExists(t, "infoblox_ip_allocation.foo", "10.0.0.1/24", "10.0.0.1", "default", "demo-network"), + { + Config: testAccresourceIPv6AllocationCreate, + Check: validateIPAllocation( + "infoblox_ipv6_allocation.foo", + &ibclient.FixedAddress{ + NetviewName: testNetView, + Cidr: "2001:db8:abcd:12::/64", + Comment: "2001:db8:abcd:12::1 IP is allocated", + IPv6Address: "2001:db8:abcd:12::1", + Duid: "11:22:33:44:55:66", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "VM Name": "tf-ec2-instance-ipv6", + "Location": "Test loc.", + "Site": "Test site", + "TestEA1": []string{"text1", "text2"}, + "TestEA2": []int{4, 5}, + }, + }, ), }, - resource.TestStep{ - Config: testAccresourceIPAllocationUpdate, - Check: resource.ComposeTestCheckFunc( - testAccIPExists(t, "infoblox_ip_allocation.foo", "10.0.0.1/24", "10.0.0.1", "default", "demo-network"), + { + Config: testAccresourceIPv6AllocationUpdate, + Check: validateIPAllocation( + "infoblox_ipv6_allocation.foo", + &ibclient.FixedAddress{ + NetviewName: testNetView, + Cidr: "2001:db8:abcd:12::/64", + Comment: "2001:db8:abcd:12::1 IP is allocated updated", + IPv6Address: "2001:db8:abcd:12::1", + Duid: "11:22:33:44:55:66", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "VM Name": "tf-ec2-instance-ipv6", + "Location": "Test loc. updated", + // lists which contain ony one element are reduced by NIOS to a single-value element + "TestEA1": "text3", + "TestEA2": 7, + }, + }, ), }, }, @@ -32,57 +397,25 @@ func TestAccResourceIPAllocation(t *testing.T) { } func testAccCheckIPAllocationDestroy(s *terraform.State) error { - meta := testAccProvider.Meta() + connector := testAccProvider.Meta().(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager( + connector, + "terraform_test", + "terraform_test_tenant") for _, rs := range s.RootModule().Resources { - if rs.Type != "resource_ip_allocation" { + if rs.Type != "infoblox_ipv4_allocation" && rs.Type != "infoblox_ipv6_allocation" { continue } - Connector := meta.(*ibclient.Connector) - objMgr := ibclient.NewObjectManager(Connector, "terraform_test", "test") - recordName, _ := objMgr.GetFixedAddress("default", "10.0.0.0/24", "10.0.0.2", "") - if recordName == nil { - return fmt.Errorf("record not found") - } - - } - return nil -} -func testAccIPExists(t *testing.T, n string, cidr string, ipAddr string, networkViewName string, recordName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found:%s", n) - } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID i set") + res, err := objMgr.GetFixedAddressByRef(rs.Primary.ID) + if err != nil { + if isNotFoundError(err) { + continue + } + return err } - meta := testAccProvider.Meta() - Connector := meta.(*ibclient.Connector) - objMgr := ibclient.NewObjectManager(Connector, "terraform_test", "test") - - recordName, _ := objMgr.GetFixedAddress(networkViewName, cidr, ipAddr, "") - if recordName != nil { - return fmt.Errorf("record not found") + if res != nil { + return fmt.Errorf("object with ID '%s' remains", rs.Primary.ID) } - - return nil } + return nil } - -var testAccresourceIPAllocationCreate = fmt.Sprintf(` -resource "infoblox_ip_allocation" "foo"{ - network_view_name="default" - vm_name="test-name" - cidr="10.0.0.0/24" - ip_addr="10.0.0.1" - tenant_id="foo" - }`) - -var testAccresourceIPAllocationUpdate = fmt.Sprintf(` -resource "infoblox_ip_allocation" "foo"{ - network_view_name="default" - vm_name="test-name" - cidr="10.0.0.0/24" - ip_addr="10.0.0.1" - tenant_id="foo" - }`) diff --git a/infoblox/resource_infoblox_ip_association.go b/infoblox/resource_infoblox_ip_association.go index ba9437aad..1a2c934b9 100644 --- a/infoblox/resource_infoblox_ip_association.go +++ b/infoblox/resource_infoblox_ip_association.go @@ -1,8 +1,8 @@ package infoblox import ( + "encoding/json" "fmt" - "log" "strings" "github.com/hashicorp/terraform/helper/schema" @@ -11,22 +11,34 @@ import ( func resourceIPAssociation() *schema.Resource { return &schema.Resource{ - Create: resourceIPAssociationCreate, - Update: resourceIPAssociationUpdate, - Delete: resourceIPAssociationDelete, - Read: resourceIPAssociationRead, - Schema: map[string]*schema.Schema{ "network_view_name": &schema.Schema{ Type: schema.TypeString, Optional: true, Default: "default", - Description: "Network view name available in Nios server.", + Description: "Network view name of NIOS server.", + }, + "enable_dns": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "flag that defines if the host record is to be used for DNS Purposes", + }, + "enable_dhcp": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "flag that defines if the host record is to be used for IPAM Purposes.", }, - "vm_name": &schema.Schema{ + "dns_view": &schema.Schema{ Type: schema.TypeString, Optional: true, - Description: "The name of the vm.", + Description: "view in which record has to be created.", + }, + "zone": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "zone under which record has been created.", }, "cidr": &schema.Schema{ Type: schema.TypeString, @@ -36,32 +48,34 @@ func resourceIPAssociation() *schema.Resource { "ip_addr": &schema.Schema{ Type: schema.TypeString, Required: true, - Description: "IP address your instance in cloud.", + Description: "IP address of cloud instance.", }, "mac_addr": &schema.Schema{ Type: schema.TypeString, - Required: true, - Description: "mac address of your instance in cloud.", + Optional: true, + Description: "mac address of cloud instance.", }, - "dns_view": &schema.Schema{ + "duid": &schema.Schema{ Type: schema.TypeString, Optional: true, - Description: "view in which record has to be created.", + Description: "DHCP unique identifier for IPv6.", }, - "zone": &schema.Schema{ + "host_name": { Type: schema.TypeString, - Optional: true, - Description: "zone under which record has been created.", + Required: true, + Description: "The host name", }, - "vm_id": &schema.Schema{ + "comment": { Type: schema.TypeString, + Default: "", Optional: true, - Description: "instance id.", + Description: "A description of the IP association.", }, - "tenant_id": &schema.Schema{ + "extensible_attributes": { Type: schema.TypeString, - Required: true, - Description: "Unique identifier of your tenant in cloud.", + Default: "", + Optional: true, + Description: "The Extensible attributes of the network container to be added/updated, as a map in JSON format", }, }, } @@ -70,153 +84,421 @@ func resourceIPAssociation() *schema.Resource { //This method has an update call for the reason that,we are creating //a reservation which doesnt have the details of the mac address //at the beginig and we are using this update call to update the mac address -//of the record after the VM has been provisined.It is in the create method +//of the record after the VM has been provisined. It is in the create method //because for this resource we are doing association instead of allocation. -func resourceIPAssociationCreate(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning Association of IP address in specified network block", resourceIPAssociationIDString(d)) +func resourceIPAssociationCreate(d *schema.ResourceData, m interface{}, isIPv6 bool) error { - if err := Resource(d, m); err != nil { + if err := Resource(d, m, isIPv6); err != nil { return err } - log.Printf("[DEBUG] %s:completing Association of IP address in specified network block", resourceIPAssociationIDString(d)) - return resourceIPAssociationRead(d, m) + return nil } -func resourceIPAssociationUpdate(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s:update operation on Association of IP address in specified network block", resourceIPAssociationIDString(d)) +func resourceIPAssociationUpdate(d *schema.ResourceData, m interface{}, isIPv6 bool) error { - if err := Resource(d, m); err != nil { + if err := Resource(d, m, isIPv6); err != nil { return err } - log.Printf("[DEBUG] %s:completing updation on Association of IP address in specified network block", resourceIPAssociationIDString(d)) - return resourceIPAssociationRead(d, m) + return nil } func resourceIPAssociationRead(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s:Reading the required IP from network block", resourceIPAllocationIDString(d)) - tenantID := d.Get("tenant_id").(string) + enableDns := d.Get("enable_dns").(bool) + enableDhcp := d.Get("enable_dhcp").(bool) + hostName := d.Get("host_name").(string) + cidr := d.Get("cidr").(string) - zone := d.Get("zone").(string) dnsView := d.Get("dns_view").(string) - connector := m.(*ibclient.Connector) + zone := d.Get("zone").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + } + } + + if hostName == "" { + return fmt.Errorf("'host_name' is mandatory to be passed for Association of IP") + } + connector := m.(*ibclient.Connector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) - if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) { + if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) && enableDns { obj, err := objMgr.GetHostRecordByRef(d.Id()) if err != nil { - return fmt.Errorf("Error getting IP from network block(%s): %s", cidr, err) + return fmt.Errorf("Error getting IP from network block %s: %s", cidr, err.Error()) + } + d.SetId(obj.Ref) + } else if enableDhcp || !enableDns { + obj, err := objMgr.GetHostRecordByRef(d.Id()) + if err != nil { + return fmt.Errorf("Error getting IP from network block %s: %s", cidr, err.Error()) } d.SetId(obj.Ref) } else { obj, err := objMgr.GetFixedAddressByRef(d.Id()) if err != nil { - return fmt.Errorf("Error getting IP from network block(%s): %s", cidr, err) + return fmt.Errorf("Error getting IP from network block %s: %s", cidr, err.Error()) } d.SetId(obj.Ref) } - log.Printf("[DEBUG] %s: Completed Reading IP from the network block", resourceIPAllocationIDString(d)) return nil } //we are updating the record with an empty mac address after the vm has been //destroyed because if we implement the delete hostrecord method here then there //will be a conflict of resources -func resourceIPAssociationDelete(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning Reassociation of IP address in specified network block", resourceIPAssociationIDString(d)) - matchClient := "MAC_ADDRESS" +func resourceIPAssociationDelete(d *schema.ResourceData, m interface{}, isIPv6 bool) error { + ipAddr := d.Get("ip_addr").(string) - vmID := d.Get("vm_id").(string) - vmName := d.Get("vm_name").(string) - tenantID := d.Get("tenant_id").(string) - zone := d.Get("zone").(string) + hostName := d.Get("host_name").(string) + + enableDns := d.Get("enable_dns").(bool) + enableDhcp := d.Get("enable_dhcp").(bool) dnsView := d.Get("dns_view").(string) + zone := d.Get("zone").(string) + duid := d.Get("duid").(string) - connector := m.(*ibclient.Connector) + comment := d.Get("comment").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + } + } + + if hostName == "" { + return fmt.Errorf("'host_name' is mandatory to be passed for removal of Association") + } + + var recFQDN string + if len(zone) > 0 { + recFQDN = hostName + "." + zone + } else { + recFQDN = hostName + } ZeroMacAddr := "00:00:00:00:00:00" + connector := m.(*ibclient.Connector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) - if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) { - _, err := objMgr.UpdateHostRecord(d.Id(), ipAddr, ZeroMacAddr, vmID, vmName) - if err != nil { - return fmt.Errorf("Error Releasing IP from network block having reference (%s): %s", d.Id(), err) + if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) && enableDns { + if isIPv6 { + obj, err := objMgr.UpdateHostRecord( + d.Id(), + enableDns, + enableDhcp, + recFQDN, + "", ipAddr, + "", duid, + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf("Error updating Host record of IP from network block having reference %s: %s", d.Id(), err.Error()) + } + d.SetId(obj.Ref) + } else { + obj, err := objMgr.UpdateHostRecord( + d.Id(), + enableDns, + enableDhcp, + recFQDN, + ipAddr, "", + ZeroMacAddr, "", + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf("Error updating Host record of IP from network block having reference %s: %s", d.Id(), err.Error()) + } + d.SetId(obj.Ref) + } + } else if enableDhcp || !enableDns { + // default value of enableDns is true. When user sets enableDhcp as true and does not pass a enableDns flag + enableDns = false + + if isIPv6 { + obj, err := objMgr.UpdateHostRecord( + d.Id(), + enableDns, + enableDhcp, + recFQDN, + "", ipAddr, + "", duid, + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf("Error updating Host record of IP from network block having reference %s: %s", d.Id(), err.Error()) + } + d.SetId(obj.Ref) + } else { + obj, err := objMgr.UpdateHostRecord( + d.Id(), + enableDns, + enableDhcp, + recFQDN, + ipAddr, "", + ZeroMacAddr, "", + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf("Error updating Host record of IP from network block having reference %s: %s", d.Id(), err.Error()) + } + d.SetId(obj.Ref) } - d.SetId("") } else { - _, err := objMgr.UpdateFixedAddress(d.Id(), matchClient, ZeroMacAddr, "", "") - if err != nil { - return fmt.Errorf("Error Releasing IP from network block having reference (%s): %s", d.Id(), err) + matchClient := "MAC_ADDRESS" + if isIPv6 { + matchClient = "" + _, err := objMgr.UpdateFixedAddress(d.Id(), hostName, matchClient, duid, comment, extAttrs) + if err != nil { + return fmt.Errorf("Error Releasing IP from network block having reference %s: %s", d.Id(), err.Error()) + } + } else { + _, err := objMgr.UpdateFixedAddress(d.Id(), hostName, matchClient, ZeroMacAddr, comment, extAttrs) + if err != nil { + return fmt.Errorf("Error Releasing IP from network block having reference %s: %s", d.Id(), err.Error()) + } } d.SetId("") } - log.Printf("[DEBUG] %s: Finishing Release of allocated IP in specified network block", resourceIPAssociationIDString(d)) return nil } -type resourceIPAssociationIDStringInterface interface { - Id() string -} - -func resourceIPAssociationIDString(d resourceIPAssociationIDStringInterface) string { - id := d.Id() - if id == "" { - id = "" - } - return fmt.Sprintf("infoblox_mac_allocation (ID = %s)", id) -} +func Resource(d *schema.ResourceData, m interface{}, isIPv6 bool) error { -func Resource(d *schema.ResourceData, m interface{}) error { - - matchClient := "MAC_ADDRESS" networkViewName := d.Get("network_view_name").(string) - Name := d.Get("vm_name").(string) - ipAddr := d.Get("ip_addr").(string) + enableDns := d.Get("enable_dns").(bool) + enableDhcp := d.Get("enable_dhcp").(bool) + dnsView := d.Get("dns_view").(string) + zone := d.Get("zone").(string) + hostName := d.Get("host_name").(string) + cidr := d.Get("cidr").(string) + ipAddr := d.Get("ip_addr").(string) macAddr := d.Get("mac_addr").(string) - tenantID := d.Get("tenant_id").(string) - vmID := d.Get("vm_id").(string) - zone := d.Get("zone").(string) - dnsView := d.Get("dns_view").(string) + duid := d.Get("duid").(string) - connector := m.(*ibclient.Connector) + comment := d.Get("comment").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValue := range extAttrs { + attrValue, _ := attrValue.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + } + } + + if hostName == "" { + return fmt.Errorf("'host_name' is mandatory to be passed for Association") + } - objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) //conversion from bit reversed EUI-48 format to hexadecimal EUI-48 format macAddr = strings.Replace(macAddr, "-", ":", -1) - name := Name + "." + zone + var recFQDN string + if len(zone) > 0 { + recFQDN = hostName + "." + zone + } else { + recFQDN = hostName + } - if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) { - hostRecordObj, err := objMgr.GetHostRecord(name) - if err != nil { - return fmt.Errorf("GetHostRecord failed from network block(%s):%s", cidr, err) - } - if hostRecordObj == nil { - return fmt.Errorf("HostRecord %s not found.", name) + connector := m.(*ibclient.Connector) + objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) + + //var err error + if (zone != "" || len(zone) != 0) && (dnsView != "" || len(dnsView) != 0) && enableDns { + if isIPv6 { + hostRecordObj, err := objMgr.GetHostRecord(recFQDN, "", ipAddr) + if err != nil { + return fmt.Errorf("GetHostRecord failed from IPv6 network block %s:%s", cidr, err.Error()) + } + if hostRecordObj == nil { + return fmt.Errorf("HostRecord %s not found.", recFQDN) + } + Obj, err := objMgr.UpdateHostRecord( + hostRecordObj.Ref, + enableDns, + enableDhcp, + recFQDN, + "", ipAddr, + "", duid, + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf("UpdateHost Record failed from IPv6 network block %s: %s", cidr, err.Error()) + } + d.SetId(Obj.Ref) + } else { + hostRecordObj, err := objMgr.GetHostRecord(recFQDN, ipAddr, "") + if err != nil { + return fmt.Errorf("GetHostRecord failed from IPv4 network block %s:%s", cidr, err.Error()) + } + if hostRecordObj == nil { + return fmt.Errorf("HostRecord %s not found.", recFQDN) + } + Obj, err := objMgr.UpdateHostRecord( + hostRecordObj.Ref, + enableDns, + enableDhcp, + recFQDN, + ipAddr, "", + macAddr, "", + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf("UpdateHost Record failed from network block %s:%s", cidr, err.Error()) + } + d.SetId(Obj.Ref) } - _, err = objMgr.UpdateHostRecord(hostRecordObj.Ref, ipAddr, macAddr, vmID, Name) - if err != nil { - return fmt.Errorf("UpdateHost Record error from network block(%s):%s", cidr, err) + } else if enableDhcp || !enableDns { + // default value of enableDns is true. When user sets enableDhcp as true and does not pass a enableDns flag + enableDns = false + + if isIPv6 { + hostRecordObj, err := objMgr.GetHostRecord(recFQDN, "", ipAddr) + if err != nil { + return fmt.Errorf("GetHostRecord failed from IPv6 network block %s:%s", cidr, err.Error()) + } + if hostRecordObj == nil { + return fmt.Errorf("HostRecord %s not found.", recFQDN) + } + Obj, err := objMgr.UpdateHostRecord( + hostRecordObj.Ref, + enableDns, + enableDhcp, + recFQDN, + "", ipAddr, + "", duid, + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf("UpdateHost Record failed from IPv6 network block %s: %s", cidr, err.Error()) + } + d.SetId(Obj.Ref) + } else { + hostRecordObj, err := objMgr.GetHostRecord(recFQDN, ipAddr, "") + if err != nil { + return fmt.Errorf("GetHostRecord failed from IPv4 network block %s:%s", cidr, err.Error()) + } + if hostRecordObj == nil { + return fmt.Errorf("HostRecord %s not found.", recFQDN) + } + Obj, err := objMgr.UpdateHostRecord( + hostRecordObj.Ref, + enableDns, + enableDhcp, + recFQDN, + ipAddr, "", + macAddr, "", + comment, + extAttrs, []string{}) + if err != nil { + return fmt.Errorf("UpdateHost Record failed from network block %s:%s", cidr, err.Error()) + } + d.SetId(Obj.Ref) } - d.SetId(hostRecordObj.Ref) } else { - fixedAddressObj, err := objMgr.GetFixedAddress(networkViewName, cidr, ipAddr, "") + fixedAddressObj, err := objMgr.GetFixedAddress(networkViewName, cidr, ipAddr, isIPv6, "") if err != nil { - return fmt.Errorf("GetFixedAddress error from network block(%s):%s", cidr, err) + return fmt.Errorf("GetFixedAddress failed from network block %s:%s", cidr, err.Error()) } if fixedAddressObj == nil { return fmt.Errorf("FixedAddress %s not found in network %s.", ipAddr, cidr) } - _, err = objMgr.UpdateFixedAddress(fixedAddressObj.Ref, matchClient, macAddr, vmID, Name) + var macOrDuid string + macOrDuid = macAddr + matchClient := "MAC_ADDRESS" + if isIPv6 { + matchClient = "" + macOrDuid = duid + } + _, err = objMgr.UpdateFixedAddress(fixedAddressObj.Ref, hostName, matchClient, macOrDuid, comment, extAttrs) if err != nil { - return fmt.Errorf("UpdateFixedAddress error from network block(%s):%s", cidr, err) + return fmt.Errorf("UpdateFixedAddress error from network block %s:%s", cidr, err.Error()) } d.SetId(fixedAddressObj.Ref) } return nil } + +// Code snippet for IPv4 IP Association +func resourceIPv4AssociationCreate(d *schema.ResourceData, m interface{}) error { + return resourceIPAssociationCreate(d, m, false) +} + +func resourceIPv4AssociationGet(d *schema.ResourceData, m interface{}) error { + return resourceIPAssociationRead(d, m) +} + +func resourceIPv4AssociationUpdate(d *schema.ResourceData, m interface{}) error { + return resourceIPAssociationUpdate(d, m, false) +} + +func resourceIPv4AssociationDelete(d *schema.ResourceData, m interface{}) error { + return resourceIPAssociationDelete(d, m, false) +} + +func resourceIPv4Association() *schema.Resource { + ipv4Association := resourceIPAssociation() + ipv4Association.Create = resourceIPv4AssociationCreate + ipv4Association.Read = resourceIPv4AssociationGet + ipv4Association.Update = resourceIPv4AssociationUpdate + ipv4Association.Delete = resourceIPv4AssociationDelete + + return ipv4Association +} + +// Code snippet for IPv6 IP Association +func resourceIPv6AssociationCreate(d *schema.ResourceData, m interface{}) error { + return resourceIPAssociationCreate(d, m, true) +} + +func resourceIPv6AssociationRead(d *schema.ResourceData, m interface{}) error { + return resourceIPAssociationRead(d, m) +} + +func resourceIPv6AssociationUpdate(d *schema.ResourceData, m interface{}) error { + return resourceIPAssociationUpdate(d, m, true) +} + +func resourceIPv6AssociationDelete(d *schema.ResourceData, m interface{}) error { + return resourceIPAssociationDelete(d, m, true) +} + +func resourceIPv6Association() *schema.Resource { + ipv6Association := resourceIPAssociation() + ipv6Association.Create = resourceIPv6AssociationCreate + ipv6Association.Read = resourceIPv6AssociationRead + ipv6Association.Update = resourceIPv6AssociationUpdate + ipv6Association.Delete = resourceIPv6AssociationDelete + + return ipv6Association +} diff --git a/infoblox/resource_infoblox_ip_association_test.go b/infoblox/resource_infoblox_ip_association_test.go index ab84fb57f..2ff20a53f 100644 --- a/infoblox/resource_infoblox_ip_association_test.go +++ b/infoblox/resource_infoblox_ip_association_test.go @@ -2,89 +2,279 @@ package infoblox import ( "fmt" + "testing" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/infobloxopen/infoblox-go-client" - "testing" + ibclient "github.com/infobloxopen/infoblox-go-client" ) -func TestAccresourceIPAssociation(t *testing.T) { +var testAccresourceIPv4AssociationCreate = fmt.Sprintf(` +resource "infoblox_ipv4_association" "foo"{ + network_view_name="%s" + cidr="10.0.0.0/24" + ip_addr="10.0.0.1" + mac_addr="11:22:33:44:55:66" + comment = "10.0.0.1 IP is associated " + extensible_attributes = jsonencode({ + "VM Name" = "tf-ec2-instance" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) + }`, testNetView) + +var testAccresourceIPv4AssociationUpdate = fmt.Sprintf(` +resource "infoblox_ipv4_association" "foo"{ + network_view_name="%s" + cidr="10.0.0.0/24" + ip_addr="10.0.0.1" + mac_addr="11:22:33:44:55:66" + comment = "10.0.0.1 IP is associated updated" + extensible_attributes = jsonencode({ + "VM Name" = "tf-ec2-instance" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc. updated" + TestEA1 = "text3" + TestEA2 = 7 + }) + }`, testNetView) + +var testAccresourceIPv6AssociationCreate = fmt.Sprintf(` + resource "infoblox_ipv6_association" "foo"{ + network_view_name="%s" + cidr="2001:db8:abcd:12::/64" + ip_addr="2001:db8:abcd:12::1" + duid="11:22:33:44:55:66" + comment = "2001:db8:abcd:12::1 IP is associated" + extensible_attributes = jsonencode({ + "VM Name" = "tf-ec2-instance-ipv6" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) + }`, testNetView) + +var testAccresourceIPv6AssociationUpdate = fmt.Sprintf(` + resource "infoblox_ipv6_association" "foo"{ + network_view_name="%s" + cidr="2001:db8:abcd:12::/64" + ip_addr="2001:db8:abcd:12::1" + duid="11:22:33:44:55:66" + comment = "2001:db8:abcd:12::1 IP is associated updated" + extensible_attributes = jsonencode({ + "VM Name" = "tf-ec2-instance-ipv6" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc. updated" + TestEA1 = "text3" + TestEA2 = 7 + }) + }`, testNetView) + +func validateIPAssociation( + resourceName string, + expectedValue *ibclient.FixedAddress) resource.TestCheckFunc { + return func(s *terraform.State) error { + res, found := s.RootModule().Resources[resourceName] + if !found { + return fmt.Errorf("not found: %s", resourceName) + } + + id := res.Primary.ID + if id == "" { + return fmt.Errorf("ID is not set") + } + connector := testAccProvider.Meta().(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager( + connector, + "terraform_test", + "terraform_test_tenant") + ipAsso, err := objMgr.GetFixedAddressByRef(id) + if err != nil { + if isNotFoundError(err) { + if expectedValue == nil { + return nil + } + return fmt.Errorf("object with ID '%s' not found, but expected to exist", id) + } + } + expNv := expectedValue.NetviewName + if ipAsso.NetviewName != expNv { + return fmt.Errorf( + "the value of 'network_view_name' field is '%s', but expected '%s'", + ipAsso.NetviewName, expNv) + } + + expComment := expectedValue.Comment + if ipAsso.Comment != expComment { + return fmt.Errorf( + "the value of 'comment' field is '%s', but expected '%s'", + ipAsso.Comment, expComment) + } + + expIPv4Address := expectedValue.IPv4Address + expIPv6Address := expectedValue.IPv6Address + if ipAsso.IPv4Address != expIPv4Address || ipAsso.IPv6Address != expIPv6Address { + return fmt.Errorf( + "the value of 'IPv4Address' field is '%s', but expected '%s' or the value of 'IPv6Address' field is '%s', but expected '%s' ", + ipAsso.IPv4Address, expIPv4Address, ipAsso.IPv6Address, expIPv6Address) + } + + expMACAddress := expectedValue.Mac + expDUID := expectedValue.Duid + if ipAsso.Mac != expMACAddress || ipAsso.Duid != expDUID { + return fmt.Errorf( + "the value of 'IPv4Address' field is '%s', but expected '%s' or the value of 'IPv6Address' field is '%s', but expected '%s' ", + ipAsso.IPv4Address, expIPv4Address, ipAsso.IPv6Address, expIPv6Address) + } + + // the rest is about extensible attributes + expectedEAs := expectedValue.Ea + if expectedEAs == nil && ipAsso.Ea != nil { + return fmt.Errorf( + "the object with ID '%s' has 'extensible_attributes' field, but it is not expected to exist", id) + } + if expectedEAs != nil && ipAsso.Ea == nil { + return fmt.Errorf( + "the object with ID '%s' has no 'extensible_attributes' field, but it is expected to exist", id) + } + if expectedEAs == nil { + return nil + } + + return validateEAs(ipAsso.Ea, expectedEAs) + } +} + +func TestAcc_resourceipAssociation_ipv4(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIPAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccresourceIPv4AllocationCreate, + Check: validateIPAssociation( + "infoblox_ipv4_association.foo", + &ibclient.FixedAddress{ + NetviewName: testNetView, + Cidr: "10.0.0.0/24", + Comment: "10.0.0.1 IP is associated", + IPv4Address: "10.0.0.1", + Mac: "11:22:33:44:55:66", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "VM Name": "tf-ec2-instance", + "Location": "Test loc.", + "Site": "Test site", + "TestEA1": []string{"text1", "text2"}, + "TestEA2": []int{4, 5}, + }, + }, + ), + }, + { + Config: testAccresourceIPv4AssociationUpdate, + Check: validateIPAssociation( + "infoblox_ipv4_association.foo", + &ibclient.FixedAddress{ + NetviewName: testNetView, + Cidr: "10.0.0.0/24", + Comment: "10.0.0.1 IP is allocated updated", + IPv4Address: "10.0.0.1", + Mac: "11:22:33:44:55:66", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "VM Name": "tf-ec2-instance", + "Location": "Test loc. updated", + // lists which contain ony one element are reduced by NIOS to a single-value element + "TestEA1": "text3", + "TestEA2": 7, + }, + }, + ), + }, + }, + }) +} + +func TestAcc_resourceIPAssociation_ipv6(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckRecordHostDestroy, + CheckDestroy: testAccCheckIPAssociationDestroy, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccresourceIPAssociationCreate, - Check: resource.ComposeTestCheckFunc( - testAccRecordHostExists(t, "infoblox_ip_association.foo", "10.0.0.0/24", "10.0.0.2", "default", "demo-network"), + { + Config: testAccresourceIPv6AssociationCreate, + Check: validateIPAssociation( + "infoblox_ipv6_association.foo", + &ibclient.FixedAddress{ + NetviewName: testNetView, + Cidr: "2001:db8:abcd:12::/64", + Comment: "2001:db8:abcd:12::1 IP is associated", + IPv6Address: "2001:db8:abcd:12::1", + Duid: "11:22:33:44:55:66", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "VM Name": "tf-ec2-instance-ipv6", + "Location": "Test loc.", + "Site": "Test site", + "TestEA1": []string{"text1", "text2"}, + "TestEA2": []int{4, 5}, + }, + }, ), }, - resource.TestStep{ - Config: testAccresourceIPAssociationUpdate, - Check: resource.ComposeTestCheckFunc( - testAccRecordHostExists(t, "infoblox_ip_association.foo", "10.0.0.0/24", "10.0.0.22", "default", "demo-network"), + { + Config: testAccresourceIPv6AssociationUpdate, + Check: validateIPAssociation( + "infoblox_ipv6_association.foo", + &ibclient.FixedAddress{ + NetviewName: testNetView, + Cidr: "2001:db8:abcd:12::/64", + Comment: "2001:db8:abcd:12::1 IP is associated updated", + IPv6Address: "2001:db8:abcd:12::1", + Duid: "11:22:33:44:55:66", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "VM Name": "tf-ec2-instance-ipv6", + "Location": "Test loc. updated", + // lists which contain ony one element are reduced by NIOS to a single-value element + "TestEA1": "text3", + "TestEA2": 7, + }, + }, ), }, }, }) } -func testAccCheckRecordHostDestroy(s *terraform.State) error { - meta := testAccProvider.Meta() +func testAccCheckIPAssociationDestroy(s *terraform.State) error { + connector := testAccProvider.Meta().(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager( + connector, + "terraform_test", + "terraform_test_tenant") for _, rs := range s.RootModule().Resources { - if rs.Type != "infoblox_ip_association" { + if rs.Type != "infoblox_ipv4_association" && rs.Type != "infoblox_ipv6_association" { continue } - Connector := meta.(*ibclient.Connector) - objMgr := ibclient.NewObjectManager(Connector, "terraform_test", "test") - recordName, _ := objMgr.GetFixedAddress("default", "10.0.0.0/24", "10.0.0.2", "") - if recordName == nil { - return fmt.Errorf("record not found") + res, err := objMgr.GetFixedAddressByRef(rs.Primary.ID) + if err != nil { + if isNotFoundError(err) { + continue + } + return err } - - } - return nil -} -func testAccRecordHostExists(t *testing.T, n string, cidr string, ipAddr string, networkViewName string, recordName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found:%s", n) - } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID i set") - } - meta := testAccProvider.Meta() - Connector := meta.(*ibclient.Connector) - objMgr := ibclient.NewObjectManager(Connector, "terraform_test", "test") - - recordName, _ := objMgr.GetFixedAddress("default", "10.0.0.0/24", "10.0.0.2", "") - if recordName == nil { - return fmt.Errorf("record not found") + if res != nil { + return fmt.Errorf("object with ID '%s' remains", rs.Primary.ID) } - - return nil } + return nil } - -var testAccresourceIPAssociationCreate = fmt.Sprintf(` -resource "infoblox_ip_association" "foo"{ - network_view_name="default" - vm_name="test-name" - mac_addr="11:22:33:44:55:66" - cidr="10.0.0.0/24" - ip_addr="10.0.0.2" - tenant_id="foo" - }`) - -var testAccresourceIPAssociationUpdate = fmt.Sprintf(` -resource "infoblox_ip_association" "foo"{ - network_view_name="default" - vm_name="test-name" - cidr="10.0.0.0/24" - ip_addr="10.0.0.2" - mac_addr="12:22:33:44:55:66" - tenant_id="foo" - }`) diff --git a/infoblox/resource_infoblox_network.go b/infoblox/resource_infoblox_network.go index 3060912da..e99cc903b 100644 --- a/infoblox/resource_infoblox_network.go +++ b/infoblox/resource_infoblox_network.go @@ -3,7 +3,6 @@ package infoblox import ( "encoding/json" "fmt" - "log" "github.com/hashicorp/terraform/helper/schema" ibclient "github.com/infobloxopen/infoblox-go-client" @@ -11,63 +10,54 @@ import ( func resourceNetwork() *schema.Resource { return &schema.Resource{ - Create: resourceNetworkCreate, - Read: resourceNetworkRead, - Update: resourceNetworkUpdate, - Delete: resourceNetworkDelete, - Schema: map[string]*schema.Schema{ - "network_view_name": &schema.Schema{ + "network_view_name": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Network view name available in NIOS Server.", }, - "network_name": &schema.Schema{ + "parent_cidr": { Type: schema.TypeString, Optional: true, - Description: "The name of your network block.", + Description: "The parent network container block in cidr format to allocate from.", }, - "cidr": &schema.Schema{ + "allocate_prefix_len": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + Description: "Set the parameter's value > 0 to allocate next available network with corresponding prefix length from the network container defined by 'parent_cidr'", + }, + "cidr": { Type: schema.TypeString, Optional: true, Computed: true, Description: "The network block in cidr format.", }, - "tenant_id": &schema.Schema{ - Type: schema.TypeString, - Required: true, - Description: "Unique identifier of your tenant in cloud.", - }, - "reserve_ip": &schema.Schema{ + "reserve_ip": { Type: schema.TypeInt, Optional: true, Default: 0, - Description: "The no of IP's you want to reserve.", + Description: "The number of IP's you want to reserve in IPv4 Network.", }, - "gateway": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Description: "gateway ip address of your network block.By default first IPv4 address is set as gateway address.", - Computed: true, - }, - "allocate_prefix_len": &schema.Schema{ + "reserve_ipv6": { Type: schema.TypeInt, Optional: true, Default: 0, - Description: "Set parameter value>0 to allocate next available network with prefix=value from network container defined by parent_cidr.", + Description: "The number of IP's you want to reserve in IPv6 Network", }, - "parent_cidr": &schema.Schema{ + "gateway": { Type: schema.TypeString, Optional: true, - Description: "The parent network container block in cidr format to allocate from.", + Description: "Gateways's IP address of your network block. By default first IPv4 address is set as gateway address.", + Computed: true, }, - "comment": &schema.Schema{ + "comment": { Type: schema.TypeString, Optional: true, Description: "A string describing the network", }, - "extensible_attributes": &schema.Schema{ + "extensible_attributes": { Type: schema.TypeString, Default: "", Optional: true, @@ -77,111 +67,185 @@ func resourceNetwork() *schema.Resource { } } -func resourceNetworkCreate(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning network block Creation", resourceNetworkIDString(d)) +func resourceNetworkCreate(d *schema.ResourceData, m interface{}, isIPv6 bool) error { networkViewName := d.Get("network_view_name").(string) + parentCidr := d.Get("parent_cidr").(string) + prefixLen := d.Get("allocate_prefix_len").(int) cidr := d.Get("cidr").(string) - parent_cidr := d.Get("parent_cidr").(string) - networkName := d.Get("network_name").(string) reserveIP := d.Get("reserve_ip").(int) + reserveIPv6 := d.Get("reserve_ipv6").(int) gateway := d.Get("gateway").(string) - tenantID := d.Get("tenant_id").(string) - connector := m.(*ibclient.Connector) - prefixLen := d.Get("allocate_prefix_len").(int) + comment := d.Get("comment").(string) - extensibleAttributesJSON := d.Get("extensible_attributes").(string) - extensibleAttributes := make(map[string]interface{}) - if extensibleAttributesJSON != "" { - if err := json.Unmarshal([]byte(extensibleAttributesJSON), &extensibleAttributes); err != nil { - return fmt.Errorf("Cannot process 'extensible_attributes' field: %s", err.Error()) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break } } ZeroMacAddr := "00:00:00:00:00:00" + connector := m.(ibclient.IBConnector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) ea := make(ibclient.EA) var network *ibclient.Network var err error - if cidr == "" && parent_cidr != "" && prefixLen > 1 { - network_container, err := objMgr.GetNetworkContainer(networkViewName, parent_cidr) - if network_container == nil { + if cidr == "" && parentCidr != "" && prefixLen > 1 { + _, err := objMgr.GetNetworkContainer(networkViewName, parentCidr, isIPv6, nil) + if err != nil { return fmt.Errorf( - "Allocation of network block failed in network view (%s) : Parent network container %s not found.", - networkViewName, parent_cidr) - + "Allocation of network block within network container '%s' under network view '%s' failed: %s", parentCidr, networkViewName, err.Error()) } - network, err = objMgr.AllocateNetwork(networkViewName, parent_cidr, uint(prefixLen), networkName, comment, extensibleAttributes) + network, err = objMgr.AllocateNetwork(networkViewName, parentCidr, isIPv6, uint(prefixLen), comment, extAttrs) if err != nil { return fmt.Errorf("Allocation of network block failed in network view (%s) : %s", networkViewName, err) } d.Set("cidr", network.Cidr) } else if cidr != "" { - network, err = objMgr.CreateNetwork(networkViewName, cidr, networkName, comment, extensibleAttributes) + network, err = objMgr.CreateNetwork(networkViewName, cidr, isIPv6, comment, extAttrs) if err != nil { return fmt.Errorf("Creation of network block failed in network view (%s) : %s", networkViewName, err) } } else { - return fmt.Errorf("Creation of network block failed: neither cidr nor parent_cidr with allocate_prefix_len was specified.") + return fmt.Errorf("Creation of network block failed: neither cidr nor parentCidr with allocate_prefix_len was specified.") } - // Check whether gateway or ip address already allocated - if gateway != "none" { - gatewayIP, err := objMgr.GetFixedAddress(networkViewName, network.Cidr, gateway, "") - if err == nil && gatewayIP != nil { - fmt.Printf("Gateway already created") - } else if gatewayIP == nil { - gatewayIP, err = objMgr.AllocateIP(networkViewName, network.Cidr, gateway, ZeroMacAddr, "", ea) + if isIPv6 { + // We need Zeroduid since AWS mandates first 3 IPv6 addresses to be reserved + Zeroduid := "00" + for i := 1; i <= reserveIPv6; i++ { + Zeroduid += ":00" + _, err = objMgr.AllocateIP(networkViewName, network.Cidr, gateway, isIPv6, Zeroduid, "", comment, ea) if err != nil { - return fmt.Errorf("Gateway Creation failed in network block(%s) error: %s", network.Cidr, err) + return fmt.Errorf("Reservation in network block failed in network view(%s):%s", networkViewName, err) } } - d.Set("gateway", gatewayIP.IPAddress) - } + } else { + // Check whether gateway or ip address already allocated + if gateway != "none" { + gatewayIP, err := objMgr.GetFixedAddress(networkViewName, network.Cidr, gateway, false, "") + if err == nil && gatewayIP != nil { + fmt.Printf("Gateway already created") + } else if gatewayIP == nil { + gatewayIP, err = objMgr.AllocateIP(networkViewName, network.Cidr, gateway, isIPv6, ZeroMacAddr, "", comment, ea) + if err != nil { + return fmt.Errorf("Gateway Creation failed in network block(%s) error: %s", network.Cidr, err) + } + } + d.Set("gateway", gatewayIP.IPv4Address) + } - for i := 1; i <= reserveIP; i++ { - _, err = objMgr.AllocateIP(networkViewName, network.Cidr, gateway, ZeroMacAddr, "", ea) - if err != nil { - return fmt.Errorf("Reservation in network block failed in network view(%s):%s", networkViewName, err) + for i := 1; i <= reserveIP; i++ { + _, err = objMgr.AllocateIP(networkViewName, network.Cidr, gateway, isIPv6, ZeroMacAddr, "", comment, ea) + if err != nil { + return fmt.Errorf("Reservation in network block failed in network view(%s):%s", networkViewName, err) + } } - } + } d.SetId(network.Ref) - - log.Printf("[DEBUG] %s: Creation on network block complete", resourceNetworkIDString(d)) - return resourceNetworkRead(d, m) + return nil } func resourceNetworkRead(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Reading the required network block", resourceNetworkIDString(d)) networkViewName := d.Get("network_view_name").(string) - tenantID := d.Get("tenant_id").(string) - connector := m.(*ibclient.Connector) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break + } + } + connector := m.(ibclient.IBConnector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) - obj, err := objMgr.GetNetworkWithRef(d.Id()) + obj, err := objMgr.GetNetworkByRef(d.Id()) if err != nil { return fmt.Errorf("Getting Network block from network view (%s) failed : %s", networkViewName, err) } d.SetId(obj.Ref) - log.Printf("[DEBUG] %s: Completed reading network block", resourceNetworkIDString(d)) return nil } func resourceNetworkUpdate(d *schema.ResourceData, m interface{}) error { - return fmt.Errorf("network updation is not supported") + networkViewName := d.Get("network_view_name").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break + } + } + + connector := m.(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) + var Network *ibclient.Network + + comment := "" + commentVal, commentFieldFound := d.GetOk("comment") + if commentFieldFound { + comment = commentVal.(string) + } + + var err error + + Network, err = objMgr.UpdateNetwork(d.Id(), extAttrs, comment) + if err != nil { + return fmt.Errorf("Updation of IPv4 Network under network view '%s' failed: '%s'", networkViewName, err.Error()) + } + + d.SetId(Network.Ref) + return nil } func resourceNetworkDelete(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning Deletion of network block", resourceNetworkIDString(d)) - networkViewName := d.Get("network_view_name").(string) - tenantID := d.Get("tenant_id").(string) - connector := m.(*ibclient.Connector) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break + } + } + connector := m.(ibclient.IBConnector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) _, err := objMgr.DeleteNetwork(d.Id()) @@ -189,19 +253,33 @@ func resourceNetworkDelete(d *schema.ResourceData, m interface{}) error { return fmt.Errorf("Deletion of Network block failed from network view(%s): %s", networkViewName, err) } d.SetId("") - - log.Printf("[DEBUG] %s: Deletion of network block complete", resourceNetworkIDString(d)) return nil } -type resourceNetworkIDStringInterface interface { - Id() string +func resourceIPv4NetworkCreate(d *schema.ResourceData, m interface{}) error { + return resourceNetworkCreate(d, m, false) } -func resourceNetworkIDString(d resourceNetworkIDStringInterface) string { - id := d.Id() - if id == "" { - id = "" - } - return fmt.Sprintf("infoblox_network (ID = %s)", id) +func resourceIPv4Network() *schema.Resource { + nw := resourceNetwork() + nw.Create = resourceIPv4NetworkCreate + nw.Read = resourceNetworkRead + nw.Update = resourceNetworkUpdate + nw.Delete = resourceNetworkDelete + + return nw +} + +func resourceIPv6NetworkCreate(d *schema.ResourceData, m interface{}) error { + return resourceNetworkCreate(d, m, true) +} + +func resourceIPv6Network() *schema.Resource { + nw := resourceNetwork() + nw.Create = resourceIPv6NetworkCreate + nw.Read = resourceNetworkRead + nw.Update = resourceNetworkUpdate + nw.Delete = resourceNetworkDelete + + return nw } diff --git a/infoblox/resource_infoblox_network_container.go b/infoblox/resource_infoblox_network_container.go new file mode 100644 index 000000000..379fa4759 --- /dev/null +++ b/infoblox/resource_infoblox_network_container.go @@ -0,0 +1,260 @@ +package infoblox + +import ( + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + ibclient "github.com/infobloxopen/infoblox-go-client" +) + +func resourceNetworkContainer() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "network_view_name": { + Type: schema.TypeString, + Required: true, + Description: "The name of network view for the network container.", + }, + "cidr": { + Type: schema.TypeString, + Required: true, + Description: "The network container's address, in CIDR format.", + }, + "comment": { + Type: schema.TypeString, + Default: "", + Optional: true, + Description: "A description of the network container.", + }, + "extensible_attributes": { + Type: schema.TypeString, + Default: "", + Optional: true, + Description: "The Extensible attributes of the network container to be added/updated, as a map in JSON format", + }, + }, + } +} + +func resourceNetworkContainerCreate(d *schema.ResourceData, m interface{}, isIPv6 bool) error { + nvName := d.Get("network_view_name").(string) + cidr := d.Get("cidr").(string) + comment := d.Get("comment").(string) + + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break + } + } + + if cidr == "" || nvName == "" { + return fmt.Errorf( + "Tenant ID, network view's name and CIDR are required to create a network container") + } + + connector := m.(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) + nc, err := objMgr.CreateNetworkContainer(nvName, cidr, isIPv6, comment, extAttrs) + if err != nil { + return fmt.Errorf( + "creation of IPv6 network container block failed in network view '%s': %s", + nvName, err.Error()) + } + d.SetId(nc.Ref) + + return nil +} + +func resourceNetworkContainerRead(d *schema.ResourceData, m interface{}) error { + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break + } + } + + connector := m.(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) + + obj, err := objMgr.GetNetworkContainerByRef(d.Id()) + if err != nil { + return fmt.Errorf("failed to retrieve network container: %s", err.Error()) + } + d.SetId(obj.Ref) + + return nil +} + +func resourceNetworkContainerUpdate(d *schema.ResourceData, m interface{}) error { + nvName := d.Get("network_view_name").(string) + cidr := d.Get("cidr").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break + } + } + + if cidr == "" || nvName == "" { + return fmt.Errorf( + "Tenant ID, network view's name and CIDR are required to update a network container") + } + + connector := m.(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) + + comment := "" + commentText, commentFieldFound := d.GetOk("comment") + if commentFieldFound { + comment = commentText.(string) + } + + nc, err := objMgr.UpdateNetworkContainer(d.Id(), extAttrs, comment) + if err != nil { + return fmt.Errorf( + "failed to update the network container in network view '%s': %s", + nvName, err.Error()) + } + + d.SetId(nc.Ref) + + nc, err = objMgr.GetNetworkContainerByRef(d.Id()) + if err != nil { + return fmt.Errorf("failed to read the network container: %s", err.Error()) + } + + easBytes, err := json.Marshal(nc.Ea) + if err != nil { + return fmt.Errorf( + "failed to read the network container's state after an update operation: %s", + err.Error()) + } + d.Set("extensible_attributes", string(easBytes)) + d.Set("comment", nc.Comment) + + return nil +} + +func resourceNetworkContainerDelete(d *schema.ResourceData, m interface{}) error { + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValueInf := range extAttrs { + attrValue, _ := attrValueInf.(string) + if attrName == "Tenant ID" { + tenantID = attrValue + break + } + } + connector := m.(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) + + if _, err := objMgr.DeleteNetworkContainer(d.Id()); err != nil { + return fmt.Errorf( + "deletion of the network container failed: %s", err.Error()) + } + + return nil +} + +// TODO: implement this after infoblox-go-client refactoring +//func resourceNetworkContainerExists(d *schema.ResourceData, m interface{}, isIPv6 bool) (bool, error) { +// return false, nil +//} + +func resourceIPv4NetworkContainerCreate(d *schema.ResourceData, m interface{}) error { + return resourceNetworkContainerCreate(d, m, false) +} + +func resourceIPv4NetworkContainerRead(d *schema.ResourceData, m interface{}) error { + return resourceNetworkContainerRead(d, m) +} + +func resourceIPv4NetworkContainerUpdate(d *schema.ResourceData, m interface{}) error { + return resourceNetworkContainerUpdate(d, m) +} + +func resourceIPv4NetworkContainerDelete(d *schema.ResourceData, m interface{}) error { + return resourceNetworkContainerDelete(d, m) +} + +//func resourceIPv4NetworkContainerExists(d *schema.ResourceData, m interface{}) (bool, error) { +// return resourceNetworkContainerExists(d, m, false) +//} + +func resourceIPv4NetworkContainer() *schema.Resource { + nc := resourceNetworkContainer() + nc.Create = resourceIPv4NetworkContainerCreate + nc.Read = resourceIPv4NetworkContainerRead + nc.Update = resourceIPv4NetworkContainerUpdate + nc.Delete = resourceIPv4NetworkContainerDelete + //nc.Exists = resourceIPv4NetworkContainerExists + + return nc +} + +func resourceIPv6NetworkContainerCreate(d *schema.ResourceData, m interface{}) error { + return resourceNetworkContainerCreate(d, m, true) +} + +func resourceIPv6NetworkContainerRead(d *schema.ResourceData, m interface{}) error { + return resourceNetworkContainerRead(d, m) +} + +func resourceIPv6NetworkContainerUpdate(d *schema.ResourceData, m interface{}) error { + return resourceNetworkContainerUpdate(d, m) +} + +func resourceIPv6NetworkContainerDelete(d *schema.ResourceData, m interface{}) error { + return resourceNetworkContainerDelete(d, m) +} + +//func resourceIPv6NetworkContainerExists(d *schema.ResourceData, m interface{}) (bool, error) { +// return resourceNetworkContainerExists(d, m, true) +//} + +func resourceIPv6NetworkContainer() *schema.Resource { + nc := resourceNetworkContainer() + nc.Create = resourceIPv6NetworkContainerCreate + nc.Read = resourceIPv6NetworkContainerRead + nc.Update = resourceIPv6NetworkContainerUpdate + nc.Delete = resourceIPv6NetworkContainerDelete + //nc.Exists = resourceIPv6NetworkContainerExists + + return nc +} diff --git a/infoblox/resource_infoblox_network_container_test.go b/infoblox/resource_infoblox_network_container_test.go new file mode 100644 index 000000000..3b6ba8257 --- /dev/null +++ b/infoblox/resource_infoblox_network_container_test.go @@ -0,0 +1,276 @@ +package infoblox + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + ibclient "github.com/infobloxopen/infoblox-go-client" +) + +const testNetView = "default" + +var resCfgNetworkContainer_create_ipv4 = fmt.Sprintf(` +resource "infoblox_ipv4_network_container" "nc_1" { + network_view_name = "%s" + cidr = "10.0.0.0/16" + comment = "10.0.0.0/16 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) +}`, testNetView) + +var resCfgNetworkContainer_update_ipv4 = fmt.Sprintf(` +resource "infoblox_ipv4_network_container" "nc_1" { + network_view_name = "%s" + cidr = "10.0.0.0/16" + comment = "new 10.0.0.0/16 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc. 2" + TestEA1 = "text3" + TestEA2 = 7 + }) +}`, testNetView) + +var resCfgNetworkContainer_update2_ipv4 = fmt.Sprintf(` +resource "infoblox_ipv4_network_container" "nc_1" { + network_view_name = "%s" + cidr = "10.0.0.0/16" + comment = "" + extensible_attributes = jsonencode({ + "Tenant ID" = "terraform_test_tenant" + }) +}`, testNetView) + +var resCfgNetworkContainer_create_ipv6 = fmt.Sprintf(` +resource "infoblox_ipv6_network_container" "nc_1" { + network_view_name = "%s" + cidr = "fc00::/56" + comment = "fc00::/56 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) +}`, testNetView) + +var resCfgNetworkContainer_update_ipv6 = fmt.Sprintf(` +resource "infoblox_ipv6_network_container" "nc_1" { + network_view_name = "%s" + cidr = "fc00::/56" + comment = "new comment for fc00::/56 network container" + extensible_attributes = jsonencode({ + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc. 2" + TestEA1 = ["text3"] + TestEA2 = 7 + }) +}`, testNetView) + +func validateNetworkContainer( + resourceName string, + expectedValue *ibclient.NetworkContainer) resource.TestCheckFunc { + return func(s *terraform.State) error { + res, found := s.RootModule().Resources[resourceName] + if !found { + return fmt.Errorf("not found: %s", resourceName) + } + + id := res.Primary.ID + if id == "" { + return fmt.Errorf("ID is not set") + } + + connector := testAccProvider.Meta().(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager( + connector, + "terraform_test", + "terraform_test_tenant") + + nc, err := objMgr.GetNetworkContainerByRef(id) + if err != nil { + if isNotFoundError(err) { + if expectedValue == nil { + return nil + } + return fmt.Errorf("object with ID '%s' not found, but expected to exist", id) + } + } + + expNv := expectedValue.NetviewName + if nc.NetviewName != expNv { + return fmt.Errorf( + "the value of 'network_view_name' field is '%s', but expected '%s'", + nc.NetviewName, expNv) + } + + expComment := expectedValue.Comment + if nc.Comment != expComment { + return fmt.Errorf( + "the value of 'comment' field is '%s', but expected '%s'", + nc.Comment, expComment) + } + + expCidr := expectedValue.Cidr + if nc.Cidr != expCidr { + return fmt.Errorf( + "the value of 'cidr' field is '%s', but expected '%s'", + nc.Cidr, expCidr) + } + + // the rest is about extensible attributes + expectedEAs := expectedValue.Ea + if expectedEAs == nil && nc.Ea != nil { + return fmt.Errorf( + "the object with ID '%s' has 'extensible_attributes' field, but it is not expected to exist", id) + } + if expectedEAs != nil && nc.Ea == nil { + return fmt.Errorf( + "the object with ID '%s' has no 'extensible_attributes' field, but it is expected to exist", id) + } + if expectedEAs == nil { + return nil + } + + return validateEAs(nc.Ea, expectedEAs) + } +} + +func TestAcc_resourceNetworkContainer_ipv4(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkContainerDestroy, + Steps: []resource.TestStep{ + { + Config: resCfgNetworkContainer_create_ipv4, + Check: validateNetworkContainer( + "infoblox_ipv4_network_container.nc_1", + &ibclient.NetworkContainer{ + NetviewName: testNetView, + Cidr: "10.0.0.0/16", + Comment: "10.0.0.0/16 network container", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "Location": "Test loc.", + "Site": "Test site", + "TestEA1": []string{"text1", "text2"}, + "TestEA2": []int{4, 5}, + }, + }, + ), + }, + { + Config: resCfgNetworkContainer_update_ipv4, + Check: validateNetworkContainer( + "infoblox_ipv4_network_container.nc_1", + &ibclient.NetworkContainer{ + NetviewName: testNetView, + Cidr: "10.0.0.0/16", + Comment: "new 10.0.0.0/16 network container", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "Location": "Test loc. 2", + + // lists which contain ony one element are reduced by NIOS to a single-value element + "TestEA1": "text3", + "TestEA2": 7, + }, + }, + ), + }, + { + Config: resCfgNetworkContainer_update2_ipv4, + Check: validateNetworkContainer( + "infoblox_ipv4_network_container.nc_1", + &ibclient.NetworkContainer{ + NetviewName: testNetView, + Cidr: "10.0.0.0/16", + Comment: "", + Ea: ibclient.EA{}, + }, + ), + }, + }, + }) +} + +func TestAcc_resourceNetworkContainer_ipv6(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkContainerDestroy, + Steps: []resource.TestStep{ + { + Config: resCfgNetworkContainer_create_ipv6, + Check: validateNetworkContainer( + "infoblox_ipv6_network_container.nc_1", + &ibclient.NetworkContainer{ + NetviewName: testNetView, + Cidr: "fc00::/56", + Comment: "fc00::/56 network container", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "Location": "Test loc.", + "Site": "Test site", + "TestEA1": []string{"text1", "text2"}, + "TestEA2": []int{4, 5}, + }, + }, + ), + }, + { + Config: resCfgNetworkContainer_update_ipv6, + Check: validateNetworkContainer( + "infoblox_ipv6_network_container.nc_1", + &ibclient.NetworkContainer{ + NetviewName: testNetView, + Cidr: "fc00::/56", + Comment: "new comment for fc00::/56 network container", + Ea: ibclient.EA{ + "Tenant ID": "terraform_test_tenant", + "Location": "Test loc. 2", + + // lists which contain ony one element are reduced by NIOS to a single-value element + "TestEA1": "text3", + "TestEA2": 7, + }, + }, + ), + }, + }, + }) +} + +func testAccCheckNetworkContainerDestroy(s *terraform.State) error { + connector := testAccProvider.Meta().(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager( + connector, + "terraform_test", + "terraform_test_tenant") + for _, rs := range s.RootModule().Resources { + if rs.Type != "infoblox_ipv4_network_container" && rs.Type != "infoblox_ipv6_network_container" { + continue + } + res, err := objMgr.GetNetworkContainerByRef(rs.Primary.ID) + if err != nil { + if isNotFoundError(err) { + continue + } + return err + } + if res != nil { + return fmt.Errorf("object with ID '%s' remains", rs.Primary.ID) + } + } + return nil +} diff --git a/infoblox/resource_infoblox_network_test.go b/infoblox/resource_infoblox_network_test.go index ace7f96d1..1444c112c 100644 --- a/infoblox/resource_infoblox_network_test.go +++ b/infoblox/resource_infoblox_network_test.go @@ -2,7 +2,6 @@ package infoblox import ( "fmt" - "regexp" "testing" "github.com/hashicorp/terraform/helper/resource" @@ -10,143 +9,238 @@ import ( ibclient "github.com/infobloxopen/infoblox-go-client" ) -func TestAccresourceNetwork(t *testing.T) { +var testAccresourceIPv4NetworkCreate = fmt.Sprintf(` +resource "infoblox_ipv4_network" "foo"{ + network_view_name="default" + cidr="10.10.0.0/24" + comment = "10.0.0.0/24 network created" + extensible_attributes = jsonencode({ + "Network Name"= "demo-network" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) + }`) + +var testAccresourceIPv4NetworkUpdate = fmt.Sprintf(` +resource "infoblox_ipv4_network" "foo"{ + network_view_name="default" + cidr="10.10.0.0/24" + comment = "10.0.0.0/24 network updated" + extensible_attributes = jsonencode({ + "Network Name"= "demo-network" + "Tenant ID" = "terraform_test_tenant" + Location = "Test loc. 2" + Site = "Test site" + TestEA1 = "text3" + TestEA2 = 7 + }) + }`) + +var testAccresourceIPv6NetworkCreate = fmt.Sprintf(` + resource "infoblox_ipv6_network" "foo"{ + network_view_name="default" + cidr="2001:db8:abcd:12::/64" + comment = "2001:db8:abcd:12::/64 network created" + extensible_attributes = jsonencode({ + "Tenant ID" = "terraform_test_tenant" + "Network Name"= "demo-network" + Location = "Test loc." + Site = "Test site" + TestEA1 = ["text1","text2"] + TestEA2 = [4,5] + }) + }`) + +var testAccresourceIPv6NetworkUpdate = fmt.Sprintf(` + resource "infoblox_ipv6_network" "foo"{ + network_view_name="default" + cidr="2001:db8:abcd:12::/64" + comment = "2001:db8:abcd:12::/64 network updated" + extensible_attributes = jsonencode({ + "Tenant ID" = "terraform_test_tenant" + "Network Name"= "demo-network" + Location = "Test loc. 2" + Site = "Test site" + TestEA1 = ["text3"] + TestEA2 = 7 + }) + }`) + +func validateNetwork( + resourceName string, + expectedValue *ibclient.Network) resource.TestCheckFunc { + return func(s *terraform.State) error { + res, found := s.RootModule().Resources[resourceName] + if !found { + return fmt.Errorf("not found: %s", resourceName) + } + + id := res.Primary.ID + if id == "" { + return fmt.Errorf("ID is not set") + } + + connector := testAccProvider.Meta().(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager( + connector, + "terraform_test", + "terraform_test_tenant") + nw, err := objMgr.GetNetworkByRef(id) + if err != nil { + if isNotFoundError(err) { + if expectedValue == nil { + return nil + } + return fmt.Errorf("object with ID '%s' not found, but expected to exist", id) + } + } + expNv := expectedValue.NetviewName + if nw.NetviewName != expNv { + return fmt.Errorf( + "the value of 'network_view_name' field is '%s', but expected '%s'", + nw.NetviewName, expNv) + } + + expComment := expectedValue.Comment + if nw.Comment != expComment { + return fmt.Errorf( + "the value of 'comment' field is '%s', but expected '%s'", + nw.Comment, expComment) + } + // the rest is about extensible attributes + expectedEAs := expectedValue.Ea + if expectedEAs == nil && nw.Ea != nil { + return fmt.Errorf( + "the object with ID '%s' has 'extensible_attributes' field, but it is not expected to exist", id) + } + if expectedEAs != nil && nw.Ea == nil { + return fmt.Errorf( + "the object with ID '%s' has no 'extensible_attributes' field, but it is expected to exist", id) + } + if expectedEAs == nil { + return nil + } + + return validateEAs(nw.Ea, expectedEAs) + } +} + +func testAccCheckNetworkDestroy(s *terraform.State) error { + connector := testAccProvider.Meta().(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager( + connector, + "terraform_test", + "terraform_test_tenant") + for _, rs := range s.RootModule().Resources { + if rs.Type != "infoblox_network" && rs.Type != "infoblox_ipv6_network" { + continue + } + res, err := objMgr.GetNetworkByRef(rs.Primary.ID) + if err != nil { + if isNotFoundError(err) { + continue + } + return err + } + if res != nil { + return fmt.Errorf("object with ID '%s' remains", rs.Primary.ID) + } + } + return nil +} + +func TestAcc_resourceNetwork_ipv4(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckNetworkDestroy, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccresourceNetworkCreate, - Check: resource.ComposeTestCheckFunc( - testAccCreateNetworkExists(t, "infoblox_network.foo", "10.10.0.0/24", "default", "demo-network"), + { + Config: testAccresourceIPv4NetworkCreate, + Check: validateNetwork( + "infoblox_ipv4_network.foo", + &ibclient.Network{ + Cidr: "10.0.0.0/24", + Comment: "10.0.0.0/24 network created", + Ea: ibclient.EA{ + "Network Name": "demo-network", + "Tenant ID": "terraform_test_tenant", + "Location": "Test loc.", + "Site": "Test site", + "TestEA1": []string{"text1", "text2"}, + "TestEA2": []int{4, 5}, + }, + }, ), }, - resource.TestStep{ - Config: testAccresourceNetworkUpdate, - Check: resource.ComposeTestCheckFunc( - testAccCreateNetworkExists(t, "infoblox_network.foo", "10.10.0.0/24", "default", "demo-network"), + { + Config: testAccresourceIPv4NetworkUpdate, + Check: validateNetwork( + "infoblox_ipv4_network.foo", + &ibclient.Network{ + Cidr: "10.0.0.0/24", + Comment: "10.0.0.0/24 network updated", + Ea: ibclient.EA{ + "Network Name": "demo-network", + "Tenant ID": "terraform_test_tenant", + "Location": "Test loc. 2", + // lists which contain ony one element are reduced by NIOS to a single-value element + "TestEA1": "text3", + "TestEA2": 7, + }, + }, ), }, }, }) } -func TestAccresourceNetwork_Allocate(t *testing.T) { - +func TestAcc_resourceNetwork_ipv6(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckNetworkDestroy, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccresourceNetworkAllocate, - Check: resource.ComposeTestCheckFunc( - testAccCreateNetworkExists(t, "infoblox_network.foo0", "10.0.0.0/24", "default", "demo-network"), - testAccCreateNetworkExists(t, "infoblox_network.foo1", "10.0.1.0/24", "default", "demo-network"), + { + Config: testAccresourceIPv6NetworkCreate, + Check: validateNetwork( + "infoblox_ipv6_network.foo", + &ibclient.Network{ + Cidr: "2001:db8:abcd:12::/64", + Comment: "2001:db8:abcd:12::/64 network created", + Ea: ibclient.EA{ + "Network Name": "demo-network", + "Tenant ID": "terraform_test_tenant", + "Location": "Test loc.", + "Site": "Test site", + "TestEA1": []string{"text1", "text2"}, + "TestEA2": []int{4, 5}, + }, + }, ), }, - }, - }) -} - -func TestAccresourceNetwork_Allocate_Fail(t *testing.T) { - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckNetworkDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccresourceNetworkAllocateFail, - ExpectError: regexp.MustCompile(`Allocation of network block failed in network view \(default\) : Parent network container 11.11.0.0/16 not found.`), + { + Config: testAccresourceIPv6NetworkUpdate, + Check: validateNetwork( + "infoblox_ipv6_network.foo", + &ibclient.Network{ + Cidr: "2001:db8:abcd:12::/64", + Comment: "2001:db8:abcd:12::/64 network updated", + Ea: ibclient.EA{ + "Network Name": "demo-network", + "Tenant ID": "terraform_test_tenant", + "Location": "Test loc. 2", + // lists which contain ony one element are reduced by NIOS to a single-value element + "TestEA1": "text3", + "TestEA2": 7, + }, + }, + ), }, }, }) } - -func testAccCheckNetworkDestroy(s *terraform.State) error { - meta := testAccProvider.Meta() - for _, rs := range s.RootModule().Resources { - if rs.Type != "infoblox_network" { - continue - } - Connector := meta.(*ibclient.Connector) - objMgr := ibclient.NewObjectManager(Connector, "terraform_test", "test") - networkName, _ := objMgr.GetNetwork("demo-network", "10.10.0.0/24", nil) - if networkName != nil { - return fmt.Errorf("Network not found") - } - - } - return nil -} - -func testAccCreateNetworkExists(t *testing.T, n string, cidr string, networkViewName string, networkName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found:%s", n) - } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID i set") - } - meta := testAccProvider.Meta() - Connector := meta.(*ibclient.Connector) - objMgr := ibclient.NewObjectManager(Connector, "terraform_test", "test") - - networkName, _ := objMgr.GetNetwork(networkName, cidr, nil) - if networkName != nil { - return fmt.Errorf("Network not found") - } - return nil - } -} - -var testAccresourceNetworkCreate = fmt.Sprintf(` -resource "infoblox_network" "foo"{ - network_view_name="default" - network_name="demo-network" - cidr="10.10.0.0/24" - tenant_id="foo" - }`) - -/* -Right now no infoblox_network_container resource available -So, before run acceptance test TestAccresourceNetwork_Allocate -in default network view should be created network container 10.0.0.0/16 -*/ -var testAccresourceNetworkAllocate = fmt.Sprintf(` -resource "infoblox_network" "foo0"{ - network_view_name="default" - network_name="demo-network" - tenant_id="foo" - allocate_prefix_len=24 - parent_cidr="10.0.0.0/16" - } -resource "infoblox_network" "foo1"{ - network_view_name="default" - network_name="demo-network" - tenant_id="foo" - allocate_prefix_len=24 - parent_cidr="10.0.0.0/16" - }`) - -/* Network container 11.11.0.0 should NOT exists to pass this test */ -var testAccresourceNetworkAllocateFail = fmt.Sprintf(` -resource "infoblox_network" "foo0"{ - network_view_name="default" - network_name="demo-network" - tenant_id="foo" - allocate_prefix_len=24 - parent_cidr="11.11.0.0/16" - }`) - -var testAccresourceNetworkUpdate = fmt.Sprintf(` -resource "infoblox_network" "foo"{ - network_view_name="default" - network_name="demo-network" - cidr="10.10.0.0/24" - tenant_id="foo" - }`) diff --git a/infoblox/resource_infoblox_network_view.go b/infoblox/resource_infoblox_network_view.go index 418daec43..b9fb03570 100644 --- a/infoblox/resource_infoblox_network_view.go +++ b/infoblox/resource_infoblox_network_view.go @@ -2,9 +2,9 @@ package infoblox import ( "fmt" + "github.com/hashicorp/terraform/helper/schema" - "github.com/infobloxopen/infoblox-go-client" - "log" + ibclient "github.com/infobloxopen/infoblox-go-client" ) func resourceNetworkView() *schema.Resource { @@ -15,94 +15,178 @@ func resourceNetworkView() *schema.Resource { Delete: resourceNetworkViewDelete, Schema: map[string]*schema.Schema{ - "network_view_name": &schema.Schema{ + "network_view_name": { Type: schema.TypeString, Required: true, Description: "Desired name of the view shown in NIOS appliance.", }, - "network_view_ref": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "tenant_id": &schema.Schema{ + "tenant_id": { Type: schema.TypeString, Required: true, - Description: "Unique identifier of your tenant in cloud.", + Description: "ID of a tenant to be used when creating objects.", }, + + // TODO: uncomment this once EA and comment support is added for NetView in go-client + //"comment": { + // Type: schema.TypeString, + // Default: "", + // Optional: true, + // Description: "A description of the IP allocation.", + //}, + //"extensible_attributes": { + // Type: schema.TypeString, + // Default: "", + // Optional: true, + // Description: "The Extensible attributes of the network container to be added/updated, as a map in JSON format", + //}, }, } } func resourceNetworkViewCreate(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning network view Creation", resourceNetworkViewIDString(d)) + var tenantID string - tenantID := d.Get("tenant_id").(string) - Connector := m.(*ibclient.Connector) - objMgr := ibclient.NewObjectManager(Connector, "Terraform", tenantID) + networkViewName := d.Get("network_view_name").(string) - networkViewName, err := objMgr.CreateNetworkView(d.Get("network_view_name").(string)) - if err != nil { - return fmt.Errorf("Failed to create Network View : %s", err) + // TODO: uncomment this once EA and comment support is added for NetView in go-client + //extAttrJSON := d.Get("extensible_attributes").(string) + //extAttrs := make(map[string]interface{}) + //if extAttrJSON != "" { + // if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + // return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + // } + //} + //for attrName, attrValue := range extAttrs { + // if attrName == "Tenant ID" { + // tenantID = attrValue.(string) + // break + // } + //} + + tenantIdTfProp, found := d.GetOk("tenant_id") + if found { + tenantID = tenantIdTfProp.(string) + } else { + tenantID = defaultTenantId } - d.SetId(networkViewName.Name) + Connector := m.(ibclient.IBConnector) + objMgr := ibclient.NewObjectManager(Connector, "Terraform", tenantID) - log.Printf("[DEBUG] %s: Completed network view Creation", resourceNetworkViewIDString(d)) + // TODO: Add comment and EAs while creation + // comment := d.Get("comment").(string) + //networkView, err := objMgr.CreateNetworkView(networkViewName, comment, extAttrs) - return resourceNetworkViewRead(d, m) + obj, err := objMgr.CreateNetworkView(networkViewName) + if err != nil { + return fmt.Errorf("Failed to create Network View : %s", err) + } + d.SetId(obj.Ref) + return nil } func resourceNetworkViewRead(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning to get network view ", resourceNetworkViewIDString(d)) + var tenantID string + + // TODO: uncomment this once EA and comment support is added for NetView in go-client + //extAttrJSON := d.Get("extensible_attributes").(string) + //extAttrs := make(map[string]interface{}) + //if extAttrJSON != "" { + // if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + // return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + // } + //} + //for attrName, attrValue := range extAttrs { + // if attrName == "Tenant ID" { + // tenantID = attrValue.(string) + // break + // } + //} + + tenantIdTfProp, found := d.GetOk("tenant_id") + if found { + tenantID = tenantIdTfProp.(string) + } else { + tenantID = defaultTenantId + } - tenantID := d.Get("tenant_id").(string) - Connector := m.(*ibclient.Connector) + Connector := m.(ibclient.IBConnector) objMgr := ibclient.NewObjectManager(Connector, "Terraform", tenantID) - obj, err := objMgr.GetNetworkView(d.Id()) + obj, err := objMgr.GetNetworkViewByRef(d.Id()) if err != nil { - return fmt.Errorf("Failed to get Network View : %s", err) + return fmt.Errorf("Failed to get Network View : %s", err.Error()) } - d.SetId(obj.Name) - d.Set("network_view_ref", obj.Ref) - - log.Printf("[DEBUG] %s: got Network View", resourceNetworkViewIDString(d)) + d.SetId(obj.Ref) return nil } func resourceNetworkViewUpdate(d *schema.ResourceData, m interface{}) error { + // TODO: Implement update at go client + /* + networkViewName := d.Get("network_view_name").(string) + comment := d.Get("comment").(string) + extAttrJSON := d.Get("extensible_attributes").(string) + extAttrs := make(map[string]interface{}) + if extAttrJSON != "" { + if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + } + } + var tenantID string + for attrName, attrValue := range extAttrs { + if attrName == "Tenant ID" { + tenantID = attrValue.(string) + break + } + } + Connector := m.(*ibclient.Connector) + objMgr := ibclient.NewObjectManager(Connector, "Terraform", tenantID) + obj, err := objMgr.UpdateNetworkView(networkViewName, comment, extAttrs) + if err != nil { + return fmt.Errorf("Failed to update Network View : %s", err.Error()) + } + d.SetId(obj.Ref) + return nil + */ return fmt.Errorf("network view updation is not supported") } func resourceNetworkViewDelete(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] %s: Beginning Deletion of network block", resourceNetworkIDString(d)) + var tenantID string networkViewName := d.Get("network_view_name").(string) - networkViewRef := d.Get("network_view_ref").(string) - tenantID := d.Get("tenant_id").(string) - connector := m.(*ibclient.Connector) + // TODO: uncomment this once EA and comment support is added for NetView in go-client + //extAttrJSON := d.Get("extensible_attributes").(string) + //extAttrs := make(map[string]interface{}) + //if extAttrJSON != "" { + // if err := json.Unmarshal([]byte(extAttrJSON), &extAttrs); err != nil { + // return fmt.Errorf("cannot process 'extensible_attributes' field: %s", err.Error()) + // } + //} + //for attrName, attrValue := range extAttrs { + // if attrName == "Tenant ID" { + // tenantID = attrValue.(string) + // break + // } + //} + + tenantIdTfProp, found := d.GetOk("tenant_id") + if found { + tenantID = tenantIdTfProp.(string) + } else { + tenantID = defaultTenantId + } + + connector := m.(ibclient.IBConnector) objMgr := ibclient.NewObjectManager(connector, "Terraform", tenantID) - _, err := objMgr.DeleteNetworkView(networkViewRef) + _, err := objMgr.DeleteNetworkView(d.Id()) if err != nil { - return fmt.Errorf("Deletion of Network view (%s) failed: %s", networkViewName, err) + return fmt.Errorf("Deletion of Network view %s failed: %s", networkViewName, err.Error()) } - d.SetId("") - log.Printf("[DEBUG] %s: Deletion of network block complete", resourceNetworkViewIDString(d)) return nil } - -type resourceNetworkViewIDStringInterface interface { - Id() string -} - -func resourceNetworkViewIDString(d resourceNetworkViewIDStringInterface) string { - id := d.Id() - if id == "" { - id = "" - } - return fmt.Sprintf("infoblox_network_view(ID = %s)", id) -} diff --git a/infoblox/resource_infoblox_network_view_test.go b/infoblox/resource_infoblox_network_view_test.go index e70a1c7b3..819a3c976 100644 --- a/infoblox/resource_infoblox_network_view_test.go +++ b/infoblox/resource_infoblox_network_view_test.go @@ -9,9 +9,9 @@ import ( ) var testAccresourceNetworkView = fmt.Sprintf(` -resource "infoblox_network_view" "foo"{ - network_view_name="test1" - tenant_id="foo" + resource "infoblox_network_view" "foo"{ + network_view_name="test1" + tenant_id="foo" }`) func TestAccresourceNetworkView(t *testing.T) { diff --git a/vendor/github.com/infobloxopen/infoblox-go-client/connector.go b/vendor/github.com/infobloxopen/infoblox-go-client/connector.go index df10dfb8b..b24d30d61 100644 --- a/vendor/github.com/infobloxopen/infoblox-go-client/connector.go +++ b/vendor/github.com/infobloxopen/infoblox-go-client/connector.go @@ -62,9 +62,9 @@ func NewTransportConfig(sslVerify string, httpRequestTimeout int, httpPoolConnec type HttpRequestBuilder interface { Init(HostConfig) - BuildUrl(r RequestType, objType string, ref string, returnFields []string, queryParams QueryParams) (urlStr string) + BuildUrl(r RequestType, objType string, ref string, returnFields []string, queryParams *QueryParams) (urlStr string) BuildBody(r RequestType, obj IBObject) (jsonStr []byte) - BuildRequest(r RequestType, obj IBObject, ref string, queryParams QueryParams) (req *http.Request, err error) + BuildRequest(r RequestType, obj IBObject, ref string, queryParams *QueryParams) (req *http.Request, err error) } type HttpRequestor interface { @@ -82,7 +82,7 @@ type WapiHttpRequestor struct { type IBConnector interface { CreateObject(obj IBObject) (ref string, err error) - GetObject(obj IBObject, ref string, res interface{}) error + GetObject(obj IBObject, ref string, queryParams *QueryParams, res interface{}) error DeleteObject(ref string) (refRes string, err error) UpdateObject(obj IBObject, ref string) (refRes string, err error) } @@ -129,8 +129,8 @@ func getHTTPResponseError(resp *http.Response) error { func (whr *WapiHttpRequestor) Init(cfg TransportConfig) { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: !cfg.SslVerify, - RootCAs: cfg.certPool, - Renegotiation: tls.RenegotiateOnceAsClient}, + RootCAs: cfg.certPool, + Renegotiation: tls.RenegotiateOnceAsClient}, MaxIdleConnsPerHost: cfg.HttpPoolConnections, } @@ -168,7 +168,7 @@ func (wrb *WapiRequestBuilder) Init(cfg HostConfig) { wrb.HostConfig = cfg } -func (wrb *WapiRequestBuilder) BuildUrl(t RequestType, objType string, ref string, returnFields []string, queryParams QueryParams) (urlStr string) { +func (wrb *WapiRequestBuilder) BuildUrl(t RequestType, objType string, ref string, returnFields []string, queryParams *QueryParams) (urlStr string) { path := []string{"wapi", "v" + wrb.HostConfig.Version} if len(ref) > 0 { path = append(path, ref) @@ -186,6 +186,10 @@ func (wrb *WapiRequestBuilder) BuildUrl(t RequestType, objType string, ref strin if queryParams.forceProxy { vals.Set("_proxy_search", "GM") } + for k, v := range queryParams.searchFields { + vals.Set(k, v) + } + qry = vals.Encode() } @@ -222,7 +226,7 @@ func (wrb *WapiRequestBuilder) BuildBody(t RequestType, obj IBObject) []byte { return objJSON } -func (wrb *WapiRequestBuilder) BuildRequest(t RequestType, obj IBObject, ref string, queryParams QueryParams) (req *http.Request, err error) { +func (wrb *WapiRequestBuilder) BuildRequest(t RequestType, obj IBObject, ref string, queryParams *QueryParams) (req *http.Request, err error) { var ( objType string returnFields []string @@ -234,7 +238,7 @@ func (wrb *WapiRequestBuilder) BuildRequest(t RequestType, obj IBObject, ref str urlStr := wrb.BuildUrl(t, objType, ref, returnFields, queryParams) var bodyStr []byte - if obj != nil { + if obj != nil && (t == CREATE || t == UPDATE) { bodyStr = wrb.BuildBody(t, obj) } @@ -249,7 +253,7 @@ func (wrb *WapiRequestBuilder) BuildRequest(t RequestType, obj IBObject, ref str return } -func (c *Connector) makeRequest(t RequestType, obj IBObject, ref string, queryParams QueryParams) (res []byte, err error) { +func (c *Connector) makeRequest(t RequestType, obj IBObject, ref string, queryParams *QueryParams) (res []byte, err error) { var req *http.Request req, err = c.RequestBuilder.BuildRequest(t, obj, ref, queryParams) res, err = c.Requestor.SendRequest(req) @@ -265,7 +269,7 @@ func (c *Connector) makeRequest(t RequestType, obj IBObject, ref string, queryPa func (c *Connector) CreateObject(obj IBObject) (ref string, err error) { ref = "" - queryParams := QueryParams{forceProxy: false} + queryParams := NewQueryParams(false, nil) resp, err := c.makeRequest(CREATE, obj, "", queryParams) if err != nil || len(resp) == 0 { log.Printf("CreateObject request error: '%s'\n", err) @@ -281,8 +285,11 @@ func (c *Connector) CreateObject(obj IBObject) (ref string, err error) { return } -func (c *Connector) GetObject(obj IBObject, ref string, res interface{}) (err error) { - queryParams := QueryParams{forceProxy: false} +// TODO: distinguish between "not found" and other kinds of errors. +func (c *Connector) GetObject( + obj IBObject, ref string, + queryParams *QueryParams, res interface{}) (err error) { + resp, err := c.makeRequest(GET, obj, ref, queryParams) //to check empty underlying value of interface var result interface{} @@ -312,7 +319,7 @@ func (c *Connector) GetObject(obj IBObject, ref string, res interface{}) (err er func (c *Connector) DeleteObject(ref string) (refRes string, err error) { refRes = "" - queryParams := QueryParams{forceProxy: false} + queryParams := NewQueryParams(false, nil) resp, err := c.makeRequest(DELETE, nil, ref, queryParams) if err != nil { log.Printf("DeleteObject request error: '%s'\n", err) @@ -329,7 +336,7 @@ func (c *Connector) DeleteObject(ref string) (refRes string, err error) { } func (c *Connector) UpdateObject(obj IBObject, ref string) (refRes string, err error) { - queryParams := QueryParams{forceProxy: false} + queryParams := NewQueryParams(false, nil) refRes = "" resp, err := c.makeRequest(UPDATE, obj, ref, queryParams) if err != nil { @@ -349,7 +356,7 @@ func (c *Connector) UpdateObject(obj IBObject, ref string) (refRes string, err e // be used in a defer statement after the Connector has been successfully // initialized. func (c *Connector) Logout() (err error) { - queryParams := QueryParams{forceProxy: false} + queryParams := NewQueryParams(false, nil) _, err = c.makeRequest(CREATE, nil, "logout", queryParams) if err != nil { log.Printf("Logout request error: '%s'\n", err) @@ -362,12 +369,13 @@ var ValidateConnector = validateConnector func validateConnector(c *Connector) (err error) { // GET UserProfile request is used here to validate connector's basic auth and reachability. - var response []UserProfile - userprofile := NewUserProfile(UserProfile{}) - err = c.GetObject(userprofile, "", &response) - if err != nil { - log.Printf("Failed to connect to the Grid, err: %s \n", err) - } + // TODO: It seems to be broken, needs to be fixed. + //var response []UserProfile + //userprofile := NewUserProfile(UserProfile{}) + //err = c.GetObject(userprofile, "", &response) + //if err != nil { + // log.Printf("Failed to connect to the Grid, err: %s \n", err) + //} return } diff --git a/vendor/github.com/infobloxopen/infoblox-go-client/lock.go b/vendor/github.com/infobloxopen/infoblox-go-client/lock.go index b4d67055c..1881d62b3 100644 --- a/vendor/github.com/infobloxopen/infoblox-go-client/lock.go +++ b/vendor/github.com/infobloxopen/infoblox-go-client/lock.go @@ -194,7 +194,8 @@ func (l *NetworkViewLock) Lock() error { } if _, ok := nw.Ea[l.LockEA]; !ok { - err = l.ObjMgr.UpdateNetworkViewEA(nw.Ref, EA{l.LockEA: freeLockVal}, nil) + nw.Ea[l.LockEA] = freeLockVal + err = l.ObjMgr.UpdateNetworkViewEA(nw.Ref, nw.Ea) if err != nil { return fmt.Errorf("Failed to Update Network view with Lock EA") } diff --git a/vendor/github.com/infobloxopen/infoblox-go-client/object_manager.go b/vendor/github.com/infobloxopen/infoblox-go-client/object_manager.go index e6c6611aa..d3a7f82ba 100644 --- a/vendor/github.com/infobloxopen/infoblox-go-client/object_manager.go +++ b/vendor/github.com/infobloxopen/infoblox-go-client/object_manager.go @@ -4,59 +4,88 @@ import ( "encoding/json" "errors" "fmt" + "net" + "net/url" "regexp" ) type IBObjectManager interface { - AllocateIP(netview string, cidr string, ipAddr string, macAddress string, name string, ea EA) (*FixedAddress, error) - AllocateNetwork(netview string, cidr string, prefixLen uint, name string, comment string, ea EA) (network *Network, err error) + AllocateIP(netview string, cidr string, ipAddr string, isIPv6 bool, macAddress string, name string, comment string, eas EA) (*FixedAddress, error) + AllocateNetwork(netview string, cidr string, isIPv6 bool, prefixLen uint, comment string, eas EA) (network *Network, err error) CreateARecord(netview string, dnsview string, recordname string, cidr string, ipAddr string, ea EA) (*RecordA, error) CreateZoneAuth(fqdn string, ea EA) (*ZoneAuth, error) CreateCNAMERecord(canonical string, recordname string, dnsview string, ea EA) (*RecordCNAME, error) CreateDefaultNetviews(globalNetview string, localNetview string) (globalNetviewRef string, localNetviewRef string, err error) CreateEADefinition(eadef EADefinition) (*EADefinition, error) - CreateHostRecord(enabledns bool, recordName string, netview string, dnsview string, cidr string, ipAddr string, macAddress string, ea EA) (*HostRecord, error) - CreateNetwork(netview string, cidr string, name string, comment string, ea EA) (*Network, error) - CreateNetworkContainer(netview string, cidr string) (*NetworkContainer, error) + CreateHostRecord(enabledns bool, enabledhcp bool, recordName string, netview string, dnsview string, ipv4cidr string, ipv6cidr string, ipv4Addr string, ipv6Addr string, macAddr string, duid string, comment string, eas EA, aliases []string) (*HostRecord, error) + CreateNetwork(netview string, cidr string, isIPv6 bool, comment string, eas EA) (*Network, error) + CreateNetworkContainer(netview string, cidr string, isIPv6 bool, comment string, eas EA) (*NetworkContainer, error) CreateNetworkView(name string) (*NetworkView, error) CreatePTRRecord(netview string, dnsview string, recordname string, cidr string, ipAddr string, ea EA) (*RecordPTR, error) + CreateTXTRecord(recordname string, text string, ttl int, dnsview string) (*RecordTXT, error) + CreateZoneDelegated(fqdn string, delegate_to []NameServer) (*ZoneDelegated, error) DeleteARecord(ref string) (string, error) DeleteZoneAuth(ref string) (string, error) DeleteCNAMERecord(ref string) (string, error) DeleteFixedAddress(ref string) (string, error) DeleteHostRecord(ref string) (string, error) - DeleteNetwork(ref string, netview string) (string, error) + DeleteNetwork(ref string) (string, error) + DeleteNetworkContainer(ref string) (string, error) DeleteNetworkView(ref string) (string, error) DeletePTRRecord(ref string) (string, error) + DeleteTXTRecord(ref string) (string, error) + DeleteZoneDelegated(ref string) (string, error) GetARecordByRef(ref string) (*RecordA, error) - GetCNAMERecordByRef(ref string) (*RecordA, error) + GetCNAMERecordByRef(ref string) (*RecordCNAME, error) GetEADefinition(name string) (*EADefinition, error) - GetFixedAddress(netview string, cidr string, ipAddr string, macAddr string) (*FixedAddress, error) + GetFixedAddress(netview string, cidr string, ipAddr string, isIPv6 bool, macAddr string) (*FixedAddress, error) GetFixedAddressByRef(ref string) (*FixedAddress, error) - GetHostRecord(recordName string) (*HostRecord, error) + GetHostRecord(recordName string, ipv4addr string, ipv6addr string) (*HostRecord, error) GetHostRecordByRef(ref string) (*HostRecord, error) GetIpAddressFromHostRecord(host HostRecord) (string, error) - GetNetwork(netview string, cidr string, ea EA) (*Network, error) - GetNetworkContainer(netview string, cidr string) (*NetworkContainer, error) + GetNetwork(netview string, cidr string, isIPv6 bool, ea EA) (*Network, error) + GetNetworkByRef(ref string) (*Network, error) + GetNetworkContainer(netview string, cidr string, isIPv6 bool, eaSearch EA) (*NetworkContainer, error) + GetNetworkContainerByRef(ref string) (*NetworkContainer, error) GetNetworkView(name string) (*NetworkView, error) + GetNetworkViewByRef(ref string) (*NetworkView, error) GetPTRRecordByRef(ref string) (*RecordPTR, error) GetZoneAuthByRef(ref string) (*ZoneAuth, error) - ReleaseIP(netview string, cidr string, ipAddr string, macAddr string) (string, error) - UpdateFixedAddress(fixedAddrRef string, matchclient string, macAddress string, vmID string, vmName string) (*FixedAddress, error) - UpdateHostRecord(hostRref string, ipAddr string, macAddress string, vmID string, vmName string) (string, error) - UpdateNetworkViewEA(ref string, addEA EA, removeEA EA) error + GetZoneDelegated(fqdn string) (*ZoneDelegated, error) + GetCapacityReport(name string) ([]CapacityReport, error) + GetUpgradeStatus(statusType string) ([]UpgradeStatus, error) + GetAllMembers() ([]Member, error) + GetGridInfo() ([]Grid, error) + GetGridLicense() ([]License, error) + ReleaseIP(netview string, cidr string, ipAddr string, isIPv6 bool, macAddr string) (string, error) + UpdateFixedAddress(fixedAddrRef string, name string, matchclient string, macAddress string, comment string, eas EA) (*FixedAddress, error) + UpdateHostRecord(hostRref string, enabledns bool, enabledhcp bool, name string, ipv4Addr string, ipv6Addr string, macAddress string, duid string, comment string, eas EA, aliases []string) (*HostRecord, error) + UpdateNetwork(ref string, setEas EA, comment string) (*Network, error) + UpdateNetworkContainer(ref string, setEas EA, comment string) (*NetworkContainer, error) + UpdateNetworkViewEA(ref string, setEas EA) error + UpdateZoneDelegated(ref string, delegate_to []NameServer) (*ZoneDelegated, error) +} + +type NotFoundError struct { + msg string +} + +func (e *NotFoundError) Error() string { + return "not found" +} + +func NewNotFoundError(msg string) *NotFoundError { + return &NotFoundError{msg: msg} } type ObjectManager struct { connector IBConnector cmpType string tenantID string - // If OmitCloudAttrs is true no extra attributes for cloud are set - OmitCloudAttrs bool } -func NewObjectManager(connector IBConnector, cmpType string, tenantID string) *ObjectManager { - objMgr := new(ObjectManager) +func NewObjectManager(connector IBConnector, cmpType string, tenantID string) IBObjectManager { + objMgr := &ObjectManager{} objMgr.connector = connector objMgr.cmpType = cmpType @@ -65,28 +94,11 @@ func NewObjectManager(connector IBConnector, cmpType string, tenantID string) *O return objMgr } -func (objMgr *ObjectManager) getBasicEA(cloudAPIOwned Bool) EA { - ea := make(EA) - if !objMgr.OmitCloudAttrs { - ea["Cloud API Owned"] = cloudAPIOwned - ea["CMP Type"] = objMgr.cmpType - ea["Tenant ID"] = objMgr.tenantID - } - return ea -} - -func (objMgr *ObjectManager) extendEA(ea EA) EA { - eas := objMgr.getBasicEA(true) - for k, v := range ea { - eas[k] = v - } - return eas -} - func (objMgr *ObjectManager) CreateNetworkView(name string) (*NetworkView, error) { networkView := NewNetworkView(NetworkView{ Name: name, - Ea: objMgr.getBasicEA(false)}) + Ea: make(EA), + }) ref, err := objMgr.connector.CreateObject(networkView) networkView.Ref = ref @@ -122,14 +134,9 @@ func (objMgr *ObjectManager) CreateDefaultNetviews(globalNetview string, localNe return } -func (objMgr *ObjectManager) CreateNetwork(netview string, cidr string, name string, comment string, ea EA) (*Network, error) { +func (objMgr *ObjectManager) CreateNetwork(netview string, cidr string, isIPv6 bool, comment string, eas EA) (*Network, error) { + network := NewNetwork(netview, cidr, isIPv6, comment, eas) - eas := objMgr.extendEA(ea) - network := NewNetwork(netview, cidr, comment, eas) - - if name != "" { - network.Ea["Network Name"] = name - } ref, err := objMgr.connector.CreateObject(network) if err != nil { return nil, err @@ -139,53 +146,64 @@ func (objMgr *ObjectManager) CreateNetwork(netview string, cidr string, name str return network, err } -func (objMgr *ObjectManager) CreateNetworkContainer(netview string, cidr string) (*NetworkContainer, error) { - container := NewNetworkContainer(NetworkContainer{ - NetviewName: netview, - Cidr: cidr, - Ea: objMgr.getBasicEA(true)}) +func (objMgr *ObjectManager) CreateNetworkContainer(netview string, cidr string, isIPv6 bool, comment string, eas EA) (*NetworkContainer, error) { + container := NewNetworkContainer(netview, cidr, isIPv6, comment, eas) ref, err := objMgr.connector.CreateObject(container) - container.Ref = ref + if err != nil { + return nil, err + } - return container, err + container.Ref = ref + return container, nil } func (objMgr *ObjectManager) GetNetworkView(name string) (*NetworkView, error) { var res []NetworkView - netview := NewNetworkView(NetworkView{Name: name}) - - err := objMgr.connector.GetObject(netview, "", &res) + netview := NewNetworkView(NetworkView{}) + sf := map[string]string{ + "name": name, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(netview, "", queryParams, &res) - if err != nil || res == nil || len(res) == 0 { + if err != nil { return nil, err } + if res == nil || len(res) == 0 { + return nil, fmt.Errorf("network view '%s' not found", name) + } return &res[0], nil } -func (objMgr *ObjectManager) UpdateNetworkViewEA(ref string, addEA EA, removeEA EA) error { +func (objMgr *ObjectManager) GetNetworkViewByRef(ref string) (*NetworkView, error) { + res := NewNetworkView(NetworkView{}) + queryParams := NewQueryParams(false, nil) + if err := objMgr.connector.GetObject(res, ref, queryParams, &res); err != nil { + return nil, err + } + if res == nil { + return nil, fmt.Errorf("network view not found") + } + + return res, nil +} + +func (objMgr *ObjectManager) UpdateNetworkViewEA(ref string, setEas EA) error { var res NetworkView nv := NetworkView{} nv.returnFields = []string{"extattrs"} - err := objMgr.connector.GetObject(&nv, ref, &res) + err := objMgr.connector.GetObject( + &nv, ref, NewQueryParams(false, nil), &res) if err != nil { return err } - for k, v := range addEA { - res.Ea[k] = v - } - - for k := range removeEA { - _, ok := res.Ea[k] - if ok { - delete(res.Ea, k) - } - } + res.Ea = setEas _, err = objMgr.connector.UpdateObject(&res, ref) return err @@ -212,65 +230,89 @@ func BuildNetworkFromRef(ref string) (*Network, error) { m := r.FindStringSubmatch(ref) if m == nil { - return nil, fmt.Errorf("Format not matched") + return nil, fmt.Errorf("CIDR format not matched") } - return &Network{ - Ref: ref, - NetviewName: m[2], - Cidr: m[1], - }, nil + newNet := NewNetwork(m[2], m[1], false, "", nil) + newNet.Ref = ref + return newNet, nil } -func (objMgr *ObjectManager) GetNetwork(netview string, cidr string, ea EA) (*Network, error) { +func (objMgr *ObjectManager) GetNetwork(netview string, cidr string, isIPv6 bool, ea EA) (*Network, error) { if netview != "" && cidr != "" { var res []Network - network := NewNetwork(netview, cidr, "", ea) + network := NewNetwork(netview, cidr, isIPv6, "", ea) - if cidr != "" { - network.Cidr = cidr - } + network.Cidr = cidr if ea != nil && len(ea) > 0 { network.eaSearch = EASearch(ea) } - err := objMgr.connector.GetObject(network, "", &res) + sf := map[string]string{ + "network_view": netview, + "network": cidr, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(network, "", queryParams, &res) if err != nil { return nil, err } else if res == nil || len(res) == 0 { - return nil, fmt.Errorf("Network with cidr: %s in network view: %s is not found.", cidr, netview) + return nil, NewNotFoundError( + fmt.Sprintf( + "Network with cidr: %s in network view: %s is not found.", + cidr, netview)) } return &res[0], nil } else { - err := fmt.Errorf("Both network view and cidr values are required") + err := fmt.Errorf("both network view and cidr values are required") return nil, err } } -func (objMgr *ObjectManager) GetNetworkWithRef(ref string) (*Network, error) { - network := NewNetwork("", "", "", nil) - err := objMgr.connector.GetObject(network, ref, &network) +func (objMgr *ObjectManager) GetNetworkByRef(ref string) (*Network, error) { + r := regexp.MustCompile("^ipv6network\\/.+") + isIPv6 := r.MatchString(ref) + + network := NewNetwork("", "", isIPv6, "", nil) + err := objMgr.connector.GetObject(network, ref, NewQueryParams(false, nil), network) return network, err } -func (objMgr *ObjectManager) GetNetworkContainer(netview string, cidr string) (*NetworkContainer, error) { +// TODO normalize IPv4 and IPv6 addresses +func (objMgr *ObjectManager) GetNetworkContainer(netview string, cidr string, isIPv6 bool, eaSearch EA) (*NetworkContainer, error) { var res []NetworkContainer - nwcontainer := NewNetworkContainer(NetworkContainer{ - NetviewName: netview, - Cidr: cidr}) + nc := NewNetworkContainer(netview, cidr, isIPv6, "", nil) + nc.eaSearch = EASearch(eaSearch) + sf := map[string]string{ + "network_view": netview, + "network": cidr, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(nc, "", queryParams, &res) + if err != nil { + return nil, err + } else if res == nil || len(res) == 0 { + return nil, NewNotFoundError("network container not found") + } + + return &res[0], nil +} - err := objMgr.connector.GetObject(nwcontainer, "", &res) +func (objMgr *ObjectManager) GetNetworkContainerByRef(ref string) (*NetworkContainer, error) { + nc := NewNetworkContainer("", "", false, "", nil) - if err != nil || res == nil || len(res) == 0 { + err := objMgr.connector.GetObject( + nc, ref, NewQueryParams(false, nil), nc) + if err != nil { return nil, err } - return &res[0], nil + return nc, nil } func GetIPAddressFromRef(ref string) string { @@ -284,64 +326,87 @@ func GetIPAddressFromRef(ref string) string { return "" } -func (objMgr *ObjectManager) AllocateIP(netview string, cidr string, ipAddr string, macAddress string, name string, ea EA) (*FixedAddress, error) { - if len(macAddress) == 0 { - macAddress = MACADDR_ZERO - } - - eas := objMgr.extendEA(ea) +func (objMgr *ObjectManager) AllocateIP( + netview string, + cidr string, + ipAddr string, + isIPv6 bool, + macOrDuid string, + name string, + comment string, + eas EA) (*FixedAddress, error) { - fixedAddr := NewFixedAddress(FixedAddress{ - NetviewName: netview, - Cidr: cidr, - Mac: macAddress, - Name: name, - Ea: eas}) - - if ipAddr == "" { - fixedAddr.IPAddress = fmt.Sprintf("func:nextavailableip:%s,%s", cidr, netview) + if isIPv6 { + if len(macOrDuid) == 0 { + return nil, fmt.Errorf("the DUID field cannot be left empty") + } } else { - fixedAddr.IPAddress = ipAddr + if len(macOrDuid) == 0 { + macOrDuid = MACADDR_ZERO + } } - + if ipAddr == "" { + ipAddr = fmt.Sprintf("func:nextavailableip:%s,%s", cidr, netview) + } + fixedAddr := NewFixedAddress( + netview, name, ipAddr, cidr, macOrDuid, "", eas, "", isIPv6, comment) ref, err := objMgr.connector.CreateObject(fixedAddr) + if err != nil { + return nil, err + } + fixedAddr.Ref = ref - fixedAddr.IPAddress = GetIPAddressFromRef(ref) + fixedAddr, err = objMgr.GetFixedAddressByRef(ref) return fixedAddr, err } -func (objMgr *ObjectManager) AllocateNetwork(netview string, cidr string, prefixLen uint, name string, comment string, ea EA) (network *Network, err error) { +func (objMgr *ObjectManager) AllocateNetwork( + netview string, + cidr string, + isIPv6 bool, + prefixLen uint, + comment string, + eas EA) (network *Network, err error) { network = nil cidr = fmt.Sprintf("func:nextavailablenetwork:%s,%s,%d", cidr, netview, prefixLen) - eas := objMgr.extendEA(ea) - networkReq := NewNetwork(netview, cidr, comment, eas) - if name != "" { - networkReq.Ea["Network Name"] = name - } + networkReq := NewNetwork(netview, cidr, isIPv6, comment, eas) ref, err := objMgr.connector.CreateObject(networkReq) - if err == nil && len(ref) > 0 { - network, err = BuildNetworkFromRef(ref) + if err == nil { + if isIPv6 { + network, err = BuildIPv6NetworkFromRef(ref) + } else { + network, err = BuildNetworkFromRef(ref) + } } return } -func (objMgr *ObjectManager) GetFixedAddress(netview string, cidr string, ipAddr string, macAddr string) (*FixedAddress, error) { +func (objMgr *ObjectManager) GetFixedAddress(netview string, cidr string, ipAddr string, isIpv6 bool, macOrDuid string) (*FixedAddress, error) { var res []FixedAddress - fixedAddr := NewFixedAddress(FixedAddress{ - NetviewName: netview, - Cidr: cidr, - IPAddress: ipAddr}) - - if macAddr != "" { - fixedAddr.Mac = macAddr + fixedAddr := NewEmptyFixedAddress(isIpv6) + sf := map[string]string{ + "network_view": netview, + "network": cidr, + } + if isIpv6 { + sf["ipv6addr"] = ipAddr + if macOrDuid != "" { + sf["duid"] = macOrDuid + } + } else { + sf["ipv4addr"] = ipAddr + if macOrDuid != "" { + sf["mac"] = macOrDuid + } } - err := objMgr.connector.GetObject(fixedAddr, "", &res) + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(fixedAddr, "", queryParams, &res) if err != nil || res == nil || len(res) == 0 { return nil, err @@ -351,8 +416,12 @@ func (objMgr *ObjectManager) GetFixedAddress(netview string, cidr string, ipAddr } func (objMgr *ObjectManager) GetFixedAddressByRef(ref string) (*FixedAddress, error) { - fixedAddr := NewFixedAddress(FixedAddress{}) - err := objMgr.connector.GetObject(fixedAddr, ref, &fixedAddr) + r := regexp.MustCompile("^ipv6fixedaddress/.+") + isIPv6 := r.MatchString(ref) + + fixedAddr := NewEmptyFixedAddress(isIPv6) + err := objMgr.connector.GetObject( + fixedAddr, ref, NewQueryParams(false, nil), &fixedAddr) return fixedAddr, err } @@ -362,9 +431,14 @@ func (objMgr *ObjectManager) DeleteFixedAddress(ref string) (string, error) { // validation for match_client func validateMatchClient(value string) bool { - match_client := [5]string{"MAC_ADDRESS", "CLIENT_ID", "RESERVED", "CIRCUIT_ID", "REMOTE_ID"} - - for _, val := range match_client { + matchClientList := [5]string{ + "MAC_ADDRESS", + "CLIENT_ID", + "RESERVED", + "CIRCUIT_ID", + "REMOTE_ID"} + + for _, val := range matchClientList { if val == value { return true } @@ -372,43 +446,47 @@ func validateMatchClient(value string) bool { return false } -func (objMgr *ObjectManager) UpdateFixedAddress(fixedAddrRef string, matchClient string, macAddress string, vmID string, vmName string) (*FixedAddress, error) { - updateFixedAddr := NewFixedAddress(FixedAddress{Ref: fixedAddrRef}) - - if len(macAddress) != 0 { - updateFixedAddr.Mac = macAddress - } - - ea := objMgr.getBasicEA(true) - if vmID != "" { - ea["VM ID"] = vmID - updateFixedAddr.Ea = ea - } - if vmName != "" { - ea["VM Name"] = vmName - updateFixedAddr.Ea = ea - } - if matchClient != "" { - if validateMatchClient(matchClient) { - updateFixedAddr.MatchClient = matchClient - } else { +func (objMgr *ObjectManager) UpdateFixedAddress( + fixedAddrRef string, + name string, + matchClient string, + macOrDuid string, + comment string, + eas EA) (*FixedAddress, error) { + r := regexp.MustCompile("^ipv6fixedaddress\\/.+") + isIPv6 := r.MatchString(fixedAddrRef) + if !isIPv6 { + if !validateMatchClient(matchClient) { return nil, fmt.Errorf("wrong value for match_client passed %s \n ", matchClient) } } + updateFixedAddr := NewFixedAddress( + "", name, "", "", + macOrDuid, matchClient, eas, fixedAddrRef, isIPv6, comment) refResp, err := objMgr.connector.UpdateObject(updateFixedAddr, fixedAddrRef) updateFixedAddr.Ref = refResp + return updateFixedAddr, err } -func (objMgr *ObjectManager) ReleaseIP(netview string, cidr string, ipAddr string, macAddr string) (string, error) { - fixAddress, _ := objMgr.GetFixedAddress(netview, cidr, ipAddr, macAddr) +func (objMgr *ObjectManager) ReleaseIP(netview string, cidr string, ipAddr string, isIpv6 bool, macOrDuid string) (string, error) { + fixAddress, _ := objMgr.GetFixedAddress(netview, cidr, ipAddr, isIpv6, macOrDuid) if fixAddress == nil { return "", nil } return objMgr.connector.DeleteObject(fixAddress.Ref) } +func (objMgr *ObjectManager) DeleteNetworkContainer(ref string) (string, error) { + ncRegExp := regexp.MustCompile("^(ipv6)?networkcontainer\\/.+") + if !ncRegExp.MatchString(ref) { + return "", fmt.Errorf("'ref' does not reference a network container") + } + + return objMgr.connector.DeleteObject(ref) +} + func (objMgr *ObjectManager) DeleteNetwork(ref string) (string, error) { return objMgr.connector.DeleteObject(ref) } @@ -422,7 +500,11 @@ func (objMgr *ObjectManager) GetEADefinition(name string) (*EADefinition, error) eadef := NewEADefinition(EADefinition{Name: name}) - err := objMgr.connector.GetObject(eadef, "", &res) + sf := map[string]string{ + "name": name, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(eadef, "", queryParams, &res) if err != nil || res == nil || len(res) == 0 { return nil, err @@ -440,52 +522,102 @@ func (objMgr *ObjectManager) CreateEADefinition(eadef EADefinition) (*EADefiniti return newEadef, err } -func (objMgr *ObjectManager) CreateHostRecord(enabledns bool, recordName string, netview string, dnsview string, cidr string, ipAddr string, macAddress string, ea EA) (*HostRecord, error) { +func BuildIPv6NetworkFromRef(ref string) (*Network, error) { + // ipv6network/ZG5zLm5ldHdvcmskODkuMC4wLjAvMjQvMjU:2001%3Adb8%3Aabcd%3A0012%3A%3A0/64/global_view + r := regexp.MustCompile(`ipv6network/[^:]+:(([^\/]+)\/\d+)\/(.+)`) + m := r.FindStringSubmatch(ref) - eas := objMgr.extendEA(ea) + if m == nil { + return nil, fmt.Errorf("CIDR format not matched") + } - recordHostIpAddr := NewHostRecordIpv4Addr(HostRecordIpv4Addr{Mac: macAddress}) + cidr, err := url.QueryUnescape(m[1]) + if err != nil { + return nil, fmt.Errorf( + "cannot extract network CIDR information from the reference '%s': %s", + ref, err.Error()) + } - if ipAddr == "" { - recordHostIpAddr.Ipv4Addr = fmt.Sprintf("func:nextavailableip:%s,%s", cidr, netview) - } else { - recordHostIpAddr.Ipv4Addr = ipAddr + if _, _, err = net.ParseCIDR(cidr); err != nil { + return nil, fmt.Errorf("CIDR format not matched") } - enableDNS := new(bool) - *enableDNS = enabledns - recordHostIpAddrSlice := []HostRecordIpv4Addr{*recordHostIpAddr} - recordHost := NewHostRecord(HostRecord{ - Name: recordName, - EnableDns: enableDNS, - NetworkView: netview, - View: dnsview, - Ipv4Addrs: recordHostIpAddrSlice, - Ea: eas}) + newNet := NewNetwork(m[3], cidr, true, "", nil) + newNet.Ref = ref + + return newNet, nil +} + +func (objMgr *ObjectManager) CreateHostRecord( + enabledns bool, + enabledhcp bool, + recordName string, + netview string, + dnsview string, + ipv4cidr string, + ipv6cidr string, + ipv4Addr string, + ipv6Addr string, + macAddr string, + duid string, + comment string, + eas EA, + aliases []string) (*HostRecord, error) { + + if ipv4Addr == "" && ipv4cidr != "" { + ipv4Addr = fmt.Sprintf("func:nextavailableip:%s,%s", ipv4cidr, netview) + } + if ipv6Addr == "" && ipv6cidr != "" { + ipv6Addr = fmt.Sprintf("func:nextavailableip:%s,%s", ipv6cidr, netview) + } + recordHost := NewEmptyHostRecord() + recordHostIpv6AddrSlice := []HostRecordIpv6Addr{} + recordHostIpv4AddrSlice := []HostRecordIpv4Addr{} + if ipv6Addr != "" { + recordHostIpv6Addr := NewHostRecordIpv6Addr(ipv6Addr, duid, &enabledhcp, "") + recordHostIpv6AddrSlice = []HostRecordIpv6Addr{*recordHostIpv6Addr} + } + if ipv4Addr != "" { + recordHostIpAddr := NewHostRecordIpv4Addr(ipv4Addr, macAddr, &enabledhcp, "") + + recordHostIpv4AddrSlice = []HostRecordIpv4Addr{*recordHostIpAddr} + } + recordHost = NewHostRecord( + netview, recordName, "", "", recordHostIpv4AddrSlice, recordHostIpv6AddrSlice, + eas, &enabledns, dnsview, "", "", comment, aliases) ref, err := objMgr.connector.CreateObject(recordHost) if err != nil { return nil, err } recordHost.Ref = ref - err = objMgr.connector.GetObject(recordHost, ref, &recordHost) + err = objMgr.connector.GetObject( + recordHost, ref, NewQueryParams(false, nil), &recordHost) return recordHost, err } func (objMgr *ObjectManager) GetHostRecordByRef(ref string) (*HostRecord, error) { - recordHost := NewHostRecord(HostRecord{}) - err := objMgr.connector.GetObject(recordHost, ref, &recordHost) + recordHost := NewEmptyHostRecord() + err := objMgr.connector.GetObject( + recordHost, ref, NewQueryParams(false, nil), &recordHost) return recordHost, err } -func (objMgr *ObjectManager) GetHostRecord(recordName string) (*HostRecord, error) { +func (objMgr *ObjectManager) GetHostRecord(recordName string, ipv4addr string, ipv6addr string) (*HostRecord, error) { var res []HostRecord - recordHost := NewHostRecord(HostRecord{}) - if recordName != "" { - recordHost.Name = recordName - } + recordHost := NewEmptyHostRecord() - err := objMgr.connector.GetObject(recordHost, "", &res) + sf := map[string]string{ + "name": recordName, + } + if ipv4addr != "" { + sf["ipv4addr"] = ipv4addr + } + if ipv6addr != "" { + sf["ipv6addr"] = ipv6addr + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(recordHost, "", queryParams, &res) if err != nil || res == nil || len(res) == 0 { return nil, err @@ -495,42 +627,117 @@ func (objMgr *ObjectManager) GetHostRecord(recordName string) (*HostRecord, erro } func (objMgr *ObjectManager) GetIpAddressFromHostRecord(host HostRecord) (string, error) { - err := objMgr.connector.GetObject(&host, host.Ref, &host) + err := objMgr.connector.GetObject( + &host, host.Ref, NewQueryParams(false, nil), &host) return host.Ipv4Addrs[0].Ipv4Addr, err } -func (objMgr *ObjectManager) UpdateHostRecord(hostRref string, ipAddr string, macAddress string, vmID string, vmName string) (string, error) { +func (objMgr *ObjectManager) UpdateHostRecord( + hostRref string, + enabledns bool, + enabledhcp bool, + name string, + ipv4Addr string, + ipv6Addr string, + macAddress string, + duid string, + comment string, + eas EA, + aliases []string) (*HostRecord, error) { - recordHostIpAddr := NewHostRecordIpv4Addr(HostRecordIpv4Addr{Mac: macAddress, Ipv4Addr: ipAddr}) - recordHostIpAddrSlice := []HostRecordIpv4Addr{*recordHostIpAddr} - updateHostRecord := NewHostRecord(HostRecord{Ipv4Addrs: recordHostIpAddrSlice}) - - ea := objMgr.getBasicEA(true) - if vmID != "" { - ea["VM ID"] = vmID - updateHostRecord.Ea = ea + enableDNS := new(bool) + *enableDNS = enabledns + enableDHCP := new(bool) + *enableDHCP = enabledhcp + recordHostIpv4AddrSlice := []HostRecordIpv4Addr{} + recordHostIpv6AddrSlice := []HostRecordIpv6Addr{} + if ipv4Addr != "" { + recordHostIpAddr := NewHostRecordIpv4Addr(ipv4Addr, macAddress, enableDHCP, "") + recordHostIpv4AddrSlice = []HostRecordIpv4Addr{*recordHostIpAddr} } - - if vmName != "" { - ea["VM Name"] = vmName - updateHostRecord.Ea = ea + if ipv6Addr != "" { + recordHostIpAddr := NewHostRecordIpv6Addr(ipv6Addr, duid, enableDHCP, "") + recordHostIpv6AddrSlice = []HostRecordIpv6Addr{*recordHostIpAddr} } + updateHostRecord := NewHostRecord( + "", name, "", "", recordHostIpv4AddrSlice, recordHostIpv6AddrSlice, + eas, enableDNS, "", "", hostRref, comment, aliases) ref, err := objMgr.connector.UpdateObject(updateHostRecord, hostRref) - return ref, err + updateHostRecord.Ref = ref + return updateHostRecord, err } func (objMgr *ObjectManager) DeleteHostRecord(ref string) (string, error) { return objMgr.connector.DeleteObject(ref) } -func (objMgr *ObjectManager) CreateARecord(netview string, dnsview string, recordname string, cidr string, ipAddr string, ea EA) (*RecordA, error) { +// UpdateNetwork updates comment and EA parameters. +// EAs which exist will be updated, +// those which do exist but not in setEas map, will be deleted, +// EAs which do not exist will be created as new. +func (objMgr *ObjectManager) UpdateNetwork( + ref string, + setEas EA, + comment string) (*Network, error) { - eas := objMgr.extendEA(ea) + r := regexp.MustCompile("^ipv6network\\/.+") + isIPv6 := r.MatchString(ref) - recordA := NewRecordA(RecordA{ - View: dnsview, - Name: recordname, - Ea: eas}) + nw := NewNetwork("", "", isIPv6, "", nil) + err := objMgr.connector.GetObject( + nw, ref, NewQueryParams(false, nil), nw) + + if err != nil { + return nil, err + } + + nw.Ea = setEas + nw.Comment = comment + + newRef, err := objMgr.connector.UpdateObject(nw, ref) + if err != nil { + return nil, err + } + + nw.Ref = newRef + return nw, nil +} + +func (objMgr *ObjectManager) UpdateNetworkContainer( + ref string, + setEas EA, + comment string) (*NetworkContainer, error) { + + nc := &NetworkContainer{} + nc.returnFields = []string{"extattrs", "comment"} + + err := objMgr.connector.GetObject( + nc, ref, NewQueryParams(false, nil), nc) + if err != nil { + return nil, err + } + + nc.Ea = setEas + nc.Comment = comment + + reference, err := objMgr.connector.UpdateObject(nc, ref) + if err != nil { + return nil, err + } + + nc.Ref = reference + return nc, nil +} + +func (objMgr *ObjectManager) CreateARecord( + netview string, + dnsview string, + recordname string, + cidr string, + ipAddr string, + eas EA) (*RecordA, error) { + + recordA := NewRecordA(dnsview, "", recordname, "", eas, "") if ipAddr == "" { recordA.Ipv4Addr = fmt.Sprintf("func:nextavailableip:%s,%s", cidr, netview) @@ -543,17 +750,20 @@ func (objMgr *ObjectManager) CreateARecord(netview string, dnsview string, recor } func (objMgr *ObjectManager) GetARecordByRef(ref string) (*RecordA, error) { - recordA := NewRecordA(RecordA{}) - err := objMgr.connector.GetObject(recordA, ref, &recordA) + recordA := NewEmptyRecordA() + err := objMgr.connector.GetObject( + recordA, ref, NewQueryParams(false, nil), &recordA) return recordA, err } func (objMgr *ObjectManager) DeleteARecord(ref string) (string, error) { return objMgr.connector.DeleteObject(ref) } -func (objMgr *ObjectManager) CreateCNAMERecord(canonical string, recordname string, dnsview string, ea EA) (*RecordCNAME, error) { - - eas := objMgr.extendEA(ea) +func (objMgr *ObjectManager) CreateCNAMERecord( + canonical string, + recordname string, + dnsview string, + eas EA) (*RecordCNAME, error) { recordCNAME := NewRecordCNAME(RecordCNAME{ View: dnsview, @@ -568,7 +778,8 @@ func (objMgr *ObjectManager) CreateCNAMERecord(canonical string, recordname stri func (objMgr *ObjectManager) GetCNAMERecordByRef(ref string) (*RecordCNAME, error) { recordCNAME := NewRecordCNAME(RecordCNAME{}) - err := objMgr.connector.GetObject(recordCNAME, ref, &recordCNAME) + err := objMgr.connector.GetObject( + recordCNAME, ref, NewQueryParams(false, nil), &recordCNAME) return recordCNAME, err } @@ -593,7 +804,8 @@ func (objMgr *ObjectManager) CreateTXTRecord(recordname string, text string, ttl func (objMgr *ObjectManager) GetTXTRecordByRef(ref string) (*RecordTXT, error) { recordTXT := NewRecordTXT(RecordTXT{}) - err := objMgr.connector.GetObject(recordTXT, ref, &recordTXT) + err := objMgr.connector.GetObject( + recordTXT, ref, NewQueryParams(false, nil), &recordTXT) return recordTXT, err } @@ -603,9 +815,13 @@ func (objMgr *ObjectManager) GetTXTRecord(name string) (*RecordTXT, error) { } var res []RecordTXT - recordTXT := NewRecordTXT(RecordTXT{Name: name}) + recordTXT := NewRecordTXT(RecordTXT{}) - err := objMgr.connector.GetObject(recordTXT, "", &res) + sf := map[string]string{ + "name": name, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(recordTXT, "", queryParams, &res) if err != nil || res == nil || len(res) == 0 { return nil, err @@ -619,7 +835,11 @@ func (objMgr *ObjectManager) UpdateTXTRecord(recordname string, text string) (*R recordTXT := NewRecordTXT(RecordTXT{Name: recordname}) - err := objMgr.connector.GetObject(recordTXT, "", &res) + sf := map[string]string{ + "name": recordname, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(recordTXT, "", queryParams, &res) if len(res) == 0 { return nil, nil @@ -642,9 +862,13 @@ func (objMgr *ObjectManager) DeleteTXTRecord(ref string) (string, error) { return objMgr.connector.DeleteObject(ref) } -func (objMgr *ObjectManager) CreatePTRRecord(netview string, dnsview string, recordname string, cidr string, ipAddr string, ea EA) (*RecordPTR, error) { - - eas := objMgr.extendEA(ea) +func (objMgr *ObjectManager) CreatePTRRecord( + netview string, + dnsview string, + recordname string, + cidr string, + ipAddr string, + eas EA) (*RecordPTR, error) { recordPTR := NewRecordPTR(RecordPTR{ View: dnsview, @@ -663,7 +887,8 @@ func (objMgr *ObjectManager) CreatePTRRecord(netview string, dnsview string, rec func (objMgr *ObjectManager) GetPTRRecordByRef(ref string) (*RecordPTR, error) { recordPTR := NewRecordPTR(RecordPTR{}) - err := objMgr.connector.GetObject(recordPTR, ref, &recordPTR) + err := objMgr.connector.GetObject( + recordPTR, ref, NewQueryParams(false, nil), &recordPTR) return recordPTR, err } @@ -675,7 +900,7 @@ func (objMgr *ObjectManager) DeletePTRRecord(ref string) (string, error) { func (objMgr *ObjectManager) CreateMultiObject(req *MultiRequest) ([]map[string]interface{}, error) { conn := objMgr.connector.(*Connector) - queryParams := QueryParams{forceProxy: false} + queryParams := NewQueryParams(false, nil) res, err := conn.makeRequest(CREATE, req, "", queryParams) if err != nil { @@ -702,8 +927,13 @@ func (objMgr *ObjectManager) GetUpgradeStatus(statusType string) ([]UpgradeStatu msg := fmt.Sprintf("Status type can not be nil") return res, errors.New(msg) } - upgradestatus := NewUpgradeStatus(UpgradeStatus{Type: statusType}) - err := objMgr.connector.GetObject(upgradestatus, "", &res) + upgradestatus := NewUpgradeStatus(UpgradeStatus{}) + + sf := map[string]string{ + "type": statusType, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(upgradestatus, "", queryParams, &res) return res, err } @@ -713,7 +943,8 @@ func (objMgr *ObjectManager) GetAllMembers() ([]Member, error) { var res []Member memberObj := NewMember(Member{}) - err := objMgr.connector.GetObject(memberObj, "", &res) + err := objMgr.connector.GetObject( + memberObj, "", NewQueryParams(false, nil), &res) return res, err } @@ -721,9 +952,13 @@ func (objMgr *ObjectManager) GetAllMembers() ([]Member, error) { func (objMgr *ObjectManager) GetCapacityReport(name string) ([]CapacityReport, error) { var res []CapacityReport - capacityObj := CapacityReport{Name: name} - capacityReport := NewCapcityReport(capacityObj) - err := objMgr.connector.GetObject(capacityReport, "", &res) + capacityReport := NewCapcityReport(CapacityReport{}) + + sf := map[string]string{ + "name": name, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(capacityReport, "", queryParams, &res) return res, err } @@ -732,7 +967,8 @@ func (objMgr *ObjectManager) GetLicense() ([]License, error) { var res []License licenseObj := NewLicense(License{}) - err := objMgr.connector.GetObject(licenseObj, "", &res) + err := objMgr.connector.GetObject( + licenseObj, "", NewQueryParams(false, nil), &res) return res, err } @@ -741,7 +977,8 @@ func (objMgr *ObjectManager) GetGridLicense() ([]License, error) { var res []License licenseObj := NewGridLicense(License{}) - err := objMgr.connector.GetObject(licenseObj, "", &res) + err := objMgr.connector.GetObject( + licenseObj, "", NewQueryParams(false, nil), &res) return res, err } @@ -750,14 +987,15 @@ func (objMgr *ObjectManager) GetGridInfo() ([]Grid, error) { var res []Grid gridObj := NewGrid(Grid{}) - err := objMgr.connector.GetObject(gridObj, "", &res) + err := objMgr.connector.GetObject( + gridObj, "", NewQueryParams(false, nil), &res) return res, err } // CreateZoneAuth creates zones and subs by passing fqdn -func (objMgr *ObjectManager) CreateZoneAuth(fqdn string, ea EA) (*ZoneAuth, error) { - - eas := objMgr.extendEA(ea) +func (objMgr *ObjectManager) CreateZoneAuth( + fqdn string, + eas EA) (*ZoneAuth, error) { zoneAuth := NewZoneAuth(ZoneAuth{ Fqdn: fqdn, @@ -769,15 +1007,15 @@ func (objMgr *ObjectManager) CreateZoneAuth(fqdn string, ea EA) (*ZoneAuth, erro } // Retreive a authortative zone by ref -func (objMgr *ObjectManager) GetZoneAuthByRef(ref string) (ZoneAuth, error) { - var res ZoneAuth +func (objMgr *ObjectManager) GetZoneAuthByRef(ref string) (*ZoneAuth, error) { + res := NewZoneAuth(ZoneAuth{}) if ref == "" { - return res, nil + return nil, fmt.Errorf("empty reference to an object is not allowed") } - zoneAuth := NewZoneAuth(ZoneAuth{}) - err := objMgr.connector.GetObject(zoneAuth, ref, &res) + err := objMgr.connector.GetObject( + res, ref, NewQueryParams(false, nil), res) return res, err } @@ -791,7 +1029,8 @@ func (objMgr *ObjectManager) GetZoneAuth() ([]ZoneAuth, error) { var res []ZoneAuth zoneAuth := NewZoneAuth(ZoneAuth{}) - err := objMgr.connector.GetObject(zoneAuth, "", &res) + err := objMgr.connector.GetObject( + zoneAuth, "", NewQueryParams(false, nil), &res) return res, err } @@ -803,9 +1042,13 @@ func (objMgr *ObjectManager) GetZoneDelegated(fqdn string) (*ZoneDelegated, erro } var res []ZoneDelegated - zoneDelegated := NewZoneDelegated(ZoneDelegated{Fqdn: fqdn}) + zoneDelegated := NewZoneDelegated(ZoneDelegated{}) - err := objMgr.connector.GetObject(zoneDelegated, "", &res) + sf := map[string]string{ + "fqdn": fqdn, + } + queryParams := NewQueryParams(false, sf) + err := objMgr.connector.GetObject(zoneDelegated, "", queryParams, &res) if err != nil || res == nil || len(res) == 0 { return nil, err diff --git a/vendor/github.com/infobloxopen/infoblox-go-client/objects.go b/vendor/github.com/infobloxopen/infoblox-go-client/objects.go index afd5b91f8..9e343b8d7 100644 --- a/vendor/github.com/infobloxopen/infoblox-go-client/objects.go +++ b/vendor/github.com/infobloxopen/infoblox-go-client/objects.go @@ -46,7 +46,7 @@ type NetworkView struct { IBBase `json:"-"` Ref string `json:"_ref,omitempty"` Name string `json:"name,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Ea EA `json:"extattrs"` } func NewNetworkView(nv NetworkView) *NetworkView { @@ -93,18 +93,22 @@ type Network struct { Ref string `json:"_ref,omitempty"` NetviewName string `json:"network_view,omitempty"` Cidr string `json:"network,omitempty"` - Ea EA `json:"extattrs,omitempty"` - Comment string `json:"comment,omitempty"` + Ea EA `json:"extattrs"` + Comment string `json:"comment"` } -func NewNetwork(netview string, cidr string, comment string, ea EA) *Network { +func NewNetwork(netview string, cidr string, isIPv6 bool, comment string, ea EA) *Network { var res Network res.NetviewName = netview res.Cidr = cidr res.Ea = ea res.Comment = comment - res.objectType = "network" - res.returnFields = []string{"extattrs", "network", "network_view", "comment"} + if isIPv6 { + res.objectType = "ipv6network" + } else { + res.objectType = "network" + } + res.returnFields = []string{"extattrs", "network", "comment"} return &res } @@ -287,15 +291,26 @@ type NetworkContainer struct { Ref string `json:"_ref,omitempty"` NetviewName string `json:"network_view,omitempty"` Cidr string `json:"network,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Comment string `json:"comment"` + Ea EA `json:"extattrs"` } -func NewNetworkContainer(nc NetworkContainer) *NetworkContainer { - res := nc - res.objectType = "networkcontainer" - res.returnFields = []string{"extattrs", "network", "network_view"} +func NewNetworkContainer(netview, cidr string, isIPv6 bool, comment string, ea EA) *NetworkContainer { + nc := NetworkContainer{ + NetviewName: netview, + Cidr: cidr, + Ea: ea, + Comment: comment, + } - return &res + if isIPv6 { + nc.objectType = "ipv6networkcontainer" + } else { + nc.objectType = "networkcontainer" + } + nc.returnFields = []string{"extattrs", "network", "network_view", "comment"} + + return &nc } type FixedAddress struct { @@ -303,30 +318,81 @@ type FixedAddress struct { Ref string `json:"_ref,omitempty"` NetviewName string `json:"network_view,omitempty"` Cidr string `json:"network,omitempty"` - IPAddress string `json:"ipv4addr,omitempty"` + Comment string `json:"comment"` + IPv4Address string `json:"ipv4addr,omitempty"` + IPv6Address string `json:"ipv6addr,omitempty"` + Duid string `json:"duid,omitempty"` Mac string `json:"mac,omitempty"` Name string `json:"name,omitempty"` MatchClient string `json:"match_client,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Ea EA `json:"extattrs"` } /*This is a general struct to add query params used in makeRequest*/ type QueryParams struct { forceProxy bool + + searchFields map[string]string } -func NewFixedAddress(fixedAddr FixedAddress) *FixedAddress { - res := fixedAddr - res.objectType = "fixedaddress" - res.returnFields = []string{"extattrs", "ipv4addr", "mac", "name", "network", "network_view"} +func NewQueryParams(forceProxy bool, searchFields map[string]string) *QueryParams { + qp := QueryParams{forceProxy: forceProxy} + if searchFields != nil { + qp.searchFields = searchFields + } else { + qp.searchFields = make(map[string]string) + } - return &res + return &qp +} + +func NewEmptyFixedAddress(isIPv6 bool) *FixedAddress { + res := &FixedAddress{} + res.Ea = make(EA) + if isIPv6 { + res.objectType = "ipv6fixedaddress" + res.returnFields = []string{"extattrs", "ipv6addr", "duid", "name", "network", "network_view", "comment"} + } else { + res.objectType = "fixedaddress" + res.returnFields = []string{"extattrs", "ipv4addr", "mac", "name", "network", "network_view", "comment"} + } + return res +} + +func NewFixedAddress( + netView string, + name string, + ipAddr string, + cidr string, + macOrDuid string, + clients string, + eas EA, + ref string, + isIPv6 bool, + comment string) *FixedAddress { + + res := NewEmptyFixedAddress(isIPv6) + res.NetviewName = netView + res.Name = name + res.Cidr = cidr + res.MatchClient = clients + res.Ea = eas + res.Ref = ref + res.Comment = comment + if isIPv6 { + res.IPv6Address = ipAddr + res.Duid = macOrDuid + } else { + res.IPv4Address = ipAddr + res.Mac = macOrDuid + } + return res } type EADefinition struct { IBBase `json:"-"` Ref string `json:"_ref,omitempty"` - Comment string `json:"comment,omitempty"` + Comment string `json:"comment"` Flags string `json:"flags,omitempty"` ListValues []EADefListValue `json:"list_values,omitempty"` Name string `json:"name,omitempty"` @@ -363,15 +429,34 @@ type RecordA struct { Name string `json:"name,omitempty"` View string `json:"view,omitempty"` Zone string `json:"zone,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Ea EA `json:"extattrs"` } -func NewRecordA(ra RecordA) *RecordA { - res := ra +func NewEmptyRecordA() *RecordA { + res := &RecordA{} res.objectType = "record:a" res.returnFields = []string{"extattrs", "ipv4addr", "name", "view", "zone"} - return &res + return res +} + +func NewRecordA( + view string, + zone string, + name string, + ipAddr string, + eas EA, + ref string) *RecordA { + + res := NewEmptyRecordA() + res.View = view + res.Zone = zone + res.Name = name + res.Ipv4Addr = ipAddr + res.Ea = eas + res.Ref = ref + + return res } type RecordPTR struct { @@ -382,7 +467,7 @@ type RecordPTR struct { PtrdName string `json:"ptrdname,omitempty"` View string `json:"view,omitempty"` Zone string `json:"zone,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Ea EA `json:"extattrs"` } func NewRecordPTR(rptr RecordPTR) *RecordPTR { @@ -400,7 +485,7 @@ type RecordCNAME struct { Name string `json:"name,omitempty"` View string `json:"view,omitempty"` Zone string `json:"zone,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Ea EA `json:"extattrs"` } func NewRecordCNAME(rc RecordCNAME) *RecordCNAME { @@ -412,18 +497,67 @@ func NewRecordCNAME(rc RecordCNAME) *RecordCNAME { } type HostRecordIpv4Addr struct { - IBBase `json:"-"` - Ipv4Addr string `json:"ipv4addr,omitempty"` - Ref string `json:"_ref,omitempty"` - Mac string `json:"mac,omitempty"` - View string `json:"view,omitempty"` - Cidr string `json:"network,omitempty"` + IBBase `json:"-"` + Ipv4Addr string `json:"ipv4addr,omitempty"` + Ref string `json:"_ref,omitempty"` + Mac string `json:"mac,omitempty"` + View string `json:"view,omitempty"` + Cidr string `json:"network,omitempty"` + EnableDHCP *bool `json:"configure_for_dhcp,omitempty"` } -func NewHostRecordIpv4Addr(hostAddr HostRecordIpv4Addr) *HostRecordIpv4Addr { - res := hostAddr +func NewEmptyHostRecordIpv4Addr() *HostRecordIpv4Addr { + res := &HostRecordIpv4Addr{} res.objectType = "record:host_ipv4addr" - return &res + + return res +} + +func NewHostRecordIpv4Addr( + ipAddr string, + macAddr string, + enableDHCP *bool, + ref string) *HostRecordIpv4Addr { + + res := NewEmptyHostRecordIpv4Addr() + res.Ipv4Addr = ipAddr + res.Mac = macAddr + res.Ref = ref + res.EnableDHCP = enableDHCP + + return res +} + +type HostRecordIpv6Addr struct { + IBBase `json:"-"` + Ipv6Addr string `json:"ipv6addr,omitempty"` + Ref string `json:"_ref,omitempty"` + Duid string `json:"duid,omitempty"` + View string `json:"view,omitempty"` + Cidr string `json:"network,omitempty"` + EnableDHCP *bool `json:"configure_for_dhcp,omitempty"` +} + +func NewEmptyHostRecordIpv6Addr() *HostRecordIpv6Addr { + res := &HostRecordIpv6Addr{} + res.objectType = "record:host_ipv6addr" + + return res +} + +func NewHostRecordIpv6Addr( + ipAddr string, + duid string, + enableDHCP *bool, + ref string) *HostRecordIpv6Addr { + + res := NewEmptyHostRecordIpv6Addr() + res.Ipv6Addr = ipAddr + res.Duid = duid + res.Ref = ref + res.EnableDHCP = enableDHCP + + return res } type HostRecord struct { @@ -431,20 +565,61 @@ type HostRecord struct { Ref string `json:"_ref,omitempty"` Ipv4Addr string `json:"ipv4addr,omitempty"` Ipv4Addrs []HostRecordIpv4Addr `json:"ipv4addrs,omitempty"` + Ipv6Addr string `json:"ipv6addr,omitempty"` + Ipv6Addrs []HostRecordIpv6Addr `json:"ipv6addrs,omitempty"` Name string `json:"name,omitempty"` View string `json:"view,omitempty"` Zone string `json:"zone,omitempty"` EnableDns *bool `json:"configure_for_dns,omitempty"` NetworkView string `json:"network_view,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Comment string `json:"comment"` + Ea EA `json:"extattrs"` + Aliases []string `json:"aliases,omitempty"` } -func NewHostRecord(rh HostRecord) *HostRecord { - res := rh +func NewEmptyHostRecord() *HostRecord { + res := &HostRecord{} res.objectType = "record:host" - res.returnFields = []string{"extattrs", "ipv4addrs", "name", "view", "zone"} + res.returnFields = []string{"extattrs", "ipv4addrs", "ipv6addrs", "name", "view", "zone", "comment", "network_view", "aliases"} + + return res +} + +func NewHostRecord( + netView string, + name string, + ipv4Addr string, + ipv6Addr string, + ipv4AddrList []HostRecordIpv4Addr, + ipv6AddrList []HostRecordIpv6Addr, + eas EA, + enableDNS *bool, + dnsView string, + zone string, + ref string, + comment string, + aliases []string) *HostRecord { + + res := NewEmptyHostRecord() + res.NetworkView = netView + res.Name = name + res.Ea = eas + res.View = dnsView + res.Zone = zone + res.Ref = ref + res.Comment = comment + res.Ipv4Addr = ipv4Addr + res.Ipv6Addr = ipv6Addr + res.Ipv4Addrs = ipv4AddrList + res.Ipv6Addrs = ipv6AddrList + res.Aliases = aliases + + if enableDNS != nil { + res.EnableDns = new(bool) + *res.EnableDns = *enableDNS + } - return &res + return res } type RecordTXT struct { @@ -455,7 +630,7 @@ type RecordTXT struct { TTL int `json:"ttl,omitempty"` View string `json:"view,omitempty"` Zone string `json:"zone,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Ea EA `json:"extattrs"` } func NewRecordTXT(rt RecordTXT) *RecordTXT { @@ -471,7 +646,7 @@ type ZoneAuth struct { Ref string `json:"_ref,omitempty"` Fqdn string `json:"fqdn,omitempty"` View string `json:"view,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Ea EA `json:"extattrs"` } func NewZoneAuth(za ZoneAuth) *ZoneAuth { @@ -493,7 +668,7 @@ type ZoneDelegated struct { Fqdn string `json:"fqdn,omitempty"` DelegateTo []NameServer `json:"delegate_to,omitempty"` View string `json:"view,omitempty"` - Ea EA `json:"extattrs,omitempty"` + Ea EA `json:"extattrs"` } func NewZoneDelegated(za ZoneDelegated) *ZoneDelegated {