Adding new data sources to cloudstack-terraform-proivder (#38)
* create ssh keypair data-source
* create a method stub for instance data-source
* extending instance data-source to use its id as an input
* use SetId() to define the ID of the instance resource
* implement filters to allow filtering off of any exported attributes
* add ip-address to schema and allow filtering off of it
* create a method stub to acceptance test the instance data-source
* basic acceptance test for instance data-source passing
* adding comments to improve the readability of code
* add basic acceptance test for ssh keypair data-source
* create network offering resource
* complete network offering resource type
* create network offering data source
* add basic acceptance test for network offering data source
* create disk offering resource
* implement create method for disk offering resource
* create cloudstack volume resource type
* create cloudstack zone resource type
* create zone data source
* add acceptance test for zone datasource
* create service offering resource
* create service offering data source
* add service offering data source
* fix typos in comments for network offering resource
* add volume data source
* add acceptance test for volume data source
* create account resource
* implement delete method in account resource
* fix a typo in network offering
* create user resource
* create domain resource
* add vpc data-source
* add ip address data-source
* add user data-source
* add vpn connection data-source
* add acceptance test for vpc data-source
* add acceptance test for ipaddress data-source
* add acceptance test for user data-source
* fix a typo in applyNetworkOfferingFilters method of network offering data-source
diff --git a/cloudstack/data_source_cloudstack_instance.go b/cloudstack/data_source_cloudstack_instance.go
new file mode 100644
index 0000000..a78f39b
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_instance.go
@@ -0,0 +1,208 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackInstance() *schema.Resource {
+ return &schema.Resource{
+ Read: dataSourceCloudstackInstanceRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "instance_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "account": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "display_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "state": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "host_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "zone_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "created": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "tags": tagsSchema(),
+
+ "nic": {
+ Type: schema.TypeList,
+ Computed: true,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "ip_address": {
+ Type: schema.TypeString,
+ Computed: true,
+ Optional: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceCloudstackInstanceRead(d *schema.ResourceData, meta interface{}) error {
+ log.Printf("Instance Data Source Read Started")
+
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.VirtualMachine.NewListVirtualMachinesParams()
+ csInstances, err := cs.VirtualMachine.ListVirtualMachines(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list instances: %s", err)
+ }
+
+ filters := d.Get("filter")
+ nic := d.Get("nic").([]interface{})
+ var instances []*cloudstack.VirtualMachine
+
+ //the if-else block to check whether to filter the data source by an IP address
+ // or by any other exported attributes
+ if len(nic) != 0 {
+ ip_address := nic[0].(map[string]interface{})["ip_address"]
+ for _, i := range csInstances.VirtualMachines {
+ if ip_address == i.Nic[0].Ipaddress {
+ instances = append(instances, i)
+ }
+ }
+ } else {
+ for _, i := range csInstances.VirtualMachines {
+ match, err := applyInstanceFilters(i, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+
+ if match {
+ instances = append(instances, i)
+ }
+ }
+ }
+
+ if len(instances) == 0 {
+ return fmt.Errorf("No instance is matching with the specified regex")
+ }
+ //return the latest instance from the list of filtered instances according
+ //to its creation date
+ instance, err := latestInstance(instances)
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] Selected instances: %s\n", instance.Displayname)
+
+ return instanceDescriptionAttributes(d, instance)
+}
+
+func instanceDescriptionAttributes(d *schema.ResourceData, instance *cloudstack.VirtualMachine) error {
+ d.SetId(instance.Id)
+ d.Set("instance_id", instance.Id)
+ d.Set("account", instance.Account)
+ d.Set("created", instance.Created)
+ d.Set("display_name", instance.Displayname)
+ d.Set("state", instance.State)
+ d.Set("host_id", instance.Hostid)
+ d.Set("zone_id", instance.Zoneid)
+ d.Set("nic", []interface{}{map[string]string{"ip_address": instance.Nic[0].Ipaddress}})
+
+ tags := make(map[string]interface{})
+ for _, tag := range instance.Tags {
+ tags[tag.Key] = tag.Value
+ }
+ d.Set("tags", tags)
+
+ return nil
+}
+
+func latestInstance(instances []*cloudstack.VirtualMachine) (*cloudstack.VirtualMachine, error) {
+ var latest time.Time
+ var instance *cloudstack.VirtualMachine
+
+ for _, i := range instances {
+ created, err := time.Parse("2006-01-02T15:04:05-0700", i.Created)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse creation date of an instance: %s", err)
+ }
+
+ if created.After(latest) {
+ latest = created
+ instance = i
+ }
+ }
+
+ return instance, nil
+}
+
+func applyInstanceFilters(instance *cloudstack.VirtualMachine, filters *schema.Set) (bool, error) {
+ var instanceJSON map[string]interface{}
+ i, _ := json.Marshal(instance)
+ err := json.Unmarshal(i, &instanceJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ instanceField := instanceJSON[updatedName].(string)
+ if !r.MatchString(instanceField) {
+ return false, nil
+ }
+
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_instance_test.go b/cloudstack/data_source_cloudstack_instance_test.go
new file mode 100644
index 0000000..e27b1e1
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_instance_test.go
@@ -0,0 +1,66 @@
+//
+// 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/helper/resource"
+)
+
+//basic acceptance to check if the display_name attribute has same value in
+//the created instance and its data source respectively.
+func TestAccInstanceDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_instance.my_instance"
+ datasourceName := "data.cloudstack_instance.my_instance_test"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccInstanceDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "display_name", resourceName, "display_name"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testAccInstanceDataSourceConfig_basic = `
+ resource "cloudstack_instance" "my_instance" {
+ name = "server-a"
+ service_offering = "Small Instance"
+ network_id = "b9c953a0-8686-4240-b8a4-43849f7079ff"
+ template = "CentOS 5.5(64-bit) no GUI (KVM)"
+ zone = "DC"
+ }
+ data "cloudstack_instance" "my_instance_test" {
+ filter {
+ name = "display_name"
+ value = "server-a"
+ }
+ depends_on = [
+ cloudstack_instance.my_instance
+ ]
+ }
+`
diff --git a/cloudstack/data_source_cloudstack_ipaddress.go b/cloudstack/data_source_cloudstack_ipaddress.go
new file mode 100644
index 0000000..ad7b108
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_ipaddress.go
@@ -0,0 +1,175 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackIPAddress() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackIPAddressRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "is_portable": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+
+ "network_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "vpc_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "zone_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "project": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "ip_address": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "is_source_nat": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+
+ "tags": tagsSchema(),
+ },
+ }
+}
+
+func datasourceCloudStackIPAddressRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.Address.NewListPublicIpAddressesParams()
+ csPublicIPAddresses, err := cs.Address.ListPublicIpAddresses(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list ip addresses: %s", err)
+ }
+
+ filters := d.Get("filter")
+ var publicIpAddresses []*cloudstack.PublicIpAddress
+
+ for _, ip := range csPublicIPAddresses.PublicIpAddresses {
+ match, err := applyIPAddressFilters(ip, filters.(*schema.Set))
+
+ if err != nil {
+ return err
+ }
+ if match {
+ publicIpAddresses = append(publicIpAddresses, ip)
+ }
+ }
+
+ if len(publicIpAddresses) == 0 {
+ return fmt.Errorf("No ip address is matching with the specified regex")
+ }
+ //return the latest ip address from the list of filtered ip addresses according
+ //to its creation date
+ publicIpAddress, err := latestIPAddress(publicIpAddresses)
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] Selected ip addresses: %s\n", publicIpAddress.Ipaddress)
+
+ return ipAddressDescriptionAttributes(d, publicIpAddress)
+}
+
+func ipAddressDescriptionAttributes(d *schema.ResourceData, publicIpAddress *cloudstack.PublicIpAddress) error {
+ d.SetId(publicIpAddress.Id)
+ d.Set("is_portable", publicIpAddress.Isportable)
+ d.Set("network_id", publicIpAddress.Networkid)
+ d.Set("vpc_id", publicIpAddress.Vpcid)
+ d.Set("zone_name", publicIpAddress.Zonename)
+ d.Set("project", publicIpAddress.Project)
+ d.Set("ip_address", publicIpAddress.Ipaddress)
+ d.Set("is_source_nat", publicIpAddress.Issourcenat)
+ d.Set("tags", publicIpAddress.Tags)
+
+ return nil
+}
+
+func latestIPAddress(publicIpAddresses []*cloudstack.PublicIpAddress) (*cloudstack.PublicIpAddress, error) {
+ var latest time.Time
+ var publicIpAddress *cloudstack.PublicIpAddress
+
+ for _, ip := range publicIpAddresses {
+ created, err := time.Parse("2006-01-02T15:04:05-0700", ip.Allocated)
+
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse allocation date of the ip address: %s", err)
+ }
+
+ if created.After(latest) {
+ latest = created
+ publicIpAddress = ip
+ }
+ }
+
+ return publicIpAddress, nil
+}
+
+func applyIPAddressFilters(publicIpAddress *cloudstack.PublicIpAddress, filters *schema.Set) (bool, error) {
+ var publicIPAdressJSON map[string]interface{}
+ k, _ := json.Marshal(publicIpAddress)
+ err := json.Unmarshal(k, &publicIPAdressJSON)
+
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ publicIPAdressField := fmt.Sprintf("%v", publicIPAdressJSON[updatedName])
+ if !r.MatchString(publicIPAdressField) {
+ return false, nil
+ }
+ }
+
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_ipaddress_test.go b/cloudstack/data_source_cloudstack_ipaddress_test.go
new file mode 100644
index 0000000..ad98201
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_ipaddress_test.go
@@ -0,0 +1,65 @@
+//
+// 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/helper/resource"
+)
+
+func TestAccIPAddressDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_ipaddress.ipaddress-resource"
+ datasourceName := "data.cloudstack_ipaddress.ipaddress-data-source"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testIPAddressDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "zone_name", resourceName, "zone"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testIPAddressDataSourceConfig_basic = `
+resource "cloudstack_ipaddress" "ipaddress-resource" {
+ zone = "DC"
+ }
+
+ data "cloudstack_ipaddress" "ipaddress-data-source"{
+ filter{
+ name = "zone_name"
+ value= "DC"
+ }
+ depends_on = [
+ cloudstack_ipaddress.ipaddress-resource
+ ]
+ }
+
+ output "ipaddress-output" {
+ value = "${data.cloudstack_ipaddress.ipaddress-data-source}"
+ }
+ `
diff --git a/cloudstack/data_source_cloudstack_network_offering.go b/cloudstack/data_source_cloudstack_network_offering.go
new file mode 100644
index 0000000..8e57fae
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_network_offering.go
@@ -0,0 +1,148 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackNetworkOffering() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackNetworkOfferingRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "display_text": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "guest_ip_type": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "traffic_type": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func datasourceCloudStackNetworkOfferingRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.NetworkOffering.NewListNetworkOfferingsParams()
+ csNetworkOfferings, err := cs.NetworkOffering.ListNetworkOfferings(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list network offerings: %s", err)
+ }
+
+ filters := d.Get("filter")
+ var networkOfferings []*cloudstack.NetworkOffering
+
+ for _, n := range csNetworkOfferings.NetworkOfferings {
+ match, err := applyNetworkOfferingFilters(n, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ networkOfferings = append(networkOfferings, n)
+ }
+ }
+
+ if len(networkOfferings) == 0 {
+ return fmt.Errorf("No network offering is matching with the specified regex")
+ }
+ //return the latest network offering from the list of filtered network offerings according
+ //to its creation date
+ networkOffering, err := latestNetworkOffering(networkOfferings)
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] Selected network offerings: %s\n", networkOffering.Displaytext)
+
+ return networkOfferingDescriptionAttributes(d, networkOffering)
+}
+
+func networkOfferingDescriptionAttributes(d *schema.ResourceData, networkOffering *cloudstack.NetworkOffering) error {
+ d.SetId(networkOffering.Id)
+ d.Set("name", networkOffering.Name)
+ d.Set("display_text", networkOffering.Displaytext)
+ d.Set("guest_ip_type", networkOffering.Guestiptype)
+ d.Set("traffic_type", networkOffering.Traffictype)
+
+ return nil
+}
+
+func latestNetworkOffering(networkOfferings []*cloudstack.NetworkOffering) (*cloudstack.NetworkOffering, error) {
+ var latest time.Time
+ var networkOffering *cloudstack.NetworkOffering
+
+ for _, n := range networkOfferings {
+ created, err := time.Parse("2006-01-02T15:04:05-0700", n.Created)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse creation date of a network offering: %s", err)
+ }
+
+ if created.After(latest) {
+ latest = created
+ networkOffering = n
+ }
+ }
+
+ return networkOffering, nil
+}
+
+func applyNetworkOfferingFilters(networkOffering *cloudstack.NetworkOffering, filters *schema.Set) (bool, error) {
+ var networkOfferingJSON map[string]interface{}
+ k, _ := json.Marshal(networkOffering)
+ err := json.Unmarshal(k, &networkOfferingJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ networkOfferingField := networkOfferingJSON[updatedName].(string)
+ if !r.MatchString(networkOfferingField) {
+ return false, nil
+ }
+
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_network_offering_test.go b/cloudstack/data_source_cloudstack_network_offering_test.go
new file mode 100644
index 0000000..0101b31
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_network_offering_test.go
@@ -0,0 +1,65 @@
+//
+// 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/helper/resource"
+)
+
+func TestAccNetworkOfferingDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_network_offering.net-off-resource"
+ datasourceName := "data.cloudstack_network_offering.net-off-data-source"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testNetworkOfferingDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testNetworkOfferingDataSourceConfig_basic = `
+resource "cloudstack_network_offering" "net-off-resource"{
+ name = "TestNetworkDisplay01"
+ display_text = "TestNetworkDisplay01"
+ guest_ip_type = "Isolated"
+ traffic_type = "Guest"
+ }
+
+ data "cloudstack_network_offering" "net-off-data-source"{
+
+ filter{
+ name = "name"
+ value="TestNetworkDisplay01"
+ }
+ depends_on = [
+ cloudstack_network_offering.net-off-resource
+ ]
+ }
+ `
diff --git a/cloudstack/data_source_cloudstack_service_offering.go b/cloudstack/data_source_cloudstack_service_offering.go
new file mode 100644
index 0000000..4b33903
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_service_offering.go
@@ -0,0 +1,138 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackServiceOffering() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackServiceOfferingRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "display_text": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func datasourceCloudStackServiceOfferingRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.ServiceOffering.NewListServiceOfferingsParams()
+ csServiceOfferings, err := cs.ServiceOffering.ListServiceOfferings(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list service offerings: %s", err)
+ }
+
+ filters := d.Get("filter")
+ var serviceOfferings []*cloudstack.ServiceOffering
+
+ for _, s := range csServiceOfferings.ServiceOfferings {
+ match, err := applyServiceOfferingFilters(s, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ serviceOfferings = append(serviceOfferings, s)
+ }
+ }
+
+ if len(serviceOfferings) == 0 {
+ return fmt.Errorf("No service offering is matching with the specified regex")
+ }
+ //return the latest service offering from the list of filtered service according
+ //to its creation date
+ serviceOffering, err := latestServiceOffering(serviceOfferings)
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] Selected service offerings: %s\n", serviceOffering.Displaytext)
+
+ return serviceOfferingDescriptionAttributes(d, serviceOffering)
+}
+
+func serviceOfferingDescriptionAttributes(d *schema.ResourceData, serviceOffering *cloudstack.ServiceOffering) error {
+ d.SetId(serviceOffering.Id)
+ d.Set("name", serviceOffering.Name)
+ d.Set("display_text", serviceOffering.Displaytext)
+
+ return nil
+}
+
+func latestServiceOffering(serviceOfferings []*cloudstack.ServiceOffering) (*cloudstack.ServiceOffering, error) {
+ var latest time.Time
+ var serviceOffering *cloudstack.ServiceOffering
+
+ for _, s := range serviceOfferings {
+ created, err := time.Parse("2006-01-02T15:04:05-0700", s.Created)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse creation date of an service offering: %s", err)
+ }
+
+ if created.After(latest) {
+ latest = created
+ serviceOffering = s
+ }
+ }
+
+ return serviceOffering, nil
+}
+
+func applyServiceOfferingFilters(serviceOffering *cloudstack.ServiceOffering, filters *schema.Set) (bool, error) {
+ var serviceOfferingJSON map[string]interface{}
+ k, _ := json.Marshal(serviceOffering)
+ err := json.Unmarshal(k, &serviceOfferingJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ serviceOfferingField := serviceOfferingJSON[updatedName].(string)
+ if !r.MatchString(serviceOfferingField) {
+ return false, nil
+ }
+
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_service_offering_test.go b/cloudstack/data_source_cloudstack_service_offering_test.go
new file mode 100644
index 0000000..fbc3faa
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_service_offering_test.go
@@ -0,0 +1,62 @@
+//
+// 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/helper/resource"
+)
+
+func TestAccServiceOfferingDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_service_offering.service-offering-resource"
+ datasourceName := "cloudstack_service_offering.service-offering-data-source"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testServiceOfferingDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testServiceOfferingDataSourceConfig_basic = `
+resource "cloudstack_service_offering" "service-offering-resource"{
+ name = "TestServiceUpdate"
+ display_text = "DisplayService"
+ }
+
+ data "cloudstack_service_offering" "service-offering-data-source"{
+ filter{
+ name = "name"
+ value="TestServiceUpdate"
+ }
+ depends_on = [
+ cloudstack_service_offering.service-resource
+ ]
+ }
+ `
diff --git a/cloudstack/data_source_cloudstack_ssh_keypair.go b/cloudstack/data_source_cloudstack_ssh_keypair.go
new file mode 100644
index 0000000..c7696cb
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_ssh_keypair.go
@@ -0,0 +1,113 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackSSHKeyPair() *schema.Resource {
+ return &schema.Resource{
+ Read: dataSourceCloudstackSSHKeyPairRead,
+
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "fingerprint": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceCloudstackSSHKeyPairRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.SSH.NewListSSHKeyPairsParams()
+ csSshKeyPairs, err := cs.SSH.ListSSHKeyPairs(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list ssh key pairs: %s", err)
+ }
+ filters := d.Get("filter")
+ var sshKeyPair *cloudstack.SSHKeyPair
+
+ for _, k := range csSshKeyPairs.SSHKeyPairs {
+ match, err := applySshKeyPairsFilters(k, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ sshKeyPair = k
+ }
+ }
+
+ if sshKeyPair == nil {
+ return fmt.Errorf("No ssh key pair is matching with the specified regex")
+ }
+ log.Printf("[DEBUG] Selected ssh key pair: %s\n", sshKeyPair.Name)
+
+ return sshKeyPairDescriptionAttributes(d, sshKeyPair)
+}
+
+func sshKeyPairDescriptionAttributes(d *schema.ResourceData, sshKeyPair *cloudstack.SSHKeyPair) error {
+ d.SetId(sshKeyPair.Name)
+ d.Set("fingerprint", sshKeyPair.Fingerprint)
+ d.Set("name", sshKeyPair.Name)
+
+ return nil
+}
+
+func applySshKeyPairsFilters(sshKeyPair *cloudstack.SSHKeyPair, filters *schema.Set) (bool, error) {
+ var sshKeyPairJSON map[string]interface{}
+ k, _ := json.Marshal(sshKeyPair)
+ err := json.Unmarshal(k, &sshKeyPairJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ sshKeyPairField := sshKeyPairJSON[updatedName].(string)
+ if !r.MatchString(sshKeyPairField) {
+ return false, nil
+ }
+
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_ssh_keypair_test.go b/cloudstack/data_source_cloudstack_ssh_keypair_test.go
new file mode 100644
index 0000000..32bd3fd
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_ssh_keypair_test.go
@@ -0,0 +1,62 @@
+//
+// 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/helper/resource"
+)
+
+func TestAccSshKeyPairDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_ssh_keypair.ssh-keypair-resource"
+ datasourceName := "data.cloudstack_ssh_keypair.ssh-keypair-data"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccSshKeyPairDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testAccSshKeyPairDataSourceConfig_basic = `
+ resource "cloudstack_ssh_keypair" "ssh-keypair-resource"{
+ name = "myKey"
+ }
+
+ data "cloudstack_ssh_keypair" "ssh-keypair-data" {
+ filter {
+ name = "name"
+ value = "myKey"
+ }
+ depends_on = [
+ cloudstack_ssh_keypair.ssh-keypair-resource
+ ]
+
+ }
+ `
diff --git a/cloudstack/data_source_cloudstack_user.go b/cloudstack/data_source_cloudstack_user.go
new file mode 100644
index 0000000..a40862a
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_user.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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackUser() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackUserRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "account": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "email": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "first_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "username": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func datasourceCloudStackUserRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.User.NewListUsersParams()
+ csUsers, err := cs.User.ListUsers(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list users: %s", err)
+ }
+
+ filters := d.Get("filter")
+ var users []*cloudstack.User
+
+ for _, u := range csUsers.Users {
+ match, err := applyUserFilters(u, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ users = append(users, u)
+ }
+ }
+
+ if len(users) == 0 {
+ return fmt.Errorf("No user is matching with the specified regex")
+ }
+ //return the latest user from the list of filtered userss according
+ //to its creation date
+ user, err := latestUser(users)
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] Selected users: %s\n", user.Username)
+
+ return userDescriptionAttributes(d, user)
+}
+
+func userDescriptionAttributes(d *schema.ResourceData, user *cloudstack.User) error {
+ d.SetId(user.Id)
+ d.Set("account", user.Account)
+ d.Set("email", user.Email)
+ d.Set("first_name", user.Firstname)
+ d.Set("last_name", user.Lastname)
+ d.Set("username", user.Username)
+
+ return nil
+}
+
+func latestUser(users []*cloudstack.User) (*cloudstack.User, error) {
+ var latest time.Time
+ var user *cloudstack.User
+
+ for _, u := range users {
+ created, err := time.Parse("2006-01-02T15:04:05-0700", u.Created)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse creation date of a user: %s", err)
+ }
+
+ if created.After(latest) {
+ latest = created
+ user = u
+ }
+ }
+
+ return user, nil
+}
+
+func applyUserFilters(user *cloudstack.User, filters *schema.Set) (bool, error) {
+ var userJSON map[string]interface{}
+ k, _ := json.Marshal(user)
+ err := json.Unmarshal(k, &userJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ log.Print(m)
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ log.Print(updatedName)
+ userField := userJSON[updatedName].(string)
+ if !r.MatchString(userField) {
+ return false, nil
+ }
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_user_test.go b/cloudstack/data_source_cloudstack_user_test.go
new file mode 100644
index 0000000..aa9c14d
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_user_test.go
@@ -0,0 +1,70 @@
+//
+// 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/helper/resource"
+)
+
+func TestAccUserDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_user.user-resource"
+ datasourceName := "data.cloudstack_user.user-data-source"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testUserDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "first_name", resourceName, "first_name"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testUserDataSourceConfig_basic = `
+resource "cloudstack_user" "user-resource" {
+ account = "admin"
+ email = "jon.doe@gmail.com"
+ first_name = "jon"
+ last_name = "doe"
+ password = "password"
+ username = "jon123"
+}
+
+data "cloudstack_user" "user-data-source"{
+ filter{
+ name = "first_name"
+ value= "jon"
+ }
+ depends_on = [
+ cloudstack_user.user-resource
+ ]
+ }
+
+output "user-output" {
+ value = "${data.cloudstack_user.user-data-source}"
+}
+ `
diff --git a/cloudstack/data_source_cloudstack_volume.go b/cloudstack/data_source_cloudstack_volume.go
new file mode 100644
index 0000000..e8c6bdb
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_volume.go
@@ -0,0 +1,143 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackVolume() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackVolumeRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "disk_offering_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "zone_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func datasourceCloudStackVolumeRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.Volume.NewListVolumesParams()
+ csVolumes, err := cs.Volume.ListVolumes(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list volumes: %s", err)
+ }
+
+ filters := d.Get("filter")
+ var volumes []*cloudstack.Volume
+
+ for _, v := range csVolumes.Volumes {
+ match, err := applyVolumeFilters(v, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ volumes = append(volumes, v)
+ }
+ }
+
+ if len(volumes) == 0 {
+ return fmt.Errorf("No volume is matching with the specified regex")
+ }
+ //return the latest volume from the list of filtered volumes according
+ //to its creation date
+ volume, err := latestVolume(volumes)
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] Selected volume: %s\n", volume.Name)
+
+ return volumeDescriptionAttributes(d, volume)
+}
+
+func volumeDescriptionAttributes(d *schema.ResourceData, volume *cloudstack.Volume) error {
+ d.SetId(volume.Id)
+ d.Set("name", volume.Name)
+ d.Set("disk_offering_id", volume.Diskofferingid)
+ d.Set("zone_id", volume.Zoneid)
+
+ return nil
+}
+
+func latestVolume(volumes []*cloudstack.Volume) (*cloudstack.Volume, error) {
+ var latest time.Time
+ var volume *cloudstack.Volume
+
+ for _, v := range volumes {
+ created, err := time.Parse("2006-01-02T15:04:05-0700", v.Created)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse creation date of a volume: %s", err)
+ }
+
+ if created.After(latest) {
+ latest = created
+ volume = v
+ }
+ }
+
+ return volume, nil
+}
+
+func applyVolumeFilters(volume *cloudstack.Volume, filters *schema.Set) (bool, error) {
+ var volumeJSON map[string]interface{}
+ v, _ := json.Marshal(volume)
+ err := json.Unmarshal(v, &volumeJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ volume := volumeJSON[updatedName].(string)
+ if !r.MatchString(volume) {
+ return false, nil
+ }
+
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_volume_test.go b/cloudstack/data_source_cloudstack_volume_test.go
new file mode 100644
index 0000000..147a701
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_volume_test.go
@@ -0,0 +1,63 @@
+//
+// 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/helper/resource"
+)
+
+func TestAccVolumeDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_volume.volume-resource"
+ datasourceName := "data.cloudstack_volume.volume-data-source"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testVolumeDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testVolumeDataSourceConfig_basic = `
+resource "cloudstack_volume" "volume-resource"{
+ name = "TestVolume"
+ disk_offering_id = "0038adec-5e3e-47df-b4b4-77b5dc8e3338"
+ zone_id = "9a7002b2-09a2-44dc-a332-f2e4e7f01539"
+ }
+
+ data "cloudstack_volume" "volume-data-source"{
+ filter{
+ name = "name"
+ value="TestVolume"
+ }
+ depends_on = [
+ cloudstack_volume.volume-resource
+ ]
+ }
+ `
diff --git a/cloudstack/data_source_cloudstack_vpc.go b/cloudstack/data_source_cloudstack_vpc.go
new file mode 100644
index 0000000..a473d99
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_vpc.go
@@ -0,0 +1,173 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackVPC() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackVPCRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "display_text": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "cidr": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "vpc_offering_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "network_domain": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "project": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "zone_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "tags": tagsSchema(),
+ },
+ }
+}
+
+func datasourceCloudStackVPCRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.VPC.NewListVPCsParams()
+ csVPCs, err := cs.VPC.ListVPCs(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list VPCs: %s", err)
+ }
+
+ filters := d.Get("filter")
+ var vpcs []*cloudstack.VPC
+
+ for _, v := range csVPCs.VPCs {
+ match, err := applyVPCFilters(v, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ vpcs = append(vpcs, v)
+ }
+ }
+
+ if len(vpcs) == 0 {
+ return fmt.Errorf("No VPC is matching with the specified regex")
+ }
+ //return the latest VPC from the list of filtered VPCs according
+ //to its creation date
+ vpc, err := latestVPC(vpcs)
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] Selected VPCs: %s\n", vpc.Displaytext)
+
+ return vpcDescriptionAttributes(d, vpc)
+}
+
+func vpcDescriptionAttributes(d *schema.ResourceData, vpc *cloudstack.VPC) error {
+ d.SetId(vpc.Id)
+ d.Set("name", vpc.Name)
+ d.Set("display_text", vpc.Displaytext)
+ d.Set("cidr", vpc.Cidr)
+ d.Set("vpc_offering_name", vpc.Vpcofferingname)
+ d.Set("network_domain", vpc.Networkdomain)
+ d.Set("project", vpc.Project)
+ d.Set("zone_name", vpc.Zonename)
+ d.Set("tags", vpc.Tags)
+
+ return nil
+}
+
+func latestVPC(vpcs []*cloudstack.VPC) (*cloudstack.VPC, error) {
+ var latest time.Time
+ var vpc *cloudstack.VPC
+
+ for _, v := range vpcs {
+ created, err := time.Parse("2006-01-02T15:04:05-0700", v.Created)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse creation date of a VPC: %s", err)
+ }
+
+ if created.After(latest) {
+ latest = created
+ vpc = v
+ }
+ }
+
+ return vpc, nil
+}
+
+func applyVPCFilters(vpc *cloudstack.VPC, filters *schema.Set) (bool, error) {
+ var vpcJSON map[string]interface{}
+ k, _ := json.Marshal(vpc)
+ err := json.Unmarshal(k, &vpcJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ log.Print(m)
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ log.Print(updatedName)
+ vpcField := vpcJSON[updatedName].(string)
+ if !r.MatchString(vpcField) {
+ return false, nil
+ }
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_vpc_test.go b/cloudstack/data_source_cloudstack_vpc_test.go
new file mode 100644
index 0000000..3920f02
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_vpc_test.go
@@ -0,0 +1,72 @@
+//
+// 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/helper/resource"
+)
+
+func TestAccVPCDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_vpc.vpc-resource"
+ datasourceName := "data.cloudstack_vpc.vpc-data-source"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testVPCDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testVPCDataSourceConfig_basic = `
+resource "cloudstack_vpc" "vpc-resource" {
+name = "test-vpc"
+cidr = "10.0.0.0/16"
+vpc_offering = "Default VPC Offering"
+zone = "DC"
+}
+
+data "cloudstack_vpc" "vpc-data-source"{
+ filter{
+ name = "name"
+ value= "test-vpc"
+ }
+ filter{
+ name = "cidr"
+ value= "10.0.0.0/16"
+ }
+ depends_on = [
+ cloudstack_vpc.vpc-resource
+ ]
+}
+
+output "vpc-output" {
+value = "${data.cloudstack_vpc.vpc-data-source}"
+}
+ `
diff --git a/cloudstack/data_source_cloudstack_vpn_connection.go b/cloudstack/data_source_cloudstack_vpn_connection.go
new file mode 100644
index 0000000..c0015bf
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_vpn_connection.go
@@ -0,0 +1,140 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudstackVPNConnection() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackVPNConnectionRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "s2s_customer_gateway_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+
+ "s2s_vpn_gateway_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func datasourceCloudStackVPNConnectionRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.VPN.NewListVpnConnectionsParams()
+ csVPNConnections, err := cs.VPN.ListVpnConnections(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list VPNs: %s", err)
+ }
+
+ filters := d.Get("filter")
+ var vpnConnections []*cloudstack.VpnConnection
+
+ for _, v := range csVPNConnections.VpnConnections {
+ match, err := applyVPNConnectionFilters(v, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ vpnConnections = append(vpnConnections, v)
+ }
+ }
+
+ if len(vpnConnections) == 0 {
+ return fmt.Errorf("No VPN Connection is matching with the specified regex")
+ }
+ //return the latest VPN Connection from the list of filtered VPN Connections according
+ //to its creation date
+ vpnConnection, err := latestVPNConnection(vpnConnections)
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] Selected VPN Connections: %s\n", vpnConnection.Id)
+
+ return vpnConnectionDescriptionAttributes(d, vpnConnection)
+}
+
+func vpnConnectionDescriptionAttributes(d *schema.ResourceData, vpnConnection *cloudstack.VpnConnection) error {
+ d.SetId(vpnConnection.Id)
+ d.Set("s2s_customer_gateway_id", vpnConnection.S2scustomergatewayid)
+ d.Set("s2s_vpn_gateway_id", vpnConnection.S2svpngatewayid)
+
+ return nil
+}
+
+func latestVPNConnection(vpnConnections []*cloudstack.VpnConnection) (*cloudstack.VpnConnection, error) {
+ var latest time.Time
+ var vpnConnection *cloudstack.VpnConnection
+
+ for _, v := range vpnConnections {
+ created, err := time.Parse("2006-01-02T15:04:05-0700", v.Created)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse creation date of a VPN Connection: %s", err)
+ }
+
+ if created.After(latest) {
+ latest = created
+ vpnConnection = v
+ }
+ }
+
+ return vpnConnection, nil
+}
+
+func applyVPNConnectionFilters(vpnConnection *cloudstack.VpnConnection, filters *schema.Set) (bool, error) {
+ var vpnConnectionJSON map[string]interface{}
+ k, _ := json.Marshal(vpnConnection)
+ err := json.Unmarshal(k, &vpnConnectionJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ log.Print(m)
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ log.Print(updatedName)
+ vpnConnectionField := vpnConnectionJSON[updatedName].(string)
+ if !r.MatchString(vpnConnectionField) {
+ return false, nil
+ }
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_zone.go b/cloudstack/data_source_cloudstack_zone.go
new file mode 100644
index 0000000..39c8317
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_zone.go
@@ -0,0 +1,121 @@
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func dataSourceCloudStackZone() *schema.Resource {
+ return &schema.Resource{
+ Read: dataSourceCloudstackZoneRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "dns1": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "internal_dns1": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "network_type": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceCloudstackZoneRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.Zone.NewListZonesParams()
+ csZones, err := cs.Zone.ListZones(p)
+
+ if err != nil {
+ return fmt.Errorf("Failed to list zones: %s", err)
+ }
+ filters := d.Get("filter")
+ var zone *cloudstack.Zone
+
+ for _, z := range csZones.Zones {
+ match, err := applyZoneFilters(z, filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ zone = z
+ }
+ }
+
+ if zone == nil {
+ return fmt.Errorf("No zone is matching with the specified regex")
+ }
+ log.Printf("[DEBUG] Selected zone: %s\n", zone.Name)
+
+ return zoneDescriptionAttributes(d, zone)
+}
+
+func zoneDescriptionAttributes(d *schema.ResourceData, zone *cloudstack.Zone) error {
+ d.SetId(zone.Name)
+ d.Set("name", zone.Name)
+ d.Set("dns1", zone.Dns1)
+ d.Set("internal_dns1", zone.Internaldns1)
+ d.Set("network_type", zone.Networktype)
+
+ return nil
+}
+
+func applyZoneFilters(zone *cloudstack.Zone, filters *schema.Set) (bool, error) {
+ var zoneJSON map[string]interface{}
+ k, _ := json.Marshal(zone)
+ err := json.Unmarshal(k, &zoneJSON)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range filters.List() {
+ m := f.(map[string]interface{})
+ r, err := regexp.Compile(m["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("Invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
+ zoneField := zoneJSON[updatedName].(string)
+ if !r.MatchString(zoneField) {
+ return false, nil
+ }
+
+ }
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_zone_test.go b/cloudstack/data_source_cloudstack_zone_test.go
new file mode 100644
index 0000000..b02bb48
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_zone_test.go
@@ -0,0 +1,66 @@
+//
+// 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/helper/resource"
+)
+
+func TestAccZoneDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_zone.zone-resource"
+ datasourceName := "data.cloudstack_zone.zone-data-source"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testZoneDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+const testZoneDataSourceConfig_basic = `
+resource "cloudstack_zone" "zone-resource"{
+ name = "TestZone"
+ dns1 = "8.8.8.8"
+ internal_dns1 = "172.20.0.1"
+ network_type = "Advanced"
+ }
+
+ data "cloudstack_zone" "zone-data-source"{
+
+ filter{
+ name = "name"
+ value="TestZone"
+ }
+ depends_on = [
+ cloudstack_zone.zone-resource
+ ]
+
+ }
+ `
diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index 4949c2f..c39914e 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -78,7 +78,17 @@
},
DataSourcesMap: map[string]*schema.Resource{
- "cloudstack_template": dataSourceCloudstackTemplate(),
+ "cloudstack_template": dataSourceCloudstackTemplate(),
+ "cloudstack_ssh_keypair": dataSourceCloudstackSSHKeyPair(),
+ "cloudstack_instance": dataSourceCloudstackInstance(),
+ "cloudstack_network_offering": dataSourceCloudstackNetworkOffering(),
+ "cloudstack_zone": dataSourceCloudStackZone(),
+ "cloudstack_service_offering": dataSourceCloudstackServiceOffering(),
+ "cloudstack_volume": dataSourceCloudstackVolume(),
+ "cloudstack_vpc": dataSourceCloudstackVPC(),
+ "cloudstack_ipaddress": dataSourceCloudstackIPAddress(),
+ "cloudstack_user": dataSourceCloudstackUser(),
+ "cloudstack_vpn_connection": dataSourceCloudstackVPNConnection(),
},
ResourcesMap: map[string]*schema.Resource{
@@ -109,6 +119,14 @@
"cloudstack_vpn_connection": resourceCloudStackVPNConnection(),
"cloudstack_vpn_customer_gateway": resourceCloudStackVPNCustomerGateway(),
"cloudstack_vpn_gateway": resourceCloudStackVPNGateway(),
+ "cloudstack_network_offering": resourceCloudStackNetworkOffering(),
+ "cloudstack_disk_offering": resourceCloudStackDiskOffering(),
+ "cloudstack_volume": resourceCloudStackVolume(),
+ "cloudstack_zone": resourceCloudStackZone(),
+ "cloudstack_service_offering": resourceCloudStackServiceOffering(),
+ "cloudstack_account": resourceCloudStackAccount(),
+ "cloudstack_user": resourceCloudStackUser(),
+ "cloudstack_domain": resourceCloudStackDomain(),
},
ConfigureFunc: providerConfigure,
diff --git a/cloudstack/resource_cloudstack_account.go b/cloudstack/resource_cloudstack_account.go
new file mode 100644
index 0000000..a0d950f
--- /dev/null
+++ b/cloudstack/resource_cloudstack_account.go
@@ -0,0 +1,123 @@
+//
+// 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/helper/schema"
+)
+
+func resourceCloudStackAccount() *schema.Resource {
+ return &schema.Resource{
+ Read: resourceCloudStackAccountRead,
+ Update: resourceCloudStackAccountUpdate,
+ Create: resourceCloudStackAccountCreate,
+ Delete: resourceCloudStackAccountDelete,
+ Schema: map[string]*schema.Schema{
+ "email": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "first_name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "last_name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "password": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "username": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "account_type": {
+ Type: schema.TypeInt,
+ Required: true,
+ },
+ "role_id": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "account": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ },
+ }
+}
+
+func resourceCloudStackAccountCreate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ email := d.Get("email").(string)
+ first_name := d.Get("first_name").(string)
+ last_name := d.Get("last_name").(string)
+ username := d.Get("username").(string)
+ password := d.Get("password").(string)
+ role_id := d.Get("role_id").(string)
+ account_type := d.Get("account_type").(int)
+ account := d.Get("account").(string)
+
+ // Create a new parameter struct
+ p := cs.Account.NewCreateAccountParams(email, first_name, last_name, password, username)
+ p.SetAccounttype(int(account_type))
+ p.SetRoleid(role_id)
+ if account != "" {
+ p.SetAccount(account)
+ } else {
+ p.SetAccount(username)
+ }
+
+ log.Printf("[DEBUG] Creating Account %s", account)
+ a, err := cs.Account.CreateAccount(p)
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[DEBUG] Account %s successfully created", account)
+ d.SetId(a.Id)
+
+ return resourceCloudStackAccountRead(d, meta)
+}
+
+func resourceCloudStackAccountRead(d *schema.ResourceData, meta interface{}) error { return nil }
+
+func resourceCloudStackAccountUpdate(d *schema.ResourceData, meta interface{}) error { return nil }
+
+func resourceCloudStackAccountDelete(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ // Create a new parameter struct
+ p := cs.Account.NewDeleteAccountParams(d.Id())
+ _, err := cs.Account.DeleteAccount(p)
+
+ if err != nil {
+ return fmt.Errorf("Error deleting Account: %s", err)
+ }
+
+ return nil
+}
diff --git a/cloudstack/resource_cloudstack_disk_offering.go b/cloudstack/resource_cloudstack_disk_offering.go
new file mode 100644
index 0000000..daa6b88
--- /dev/null
+++ b/cloudstack/resource_cloudstack_disk_offering.go
@@ -0,0 +1,79 @@
+//
+// 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 (
+ "log"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func resourceCloudStackDiskOffering() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceCloudStackDiskOfferingCreate,
+ Read: resourceCloudStackDiskOfferingRead,
+ Update: resourceCloudStackDiskOfferingUpdate,
+ Delete: resourceCloudStackDiskOfferingDelete,
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "display_text": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "disk_size": {
+ Type: schema.TypeInt,
+ Required: true,
+ },
+ },
+ }
+}
+
+func resourceCloudStackDiskOfferingCreate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ name := d.Get("name").(string)
+ display_text := d.Get("display_text").(string)
+ disk_size := d.Get("disk_size").(int)
+
+ // Create a new parameter struct
+ p := cs.DiskOffering.NewCreateDiskOfferingParams(name, display_text)
+ p.SetDisksize(int64(disk_size))
+
+ log.Printf("[DEBUG] Creating Disk Offering %s", name)
+ diskOff, err := cs.DiskOffering.CreateDiskOffering(p)
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[DEBUG] Disk Offering %s successfully created", name)
+ d.SetId(diskOff.Id)
+
+ return resourceCloudStackDiskOfferingRead(d, meta)
+}
+
+func resourceCloudStackDiskOfferingRead(d *schema.ResourceData, meta interface{}) error { return nil }
+
+func resourceCloudStackDiskOfferingUpdate(d *schema.ResourceData, meta interface{}) error { return nil }
+
+func resourceCloudStackDiskOfferingDelete(d *schema.ResourceData, meta interface{}) error { return nil }
diff --git a/cloudstack/resource_cloudstack_domain.go b/cloudstack/resource_cloudstack_domain.go
new file mode 100644
index 0000000..4123a6e
--- /dev/null
+++ b/cloudstack/resource_cloudstack_domain.go
@@ -0,0 +1,108 @@
+//
+// 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/helper/schema"
+)
+
+func resourceCloudStackDomain() *schema.Resource {
+ return &schema.Resource{
+ Read: resourceCloudStackDomainRead,
+ Update: resourceCloudStackDomainUpdate,
+ Create: resourceCloudStackDomainCreate,
+ Delete: resourceCloudStackDomainDelete,
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "domain_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "network_domain": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "parent_domain_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ },
+ }
+}
+
+func resourceCloudStackDomainCreate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ name := d.Get("name").(string)
+ domain_id := d.Get("domain_id").(string)
+ network_domain := d.Get("network_domain").(string)
+ parent_domain_id := d.Get("parent_domain_id").(string)
+
+ // Create a new parameter struct
+ p := cs.Domain.NewCreateDomainParams(name)
+
+ if domain_id != "" {
+ p.SetDomainid(domain_id)
+ }
+
+ if network_domain != "" {
+ p.SetNetworkdomain(network_domain)
+ }
+
+ if parent_domain_id != "" {
+ p.SetParentdomainid(parent_domain_id)
+ }
+
+ log.Printf("[DEBUG] Creating Domain %s", name)
+ domain, err := cs.Domain.CreateDomain(p)
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[DEBUG] Domain %s successfully created", name)
+ d.SetId(domain.Id)
+
+ return resourceCloudStackDomainRead(d, meta)
+}
+
+func resourceCloudStackDomainRead(d *schema.ResourceData, meta interface{}) error { return nil }
+
+func resourceCloudStackDomainUpdate(d *schema.ResourceData, meta interface{}) error { return nil }
+
+func resourceCloudStackDomainDelete(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ // Create a new parameter struct
+ p := cs.Domain.NewDeleteDomainParams(d.Id())
+ _, err := cs.Domain.DeleteDomain(p)
+
+ if err != nil {
+ return fmt.Errorf("Error deleting Domain: %s", err)
+ }
+
+ return nil
+}
diff --git a/cloudstack/resource_cloudstack_network_offering.go b/cloudstack/resource_cloudstack_network_offering.go
new file mode 100644
index 0000000..e11ece2
--- /dev/null
+++ b/cloudstack/resource_cloudstack_network_offering.go
@@ -0,0 +1,214 @@
+//
+// 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/helper/schema"
+)
+
+func resourceCloudStackNetworkOffering() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceCloudStackNetworkOfferingCreate,
+ Read: resourceCloudStackNetworkOfferingRead,
+ Update: resourceCloudStackNetworkOfferingUpdate,
+ Delete: resourceCloudStackNetworkOfferingDelete,
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "display_text": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "guest_ip_type": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "traffic_type": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ },
+ }
+}
+
+func resourceCloudStackNetworkOfferingCreate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ name := d.Get("name").(string)
+ display_text := d.Get("display_text").(string)
+ guest_ip_type := d.Get("guest_ip_type").(string)
+ traffic_type := d.Get("traffic_type").(string)
+
+ // Create a new parameter struct
+ p := cs.NetworkOffering.NewCreateNetworkOfferingParams(display_text, guest_ip_type, name, []string{}, traffic_type)
+
+ if guest_ip_type == "Shared" {
+ p.SetSpecifyvlan(true)
+ p.SetSpecifyipranges(true)
+ }
+
+ log.Printf("[DEBUG] Creating Network Offering %s", name)
+ n, err := cs.NetworkOffering.CreateNetworkOffering(p)
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[DEBUG] Network Offering %s successfully created", name)
+ d.SetId(n.Id)
+
+ return resourceCloudStackNetworkOfferingRead(d, meta)
+}
+
+func resourceCloudStackNetworkOfferingUpdate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ d.Partial(true)
+
+ name := d.Get("name").(string)
+
+ // Check if the name is changed and if so, update the network offering
+ if d.HasChange("name") {
+ log.Printf("[DEBUG] Name changed for %s, starting update", name)
+
+ // Create a new parameter struct
+ p := cs.NetworkOffering.NewUpdateNetworkOfferingParams()
+
+ // Set the new name
+ p.SetName(d.Get("name").(string))
+
+ // Update the name
+ _, err := cs.NetworkOffering.UpdateNetworkOffering(p)
+ if err != nil {
+ return fmt.Errorf(
+ "Error updating the name for network offering %s: %s", name, err)
+ }
+
+ d.SetPartial("name")
+ }
+
+ // Check if the display text is changed and if so, update the virtual machine
+ if d.HasChange("display_text") {
+ log.Printf("[DEBUG] Display text changed for %s, starting update", name)
+
+ // Create a new parameter struct
+ p := cs.NetworkOffering.NewUpdateNetworkOfferingParams()
+
+ // Set the new display text
+ p.SetName(d.Get("display_text").(string))
+
+ // Update the display text
+ _, err := cs.NetworkOffering.UpdateNetworkOffering(p)
+ if err != nil {
+ return fmt.Errorf(
+ "Error updating the display text for network offering %s: %s", name, err)
+ }
+
+ d.SetPartial("display_text")
+ }
+
+ // Check if the guest ip type is changed and if so, update the virtual machine
+ if d.HasChange("guest_ip_type") {
+ log.Printf("[DEBUG] Guest ip type changed for %s, starting update", name)
+
+ // Create a new parameter struct
+ p := cs.NetworkOffering.NewUpdateNetworkOfferingParams()
+
+ // Set the new uest ip type
+ p.SetName(d.Get("guest_ip_type").(string))
+
+ // Update the uest ip type
+ _, err := cs.NetworkOffering.UpdateNetworkOffering(p)
+ if err != nil {
+ return fmt.Errorf(
+ "Error updating the guest ip type for network offering %s: %s", name, err)
+ }
+
+ d.SetPartial("guest_ip_type")
+ }
+
+ // Check if the traffic type is changed and if so, update the virtual machine
+ if d.HasChange("traffic_type") {
+ log.Printf("[DEBUG] Traffic type changed for %s, starting update", name)
+
+ // Create a new parameter struct
+ p := cs.NetworkOffering.NewUpdateNetworkOfferingParams()
+
+ // Set the new traffic type
+ p.SetName(d.Get("traffic_type").(string))
+
+ // Update the traffic type
+ _, err := cs.NetworkOffering.UpdateNetworkOffering(p)
+ if err != nil {
+ return fmt.Errorf(
+ "Error updating the traffic type for network offering %s: %s", name, err)
+ }
+
+ d.SetPartial("traffic_type")
+ }
+
+ d.Partial(false)
+
+ return resourceCloudStackInstanceRead(d, meta)
+}
+
+func resourceCloudStackNetworkOfferingDelete(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ // Create a new parameter struct
+ p := cs.NetworkOffering.NewDeleteNetworkOfferingParams(d.Id())
+ _, err := cs.NetworkOffering.DeleteNetworkOffering(p)
+
+ if err != nil {
+ return fmt.Errorf("Error deleting Network Offering: %s", err)
+ }
+
+ return nil
+}
+
+func resourceCloudStackNetworkOfferingRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ log.Printf("[DEBUG] Retrieving Network Offering %s", d.Get("name").(string))
+
+ // Get the Network Offering details
+ n, count, err := cs.NetworkOffering.GetNetworkOfferingByName(d.Get("name").(string))
+
+ if err != nil {
+ if count == 0 {
+ log.Printf("[DEBUG] Network Offering %s does no longer exist", d.Get("name").(string))
+ d.SetId("")
+ return nil
+ }
+
+ return err
+ }
+
+ d.SetId(n.Id)
+ d.Set("name", n.Name)
+ d.Set("display_text", n.Displaytext)
+ d.Set("guest_ip_type", n.Guestiptype)
+ d.Set("traffic_type", n.Traffictype)
+
+ return nil
+}
diff --git a/cloudstack/resource_cloudstack_service_offering.go b/cloudstack/resource_cloudstack_service_offering.go
new file mode 100644
index 0000000..ad887b1
--- /dev/null
+++ b/cloudstack/resource_cloudstack_service_offering.go
@@ -0,0 +1,156 @@
+//
+// 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/helper/schema"
+)
+
+func resourceCloudStackServiceOffering() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceCloudStackServiceOfferingCreate,
+ Read: resourceCloudStackServiceOfferingRead,
+ Update: resourceCloudStackServiceOfferingUpdate,
+ Delete: resourceCloudStackServiceOfferingDelete,
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "display_text": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ },
+ }
+}
+
+func resourceCloudStackServiceOfferingCreate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ name := d.Get("name").(string)
+ display_text := d.Get("display_text").(string)
+
+ // Create a new parameter struct
+ p := cs.ServiceOffering.NewCreateServiceOfferingParams(display_text, name)
+
+ log.Printf("[DEBUG] Creating Service Offering %s", name)
+ s, err := cs.ServiceOffering.CreateServiceOffering(p)
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[DEBUG] Service Offering %s successfully created", name)
+ d.SetId(s.Id)
+
+ return resourceCloudStackServiceOfferingRead(d, meta)
+}
+
+func resourceCloudStackServiceOfferingRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ log.Printf("[DEBUG] Retrieving Service Offering %s", d.Get("name").(string))
+
+ // Get the Service Offering details
+ s, count, err := cs.ServiceOffering.GetServiceOfferingByName(d.Get("name").(string))
+
+ if err != nil {
+ if count == 0 {
+ log.Printf("[DEBUG] Service Offering %s does no longer exist", d.Get("name").(string))
+ d.SetId("")
+ return nil
+ }
+ return err
+ }
+
+ d.SetId(s.Id)
+ d.Set("name", s.Name)
+ d.Set("display_text", s.Displaytext)
+
+ return nil
+}
+
+func resourceCloudStackServiceOfferingUpdate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ d.Partial(true)
+
+ name := d.Get("name").(string)
+
+ // Check if the name is changed and if so, update the service offering
+ if d.HasChange("name") {
+ log.Printf("[DEBUG] Name changed for %s, starting update", name)
+
+ // Create a new parameter struct
+ p := cs.ServiceOffering.NewUpdateServiceOfferingParams(d.Id())
+
+ // Set the new name
+ p.SetName(d.Get("name").(string))
+
+ // Update the name
+ _, err := cs.ServiceOffering.UpdateServiceOffering(p)
+ if err != nil {
+ return fmt.Errorf(
+ "Error updating the name for service offering %s: %s", name, err)
+ }
+
+ d.SetPartial("name")
+ }
+
+ // Check if the display text is changed and if so, update seervice offering
+ if d.HasChange("display_text") {
+ log.Printf("[DEBUG] Display text changed for %s, starting update", name)
+
+ // Create a new parameter struct
+ p := cs.ServiceOffering.NewUpdateServiceOfferingParams(d.Id())
+
+ // Set the new display text
+ p.SetName(d.Get("display_text").(string))
+
+ // Update the display text
+ _, err := cs.ServiceOffering.UpdateServiceOffering(p)
+ if err != nil {
+ return fmt.Errorf(
+ "Error updating the display text for service offering %s: %s", name, err)
+ }
+
+ d.SetPartial("display_text")
+ }
+
+ d.Partial(false)
+
+ return resourceCloudStackServiceOfferingRead(d, meta)
+}
+
+func resourceCloudStackServiceOfferingDelete(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ // Create a new parameter struct
+ p := cs.ServiceOffering.NewDeleteServiceOfferingParams(d.Id())
+ _, err := cs.ServiceOffering.DeleteServiceOffering(p)
+
+ if err != nil {
+ return fmt.Errorf("Error deleting Service Offering: %s", err)
+ }
+
+ return nil
+}
diff --git a/cloudstack/resource_cloudstack_user.go b/cloudstack/resource_cloudstack_user.go
new file mode 100644
index 0000000..aabb7b2
--- /dev/null
+++ b/cloudstack/resource_cloudstack_user.go
@@ -0,0 +1,110 @@
+//
+// 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/helper/schema"
+)
+
+func resourceCloudStackUser() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceCloudStackUserCreate,
+ Read: resourceCloudStackUserRead,
+ Update: resourceCloudStackUserUpdate,
+ Delete: resourceCloudStackUserDelete,
+ Schema: map[string]*schema.Schema{
+ "account": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "email": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "first_name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "last_name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "password": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "username": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ },
+ }
+}
+
+func resourceCloudStackUserCreate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ account := d.Get("account").(string)
+ email := d.Get("email").(string)
+ first_name := d.Get("first_name").(string)
+ last_name := d.Get("last_name").(string)
+ password := d.Get("password").(string)
+ username := d.Get("username").(string)
+
+ // Create a new parameter struct
+ p := cs.User.NewCreateUserParams(account, email, first_name, last_name, password, username)
+
+ log.Printf("[DEBUG] Creating User %s", username)
+ u, err := cs.User.CreateUser(p)
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[DEBUG] User %s successfully created", username)
+ d.SetId(u.Id)
+
+ return resourceCloudStackUserRead(d, meta)
+}
+
+func resourceCloudStackUserUpdate(d *schema.ResourceData, meta interface{}) error {
+ return resourceCloudStackUserRead(d, meta)
+}
+
+func resourceCloudStackUserDelete(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ // Create a new parameter struct
+ p := cs.User.NewDeleteUserParams(d.Id())
+ _, err := cs.User.DeleteUser(p)
+
+ if err != nil {
+ return fmt.Errorf("Error deleting User: %s", err)
+ }
+
+ return nil
+}
+
+func resourceCloudStackUserRead(d *schema.ResourceData, meta interface{}) error {
+ return nil
+}
diff --git a/cloudstack/resource_cloudstack_volume.go b/cloudstack/resource_cloudstack_volume.go
new file mode 100644
index 0000000..a2fb725
--- /dev/null
+++ b/cloudstack/resource_cloudstack_volume.go
@@ -0,0 +1,115 @@
+//
+// 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/helper/schema"
+)
+
+func resourceCloudStackVolume() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceCloudStackVolumeCreate,
+ Read: resourceCloudStackVolumeRead,
+ Delete: resourceCloudStackVolumeDelete,
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ "disk_offering_id": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ "zone_id": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ },
+ }
+}
+
+func resourceCloudStackVolumeCreate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ name := d.Get("name").(string)
+ disk_offering_id := d.Get("disk_offering_id").(string)
+ zone_id := d.Get("zone_id").(string)
+
+ //Create a new parameter struct
+ p := cs.Volume.NewCreateVolumeParams()
+ p.SetDiskofferingid(disk_offering_id)
+ p.SetZoneid(zone_id)
+ p.SetName(name)
+
+ log.Printf("[DEBUG] Creating Volume %s", name)
+ v, err := cs.Volume.CreateVolume(p)
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[DEBUG] Volume %s successfully created", name)
+ d.SetId(v.Id)
+
+ return resourceCloudStackVolumeRead(d, meta)
+}
+func resourceCloudStackVolumeRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ log.Printf("[DEBUG] Retrieving Volume %s", d.Get("name").(string))
+
+ // Get the Volume details
+ v, count, err := cs.Volume.GetVolumeByName(d.Get("name").(string))
+
+ if err != nil {
+ if count == 0 {
+ log.Printf("[DEBUG] Volume %s does no longer exist", d.Get("name").(string))
+ d.SetId("")
+ return nil
+ }
+ return err
+ }
+
+ d.SetId(v.Id)
+ d.Set("name", v.Name)
+ d.Set("disk_offering_id", v.Diskofferingid)
+ d.Set("zone_id", v.Zoneid)
+
+ return nil
+}
+
+func resourceCloudStackVolumeDelete(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ // Create a new parameter struct
+ p := cs.Volume.NewDeleteVolumeParams(d.Id())
+ _, err := cs.Volume.DeleteVolume(p)
+
+ if err != nil {
+ return fmt.Errorf("Error deleting Volume: %s", err)
+ }
+
+ return nil
+}
diff --git a/cloudstack/resource_cloudstack_zone.go b/cloudstack/resource_cloudstack_zone.go
new file mode 100644
index 0000000..a259f65
--- /dev/null
+++ b/cloudstack/resource_cloudstack_zone.go
@@ -0,0 +1,119 @@
+//
+// 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/helper/schema"
+)
+
+func resourceCloudStackZone() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceCloudStackZoneCreate,
+ Read: resourceCloudStackZoneRead,
+ Update: resourceCloudStackZoneUpdate,
+ Delete: resourceCloudStackZoneDelete,
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "dns1": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "internal_dns1": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "network_type": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ },
+ }
+}
+
+func resourceCloudStackZoneCreate(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ name := d.Get("name").(string)
+ dns1 := d.Get("dns1").(string)
+ internal_dns1 := d.Get("internal_dns1").(string)
+ network_type := d.Get("network_type").(string)
+
+ // Create a new parameter struct
+ p := cs.Zone.NewCreateZoneParams(dns1, internal_dns1, name, network_type)
+
+ log.Printf("[DEBUG] Creating Zone %s", name)
+ n, err := cs.Zone.CreateZone(p)
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("[DEBUG] Zone %s successfully created", name)
+ d.SetId(n.Id)
+
+ return resourceCloudStackZoneRead(d, meta)
+}
+
+func resourceCloudStackZoneRead(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ log.Printf("[DEBUG] Retrieving Zone %s", d.Get("name").(string))
+
+ // Get the Zone details
+ z, count, err := cs.Zone.GetZoneByName(d.Get("name").(string))
+
+ if err != nil {
+ if count == 0 {
+ log.Printf("[DEBUG] Zone %s does no longer exist", d.Get("name").(string))
+ d.SetId("")
+ return nil
+ }
+ return err
+ }
+
+ d.SetId(z.Id)
+ d.Set("name", z.Name)
+ d.Set("dns1", z.Dns1)
+ d.Set("internal_dns1", z.Internaldns1)
+ d.Set("network_type", z.Networktype)
+
+ return nil
+}
+
+func resourceCloudStackZoneUpdate(d *schema.ResourceData, meta interface{}) error { return nil }
+
+func resourceCloudStackZoneDelete(d *schema.ResourceData, meta interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ // Create a new parameter struct
+ p := cs.Zone.NewDeleteZoneParams(d.Id())
+ _, err := cs.Zone.DeleteZone(p)
+
+ if err != nil {
+ return fmt.Errorf("Error deleting Zone: %s", err)
+ }
+
+ return nil
+}