diff --git a/cloudstack/provider.go b/cloudstack/provider.go index 72090147..48c4ee85 100644 --- a/cloudstack/provider.go +++ b/cloudstack/provider.go @@ -143,6 +143,7 @@ func Provider() *schema.Provider { "cloudstack_ssh_keypair": resourceCloudStackSSHKeyPair(), "cloudstack_static_nat": resourceCloudStackStaticNAT(), "cloudstack_static_route": resourceCloudStackStaticRoute(), + "cloudstack_storage_network_ip_range": resourceCloudStackStorageNetworkIpRange(), "cloudstack_storage_pool": resourceCloudStackStoragePool(), "cloudstack_template": resourceCloudStackTemplate(), "cloudstack_traffic_type": resourceCloudStackTrafficType(), diff --git a/cloudstack/resource_cloudstack_storage_network_ip_range.go b/cloudstack/resource_cloudstack_storage_network_ip_range.go new file mode 100644 index 00000000..709108d9 --- /dev/null +++ b/cloudstack/resource_cloudstack_storage_network_ip_range.go @@ -0,0 +1,169 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "log" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudStackStorageNetworkIpRange() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudStackStorageNetworkIpRangeCreate, + Read: resourceCloudStackStorageNetworkIpRangeRead, + Update: resourceCloudStackStorageNetworkIpRangeUpdate, + Delete: resourceCloudStackStorageNetworkIpRangeDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "gateway": { + Description: "the gateway for the storage network IP range", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "netmask": { + Description: "the netmask for the storage network IP range", + Type: schema.TypeString, + Required: true, + }, + "pod_id": { + Description: "the Pod ID for the storage network IP range", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "start_ip": { + Description: "the beginning IP address in the storage network IP range", + Type: schema.TypeString, + Required: true, + }, + "end_ip": { + Description: "the ending IP address in the storage network IP range", + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "vlan": { + Description: "the optional VLAN of the storage network IP range", + Type: schema.TypeInt, + Optional: true, + }, + "network_id": { + Description: "the network id of the storage network IP range", + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceCloudStackStorageNetworkIpRangeCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.Network.NewCreateStorageNetworkIpRangeParams( + d.Get("gateway").(string), + d.Get("netmask").(string), + d.Get("pod_id").(string), + d.Get("start_ip").(string), + ) + + if v, ok := d.GetOk("end_ip"); ok { + p.SetEndip(v.(string)) + } + if v, ok := d.GetOk("vlan"); ok { + p.SetVlan(v.(int)) + } + + r, err := cs.Network.CreateStorageNetworkIpRange(p) + if err != nil { + return fmt.Errorf("Error creating storage network IP range: %s", err) + } + + d.SetId(r.Id) + + return resourceCloudStackStorageNetworkIpRangeRead(d, meta) +} + +func resourceCloudStackStorageNetworkIpRangeRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + r, count, err := cs.Network.GetStorageNetworkIpRangeByID(d.Id()) + if err != nil { + if count == 0 { + log.Printf("[DEBUG] Storage network IP range %s does no longer exist", d.Id()) + d.SetId("") + return nil + } + + return err + } + + d.Set("gateway", r.Gateway) + d.Set("netmask", r.Netmask) + d.Set("pod_id", r.Podid) + d.Set("start_ip", r.Startip) + d.Set("end_ip", r.Endip) + d.Set("vlan", r.Vlan) + d.Set("network_id", r.Networkid) + + return nil +} + +func resourceCloudStackStorageNetworkIpRangeUpdate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + p := cs.Network.NewUpdateStorageNetworkIpRangeParams(d.Id()) + + if v, ok := d.GetOk("netmask"); ok { + p.SetNetmask(v.(string)) + } + if v, ok := d.GetOk("start_ip"); ok { + p.SetStartip(v.(string)) + } + if v, ok := d.GetOk("end_ip"); ok { + p.SetEndip(v.(string)) + } + if v, ok := d.GetOk("vlan"); ok { + p.SetVlan(v.(int)) + } + + _, err := cs.Network.UpdateStorageNetworkIpRange(p) + if err != nil { + return fmt.Errorf("Error updating storage network IP range: %s", err) + } + + return resourceCloudStackStorageNetworkIpRangeRead(d, meta) +} + +func resourceCloudStackStorageNetworkIpRangeDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + _, err := cs.Network.DeleteStorageNetworkIpRange(cs.Network.NewDeleteStorageNetworkIpRangeParams(d.Id())) + if err != nil { + return fmt.Errorf("Error deleting storage network IP range: %s", err) + } + + return nil +} diff --git a/cloudstack/resource_cloudstack_storage_network_ip_range_test.go b/cloudstack/resource_cloudstack_storage_network_ip_range_test.go new file mode 100644 index 00000000..f8c833aa --- /dev/null +++ b/cloudstack/resource_cloudstack_storage_network_ip_range_test.go @@ -0,0 +1,154 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccCloudStackStorageNetworkIpRange_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackStorageNetworkIpRange_basic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "gateway", "10.1.1.1"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "netmask", "255.255.255.0"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "start_ip", "10.1.1.2"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "end_ip", "10.1.1.10"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "vlan", "100"), + ), + }, + { + Config: testAccCloudStackStorageNetworkIpRange_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "gateway", "10.1.1.1"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "netmask", "255.255.255.0"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "start_ip", "10.1.1.20"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "end_ip", "10.1.1.30"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "vlan", "100"), + ), + }, + }, + }) +} + +func TestAccCloudStackStorageNetworkIpRange_noVlan(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackStorageNetworkIpRange_noVlan, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "start_ip", "10.1.1.2"), + resource.TestCheckResourceAttr("cloudstack_storage_network_ip_range.test", "end_ip", "10.1.1.10"), + ), + }, + }, + }) +} + +const testAccCloudStackStorageNetworkIpRange_basic = ` +resource "cloudstack_zone" "test" { + name = "acctest" + dns1 = "8.8.8.8" + dns2 = "8.8.8.8" + internal_dns1 = "8.8.4.4" + internal_dns2 = "8.8.4.4" + network_type = "Advanced" + domain = "cloudstack.apache.org" +} +resource "cloudstack_pod" "test" { + name = "acctestpod" + allocation_state = "Enabled" + zone_id = cloudstack_zone.test.id + gateway = "10.0.0.1" + netmask = "255.255.255.0" + start_ip = "10.0.0.2" +} +resource "cloudstack_storage_network_ip_range" "test" { + pod_id = cloudstack_pod.test.id + gateway = "10.1.1.1" + netmask = "255.255.255.0" + start_ip = "10.1.1.2" + end_ip = "10.1.1.10" + vlan = 100 +} +` + +const testAccCloudStackStorageNetworkIpRange_update = ` +resource "cloudstack_zone" "test" { + name = "acctest" + dns1 = "8.8.8.8" + dns2 = "8.8.8.8" + internal_dns1 = "8.8.4.4" + internal_dns2 = "8.8.4.4" + network_type = "Advanced" + domain = "cloudstack.apache.org" +} +resource "cloudstack_pod" "test" { + name = "acctestpod" + allocation_state = "Enabled" + zone_id = cloudstack_zone.test.id + gateway = "10.0.0.1" + netmask = "255.255.255.0" + start_ip = "10.0.0.2" +} +resource "cloudstack_storage_network_ip_range" "test" { + pod_id = cloudstack_pod.test.id + gateway = "10.1.1.1" + netmask = "255.255.255.0" + start_ip = "10.1.1.20" + end_ip = "10.1.1.30" + vlan = 100 +} +` + +const testAccCloudStackStorageNetworkIpRange_noVlan = ` +resource "cloudstack_zone" "test" { + name = "acctest" + dns1 = "8.8.8.8" + dns2 = "8.8.8.8" + internal_dns1 = "8.8.4.4" + internal_dns2 = "8.8.4.4" + network_type = "Advanced" + domain = "cloudstack.apache.org" +} +resource "cloudstack_pod" "test" { + name = "acctestpod" + allocation_state = "Enabled" + zone_id = cloudstack_zone.test.id + gateway = "10.0.0.1" + netmask = "255.255.255.0" + start_ip = "10.0.0.2" +} +resource "cloudstack_storage_network_ip_range" "test" { + pod_id = cloudstack_pod.test.id + gateway = "10.1.1.1" + netmask = "255.255.255.0" + start_ip = "10.1.1.2" + end_ip = "10.1.1.10" +} +` diff --git a/website/cloudstack.erb b/website/cloudstack.erb index 1ea7f7ec..e9b0b63d 100644 --- a/website/cloudstack.erb +++ b/website/cloudstack.erb @@ -109,6 +109,10 @@ cloudstack_static_route +