blob: 743cd10bf954d78fb026c7bf857cb100c977cc04 [file] [log] [blame]
//
// 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"
"strings"
"github.com/apache/cloudstack-go/v2/cloudstack"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceCloudStackIPAddress() *schema.Resource {
return &schema.Resource{
Create: resourceCloudStackIPAddressCreate,
Read: resourceCloudStackIPAddressRead,
Delete: resourceCloudStackIPAddressDelete,
Schema: map[string]*schema.Schema{
"is_portable": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
},
"network_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"vpc_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"zone": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"project": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"ip_address": {
Type: schema.TypeString,
Computed: true,
},
"tags": tagsSchema(),
},
}
}
func resourceCloudStackIPAddressCreate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
if err := verifyIPAddressParams(d); err != nil {
return err
}
// Create a new parameter struct
p := cs.Address.NewAssociateIpAddressParams()
if d.Get("is_portable").(bool) {
p.SetIsportable(true)
}
if networkid, ok := d.GetOk("network_id"); ok {
// Set the networkid
p.SetNetworkid(networkid.(string))
}
if vpcid, ok := d.GetOk("vpc_id"); ok {
// Set the vpcid
p.SetVpcid(vpcid.(string))
}
if zone, ok := d.GetOk("zone"); ok {
// Retrieve the zone ID
zoneid, e := retrieveID(cs, "zone", zone.(string))
if e != nil {
return e.Error()
}
// Set the zoneid
p.SetZoneid(zoneid)
}
// If there is a project supplied, we retrieve and set the project id
if err := setProjectid(p, cs, d); err != nil {
return err
}
// Associate a new IP address
r, err := cs.Address.AssociateIpAddress(p)
if err != nil {
return fmt.Errorf("Error associating a new IP address: %s", err)
}
d.SetId(r.Id)
// Set tags if necessary
err = setTags(cs, d, "PublicIpAddress")
if err != nil {
return fmt.Errorf("Error setting tags on the IP address: %s", err)
}
return resourceCloudStackIPAddressRead(d, meta)
}
func resourceCloudStackIPAddressRead(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
// Get the IP address details
ip, count, err := cs.Address.GetPublicIpAddressByID(
d.Id(),
cloudstack.WithProject(d.Get("project").(string)),
)
if err != nil {
if count == 0 {
log.Printf(
"[DEBUG] IP address with ID %s is no longer associated", d.Id())
d.SetId("")
return nil
}
return err
}
d.Set("is_portable", ip.Isportable)
// Updated the IP address
d.Set("ip_address", ip.Ipaddress)
if _, ok := d.GetOk("network_id"); ok {
d.Set("network_id", ip.Associatednetworkid)
}
if _, ok := d.GetOk("vpc_id"); ok {
d.Set("vpc_id", ip.Vpcid)
}
if _, ok := d.GetOk("zone"); ok {
setValueOrID(d, "zone", ip.Zonename, ip.Zoneid)
}
tags := make(map[string]interface{})
for _, tag := range ip.Tags {
tags[tag.Key] = tag.Value
}
d.Set("tags", tags)
setValueOrID(d, "project", ip.Project, ip.Projectid)
return nil
}
func resourceCloudStackIPAddressDelete(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
// Create a new parameter struct
p := cs.Address.NewDisassociateIpAddressParams(d.Id())
// Disassociate the IP address
if _, err := cs.Address.DisassociateIpAddress(p); err != nil {
// This is a very poor way to be told the ID does no longer exist :(
if strings.Contains(err.Error(), fmt.Sprintf(
"Invalid parameter id value=%s due to incorrect long value format, "+
"or entity does not exist", d.Id())) {
return nil
}
return fmt.Errorf("Error disassociating IP address %s: %s", d.Id(), err)
}
return nil
}
func verifyIPAddressParams(d *schema.ResourceData) error {
_, portable := d.GetOk("is_portable")
_, network := d.GetOk("network_id")
_, vpc := d.GetOk("vpc_id")
_, zone := d.GetOk("zone")
if portable && ((network && vpc) || (!network && !vpc)) {
return fmt.Errorf(
"You must supply a value for either (so not both) the 'network_id' or 'vpc_id' parameter for a portable IP")
}
if !portable && !zone && !network {
return fmt.Errorf("You must supply a value for the 'network_id' and/or 'zone' parameters for a non portable IP")
}
return nil
}