github.com/danrjohnson/terraform@v0.7.0-rc2.0.20160627135212-d0fc1fa086ff/builtin/providers/cloudstack/resource_cloudstack_network.go (about)

     1  package cloudstack
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  	"github.com/xanzy/go-cloudstack/cloudstack"
    12  )
    13  
    14  const none = "none"
    15  
    16  func resourceCloudStackNetwork() *schema.Resource {
    17  	aclidSchema := &schema.Schema{
    18  		Type:          schema.TypeString,
    19  		Optional:      true,
    20  		Default:       none,
    21  		ConflictsWith: []string{"aclid"},
    22  	}
    23  
    24  	aclidSchema.StateFunc = func(v interface{}) string {
    25  		value := v.(string)
    26  
    27  		if value == none {
    28  			aclidSchema.ForceNew = true
    29  		}
    30  
    31  		return value
    32  	}
    33  
    34  	return &schema.Resource{
    35  		Create: resourceCloudStackNetworkCreate,
    36  		Read:   resourceCloudStackNetworkRead,
    37  		Update: resourceCloudStackNetworkUpdate,
    38  		Delete: resourceCloudStackNetworkDelete,
    39  
    40  		Schema: map[string]*schema.Schema{
    41  			"name": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Required: true,
    44  			},
    45  
    46  			"display_text": &schema.Schema{
    47  				Type:     schema.TypeString,
    48  				Optional: true,
    49  				Computed: true,
    50  			},
    51  
    52  			"cidr": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Required: true,
    55  				ForceNew: true,
    56  			},
    57  
    58  			"gateway": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Optional: true,
    61  				Computed: true,
    62  				ForceNew: true,
    63  			},
    64  
    65  			"startip": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Optional: true,
    68  				Computed: true,
    69  				ForceNew: true,
    70  			},
    71  
    72  			"endip": &schema.Schema{
    73  				Type:     schema.TypeString,
    74  				Optional: true,
    75  				Computed: true,
    76  				ForceNew: true,
    77  			},
    78  
    79  			"network_offering": &schema.Schema{
    80  				Type:     schema.TypeString,
    81  				Required: true,
    82  			},
    83  
    84  			"vlan": &schema.Schema{
    85  				Type:     schema.TypeInt,
    86  				Optional: true,
    87  				ForceNew: true,
    88  			},
    89  
    90  			"vpc_id": &schema.Schema{
    91  				Type:     schema.TypeString,
    92  				Optional: true,
    93  				Computed: true,
    94  				ForceNew: true,
    95  			},
    96  
    97  			"vpc": &schema.Schema{
    98  				Type:       schema.TypeString,
    99  				Optional:   true,
   100  				ForceNew:   true,
   101  				Deprecated: "Please use the `vpc_id` field instead",
   102  			},
   103  
   104  			"acl_id": aclidSchema,
   105  
   106  			"aclid": &schema.Schema{
   107  				Type:       schema.TypeString,
   108  				Optional:   true,
   109  				Deprecated: "Please use the `acl_id` field instead",
   110  			},
   111  
   112  			"project": &schema.Schema{
   113  				Type:     schema.TypeString,
   114  				Optional: true,
   115  				ForceNew: true,
   116  			},
   117  
   118  			"zone": &schema.Schema{
   119  				Type:     schema.TypeString,
   120  				Required: true,
   121  				ForceNew: true,
   122  			},
   123  
   124  			"tags": tagsSchema(),
   125  		},
   126  	}
   127  }
   128  
   129  func resourceCloudStackNetworkCreate(d *schema.ResourceData, meta interface{}) error {
   130  	cs := meta.(*cloudstack.CloudStackClient)
   131  
   132  	name := d.Get("name").(string)
   133  
   134  	// Retrieve the network_offering ID
   135  	networkofferingid, e := retrieveID(cs, "network_offering", d.Get("network_offering").(string))
   136  	if e != nil {
   137  		return e.Error()
   138  	}
   139  
   140  	// Retrieve the zone ID
   141  	zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
   142  	if e != nil {
   143  		return e.Error()
   144  	}
   145  
   146  	// Compute/set the display text
   147  	displaytext, ok := d.GetOk("display_text")
   148  	if !ok {
   149  		displaytext = name
   150  	}
   151  	// Create a new parameter struct
   152  	p := cs.Network.NewCreateNetworkParams(displaytext.(string), name, networkofferingid, zoneid)
   153  
   154  	m, err := parseCIDR(d)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	// Set the needed IP config
   160  	p.SetStartip(m["startip"])
   161  	p.SetGateway(m["gateway"])
   162  	p.SetEndip(m["endip"])
   163  	p.SetNetmask(m["netmask"])
   164  
   165  	if vlan, ok := d.GetOk("vlan"); ok {
   166  		p.SetVlan(strconv.Itoa(vlan.(int)))
   167  	}
   168  
   169  	// Check is this network needs to be created in a VPC
   170  	vpc, ok := d.GetOk("vpc_id")
   171  	if !ok {
   172  		vpc, ok = d.GetOk("vpc")
   173  	}
   174  	if ok {
   175  		// Retrieve the vpc ID
   176  		vpcid, e := retrieveID(
   177  			cs,
   178  			"vpc",
   179  			vpc.(string),
   180  			cloudstack.WithProject(d.Get("project").(string)),
   181  		)
   182  		if e != nil {
   183  			return e.Error()
   184  		}
   185  
   186  		// Set the vpcid
   187  		p.SetVpcid(vpcid)
   188  
   189  		// Since we're in a VPC, check if we want to assiciate an ACL list
   190  		aclid, ok := d.GetOk("acl_id")
   191  		if !ok {
   192  			aclid, ok = d.GetOk("acl")
   193  		}
   194  		if ok && aclid != none {
   195  			// Set the acl ID
   196  			p.SetAclid(aclid.(string))
   197  		}
   198  	}
   199  
   200  	// If there is a project supplied, we retrieve and set the project id
   201  	if err := setProjectid(p, cs, d); err != nil {
   202  		return err
   203  	}
   204  
   205  	// Create the new network
   206  	r, err := cs.Network.CreateNetwork(p)
   207  	if err != nil {
   208  		return fmt.Errorf("Error creating network %s: %s", name, err)
   209  	}
   210  
   211  	d.SetId(r.Id)
   212  
   213  	err = setTags(cs, d, "network")
   214  	if err != nil {
   215  		return fmt.Errorf("Error setting tags: %s", err)
   216  	}
   217  
   218  	return resourceCloudStackNetworkRead(d, meta)
   219  }
   220  
   221  func resourceCloudStackNetworkRead(d *schema.ResourceData, meta interface{}) error {
   222  	cs := meta.(*cloudstack.CloudStackClient)
   223  
   224  	// Get the virtual machine details
   225  	n, count, err := cs.Network.GetNetworkByID(
   226  		d.Id(),
   227  		cloudstack.WithProject(d.Get("project").(string)),
   228  	)
   229  	if err != nil {
   230  		if count == 0 {
   231  			log.Printf(
   232  				"[DEBUG] Network %s does no longer exist", d.Get("name").(string))
   233  			d.SetId("")
   234  			return nil
   235  		}
   236  
   237  		return err
   238  	}
   239  
   240  	d.Set("name", n.Name)
   241  	d.Set("display_text", n.Displaytext)
   242  	d.Set("cidr", n.Cidr)
   243  	d.Set("gateway", n.Gateway)
   244  
   245  	_, vpcID := d.GetOk("vpc_id")
   246  	_, vpc := d.GetOk("vpc")
   247  	if vpcID || vpc {
   248  		d.Set("vpc_id", n.Vpcid)
   249  
   250  		// Since we're in a VPC, also update the ACL ID. If we don't
   251  		// have an ACL ID make sure we set the default value instead.
   252  		if n.Aclid == "" {
   253  			n.Aclid = none
   254  		}
   255  		d.Set("acl_id", n.Aclid)
   256  	}
   257  
   258  	// Read the tags and store them in a map
   259  	tags := make(map[string]interface{})
   260  	for item := range n.Tags {
   261  		tags[n.Tags[item].Key] = n.Tags[item].Value
   262  	}
   263  	d.Set("tags", tags)
   264  
   265  	setValueOrID(d, "network_offering", n.Networkofferingname, n.Networkofferingid)
   266  	setValueOrID(d, "project", n.Project, n.Projectid)
   267  	setValueOrID(d, "zone", n.Zonename, n.Zoneid)
   268  
   269  	return nil
   270  }
   271  
   272  func resourceCloudStackNetworkUpdate(d *schema.ResourceData, meta interface{}) error {
   273  	cs := meta.(*cloudstack.CloudStackClient)
   274  	name := d.Get("name").(string)
   275  
   276  	// Create a new parameter struct
   277  	p := cs.Network.NewUpdateNetworkParams(d.Id())
   278  
   279  	// Check if the name or display text is changed
   280  	if d.HasChange("name") || d.HasChange("display_text") {
   281  		p.SetName(name)
   282  
   283  		// Compute/set the display text
   284  		displaytext := d.Get("display_text").(string)
   285  		if displaytext == "" {
   286  			displaytext = name
   287  		}
   288  		p.SetDisplaytext(displaytext)
   289  	}
   290  
   291  	// Check if the cidr is changed
   292  	if d.HasChange("cidr") {
   293  		p.SetGuestvmcidr(d.Get("cidr").(string))
   294  	}
   295  
   296  	// Check if the network offering is changed
   297  	if d.HasChange("network_offering") {
   298  		// Retrieve the network_offering ID
   299  		networkofferingid, e := retrieveID(cs, "network_offering", d.Get("network_offering").(string))
   300  		if e != nil {
   301  			return e.Error()
   302  		}
   303  		// Set the new network offering
   304  		p.SetNetworkofferingid(networkofferingid)
   305  	}
   306  
   307  	// Update the network
   308  	_, err := cs.Network.UpdateNetwork(p)
   309  	if err != nil {
   310  		return fmt.Errorf(
   311  			"Error updating network %s: %s", name, err)
   312  	}
   313  
   314  	// Replace the ACL if the ID has changed
   315  	if d.HasChange("acl_id") || d.HasChange("acl") {
   316  		aclid, ok := d.GetOk("acl_id")
   317  		if !ok {
   318  			aclid, ok = d.GetOk("acl")
   319  		}
   320  		if !ok {
   321  			return fmt.Errorf("Replacing the ACL requires a valid ACL ID")
   322  		}
   323  
   324  		p := cs.NetworkACL.NewReplaceNetworkACLListParams(aclid.(string))
   325  		p.SetNetworkid(d.Id())
   326  
   327  		_, err := cs.NetworkACL.ReplaceNetworkACLList(p)
   328  		if err != nil {
   329  			return fmt.Errorf("Error replacing ACL: %s", err)
   330  		}
   331  	}
   332  
   333  	// Update tags if they have changed
   334  	if d.HasChange("tags") {
   335  		err = setTags(cs, d, "network")
   336  		if err != nil {
   337  			return fmt.Errorf("Error updating tags: %s", err)
   338  		}
   339  	}
   340  
   341  	return resourceCloudStackNetworkRead(d, meta)
   342  }
   343  
   344  func resourceCloudStackNetworkDelete(d *schema.ResourceData, meta interface{}) error {
   345  	cs := meta.(*cloudstack.CloudStackClient)
   346  
   347  	// Create a new parameter struct
   348  	p := cs.Network.NewDeleteNetworkParams(d.Id())
   349  
   350  	// Delete the network
   351  	_, err := cs.Network.DeleteNetwork(p)
   352  	if err != nil {
   353  		// This is a very poor way to be told the ID does no longer exist :(
   354  		if strings.Contains(err.Error(), fmt.Sprintf(
   355  			"Invalid parameter id value=%s due to incorrect long value format, "+
   356  				"or entity does not exist", d.Id())) {
   357  			return nil
   358  		}
   359  
   360  		return fmt.Errorf("Error deleting network %s: %s", d.Get("name").(string), err)
   361  	}
   362  	return nil
   363  }
   364  
   365  func parseCIDR(d *schema.ResourceData) (map[string]string, error) {
   366  	m := make(map[string]string, 4)
   367  
   368  	cidr := d.Get("cidr").(string)
   369  	ip, ipnet, err := net.ParseCIDR(cidr)
   370  	if err != nil {
   371  		return nil, fmt.Errorf("Unable to parse cidr %s: %s", cidr, err)
   372  	}
   373  
   374  	msk := ipnet.Mask
   375  	sub := ip.Mask(msk)
   376  
   377  	m["netmask"] = fmt.Sprintf("%d.%d.%d.%d", msk[0], msk[1], msk[2], msk[3])
   378  
   379  	if gateway, ok := d.GetOk("gateway"); ok {
   380  		m["gateway"] = gateway.(string)
   381  	} else {
   382  		m["gateway"] = fmt.Sprintf("%d.%d.%d.%d", sub[0], sub[1], sub[2], sub[3]+1)
   383  	}
   384  
   385  	if startip, ok := d.GetOk("startip"); ok {
   386  		m["startip"] = startip.(string)
   387  	} else {
   388  		m["startip"] = fmt.Sprintf("%d.%d.%d.%d", sub[0], sub[1], sub[2], sub[3]+2)
   389  	}
   390  
   391  	if endip, ok := d.GetOk("endip"); ok {
   392  		m["endip"] = endip.(string)
   393  	} else {
   394  		m["endip"] = fmt.Sprintf("%d.%d.%d.%d",
   395  			sub[0]+(0xff-msk[0]), sub[1]+(0xff-msk[1]), sub[2]+(0xff-msk[2]), sub[3]+(0xff-msk[3]-1))
   396  	}
   397  
   398  	return m, nil
   399  }