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