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