aboutsummaryrefslogtreecommitdiff
path: root/libvirt
diff options
context:
space:
mode:
authorAlvaro Saurin <alvaro.saurin@gmail.com>2018-03-13 18:54:52 +0100
committerAlvaro Saurin <alvaro.saurin@gmail.com>2018-03-14 11:34:35 +0100
commitce90234bfc8aa09d5aead5992af2e07667603d07 (patch)
tree385272a5233b187d01bcc0b591286b8dccebd29a /libvirt
parentd37e941778af77694f63306d4cae3250885abd87 (diff)
downloadterraform-provider-libvirt-ce90234bfc8aa09d5aead5992af2e07667603d07.tar
terraform-provider-libvirt-ce90234bfc8aa09d5aead5992af2e07667603d07.tar.gz
More configuration options for networks
Diffstat (limited to 'libvirt')
-rw-r--r--libvirt/resource_libvirt_domain.go2
-rw-r--r--libvirt/resource_libvirt_network.go274
-rw-r--r--libvirt/resource_libvirt_network_test.go118
-rw-r--r--libvirt/utils_net.go10
-rw-r--r--libvirt/utils_net_test.go2
-rw-r--r--libvirt/utils_test.go2
6 files changed, 345 insertions, 63 deletions
diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go
index f7d7a074..526f5a91 100644
--- a/libvirt/resource_libvirt_domain.go
+++ b/libvirt/resource_libvirt_domain.go
@@ -1238,7 +1238,7 @@ func setNetworkInterfaces(d *schema.ResourceData, domainDef *libvirtxml.Domain,
mac = strings.ToUpper(macI.(string))
} else {
var err error
- mac, err = RandomMACAddress()
+ mac, err = randomMACAddress()
if err != nil {
return fmt.Errorf("Error generating mac address: %s", err)
}
diff --git a/libvirt/resource_libvirt_network.go b/libvirt/resource_libvirt_network.go
index 4f984e72..773e9b29 100644
--- a/libvirt/resource_libvirt_network.go
+++ b/libvirt/resource_libvirt_network.go
@@ -78,27 +78,83 @@ func resourceLibvirtNetwork() *schema.Resource {
Optional: true,
Required: false,
},
- "dns_forwarder": {
+ "dhcp": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
+ MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
- "address": {
- Type: schema.TypeString,
+ "enabled": {
+ Type: schema.TypeBool,
+ Default: true,
Optional: true,
Required: false,
- ForceNew: true,
},
- "domain": {
- Type: schema.TypeString,
+ },
+ },
+ },
+ "dns": {
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "enabled": {
+ Type: schema.TypeBool,
+ Default: true,
Optional: true,
Required: false,
+ },
+ "local_only": {
+ Type: schema.TypeBool,
+ Default: false,
+ Optional: true,
+ Required: false,
+ },
+ "forwarders": {
+ Type: schema.TypeList,
+ Optional: true,
ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "host": {
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "address": {
+ Type: schema.TypeString,
+ Optional: true,
+ Required: false,
+ ForceNew: true,
+ },
+ "name": {
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ },
+ },
},
},
},
},
+ "routes": {
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
},
}
}
@@ -167,8 +223,17 @@ func resourceLibvirtNetworkCreate(d *schema.ResourceData, meta interface{}) erro
networkDef := newNetworkDef()
networkDef.Name = d.Get("name").(string)
- networkDef.Domain = &libvirtxml.NetworkDomain{
- Name: d.Get("domain").(string),
+
+ if domain, ok := d.GetOk("domain"); ok {
+ networkDef.Domain = &libvirtxml.NetworkDomain{
+ Name: domain.(string),
+ }
+
+ if dnsLocalOnly, ok := d.GetOk("dns.0.local_only"); ok {
+ if dnsLocalOnly.(bool) {
+ networkDef.Domain.LocalOnly = "yes" // this "boolean" must be "yes"|"no"
+ }
+ }
}
// use a bridge provided by the user, or create one otherwise (libvirt will assign on automatically when empty)
@@ -195,77 +260,128 @@ func resourceLibvirtNetworkCreate(d *schema.ResourceData, meta interface{}) erro
networkDef.Forward.NAT = nil
}
- // some network modes require a DHCP/DNS server
- // set the addresses for DHCP
+ // set the addresses
if addresses, ok := d.GetOk("addresses"); ok {
ipsPtrsLst := []libvirtxml.NetworkIP{}
for _, addressI := range addresses.([]interface{}) {
- address := addressI.(string)
+ // get the IP address entry for this subnet (with a guessed DHCP range)
+ dni, dhcp, err := setNetworkIP(addressI.(string))
+ if err != nil {
+ return err
+ }
+ if d.Get("dhcp.0.enabled").(bool) {
+ dni.DHCP = dhcp
+ }
+
+ ipsPtrsLst = append(ipsPtrsLst, *dni)
+ }
+ networkDef.IPs = ipsPtrsLst
+ }
+
+ // set static routes
+ if routes, ok := d.GetOk("routes"); ok {
+ for _, routeI := range routes.([]interface{}) {
+ route := libvirtxml.NetworkRoute{}
+
+ routeComponents := strings.Split(routeI.(string), "->")
+ if len(routeComponents) != 2 {
+ return fmt.Errorf("Error parsing address '%s'", routeI.(string))
+ }
+
+ address := strings.TrimSpace(routeComponents[0])
+ gateway := strings.TrimSpace(routeComponents[1])
+
+ // parse the address
_, ipNet, err := net.ParseCIDR(address)
if err != nil {
- return fmt.Errorf("Error parsing addresses definition '%s': %s", address, err)
+ return fmt.Errorf("Error parsing address '%s': %s", address, err)
}
ones, bits := ipNet.Mask.Size()
family := "ipv4"
if bits == (net.IPv6len * 8) {
family = "ipv6"
}
- ipsRange := 2 ^ bits - 2 ^ ones
- if ipsRange < 4 {
- return fmt.Errorf("Netmask seems to be too strict: only %d IPs available (%s)", ipsRange-3, family)
+ route.Address = ipNet.IP.String()
+ route.Prefix = strconv.Itoa(ones)
+ route.Family = family
+
+ // parse and check the gateway
+ parsedGateway := net.ParseIP(gateway)
+ if parsedGateway == nil {
+ return fmt.Errorf("Could not parse IP address '%s'", parsedGateway)
}
+ route.Gateway = parsedGateway.String()
- // we should calculate the range served by DHCP. For example, for
- // 192.168.121.0/24 we will serve 192.168.121.2 - 192.168.121.254
- start, end := NetworkRange(ipNet)
+ networkDef.Routes = append(networkDef.Routes, route)
+ }
+ }
- // skip the .0, (for the network),
- start[len(start)-1]++
+ if _, ok := d.GetOk("dns.0"); ok {
+ dnsPrefix := "dns.0"
+ dns := libvirtxml.NetworkDNS{}
- // assign the .1 to the host interface
- dni := libvirtxml.NetworkIP{
- Address: start.String(),
- Prefix: strconv.Itoa(ones),
- Family: family,
- }
+ if d.Get(dnsPrefix + ".enabled").(bool) {
+ dns.Enable = "yes"
+ }
- start[len(start)-1]++ // then skip the .1
- end[len(end)-1]-- // and skip the .255 (for broadcast)
+ if forwarders, ok := d.GetOk(dnsPrefix + ".forwarders"); ok {
+ dns.Forwarders = []libvirtxml.NetworkDNSForwarder{}
+
+ for _, forwardersI := range forwarders.([]interface{}) {
+ forwarderSpec := forwardersI.(string)
+ ip := ""
+ domain := ""
+
+ forwarderComponents := strings.Split(forwarderSpec, "->")
+ if len(forwarderComponents) == 1 {
+ // the first element can be an IP or a domain: we must identify the class
+ target := strings.TrimSpace(forwarderComponents[0])
+ parsedIP := net.ParseIP(target)
+ if parsedIP != nil {
+ ip = parsedIP.String()
+ } else {
+ domain = target
+ }
+ } else if len(forwarderComponents) == 2 {
+ domain = strings.TrimSpace(forwarderComponents[0])
+ ip = strings.TrimSpace(forwarderComponents[1])
+ } else {
+ return fmt.Errorf("Error parsing forwarder '%s'", forwarders.(string))
+ }
- dni.DHCP = &libvirtxml.NetworkDHCP{
- Ranges: []libvirtxml.NetworkDHCPRange{
- {
- Start: start.String(),
- End: end.String(),
- },
- },
+ parsedIP := net.ParseIP(ip)
+ if parsedIP == nil {
+ return fmt.Errorf("Could not parse address in forwarder specification '%s'", forwarderSpec)
+ }
+
+ dns.Forwarders = append(dns.Forwarders, libvirtxml.NetworkDNSForwarder{Addr: parsedIP.String(), Domain: domain})
}
- ipsPtrsLst = append(ipsPtrsLst, dni)
}
- networkDef.IPs = ipsPtrsLst
- }
- if dnsForwardCount, ok := d.GetOk("dns_forwarder.#"); ok {
- dns := libvirtxml.NetworkDNS{
- Forwarders: []libvirtxml.NetworkDNSForwarder{},
- }
+ if _, ok := d.GetOk(dnsPrefix + ".host"); ok {
+ dns.Host = &libvirtxml.NetworkDNSHost{}
+ hostPrefix := dnsPrefix + ".host"
- for i := 0; i < dnsForwardCount.(int); i++ {
- forward := libvirtxml.NetworkDNSForwarder{}
- forwardPrefix := fmt.Sprintf("dns_forwarder.%d", i)
- if address, ok := d.GetOk(forwardPrefix + ".address"); ok {
- ip := net.ParseIP(address.(string))
- if ip == nil {
- return fmt.Errorf("Could not parse address '%s'", address)
+ if address, ok := d.GetOk(hostPrefix + ".address"); ok {
+ parsedIP := net.ParseIP(address.(string))
+ if parsedIP == nil {
+ return fmt.Errorf("Could not parse IP address '%s'", parsedIP)
}
- forward.Addr = ip.String()
+ dns.Host.IP = parsedIP.String()
}
- if domain, ok := d.GetOk(forwardPrefix + ".domain"); ok {
- forward.Domain = domain.(string)
+
+ if dnsHostCount, ok := d.GetOk(hostPrefix + ".name.#"); ok {
+ dns.Host.Hostnames = []libvirtxml.NetworkDNSHostHostname{}
+
+ for i := 0; i < dnsHostCount.(int); i++ {
+ dnsHostNamePrefix := fmt.Sprintf(dnsPrefix+".name.%d", i)
+ if name, ok := d.GetOk(dnsHostNamePrefix); ok {
+ hostname := libvirtxml.NetworkDNSHostHostname{Hostname: name.(string)}
+ dns.Host.Hostnames = append(dns.Host.Hostnames, hostname)
+ }
+ }
}
- dns.Forwarders = append(dns.Forwarders, forward)
}
- networkDef.DNS = &dns
}
} else if networkDef.Forward.Mode == netModeBridge {
@@ -368,6 +484,7 @@ func resourceLibvirtNetworkRead(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("Error reading network autostart setting: %s", err)
}
d.Set("autostart", autostart)
+
addresses := []string{}
for _, address := range networkDef.IPs {
// we get the host interface IP (ie, 10.10.8.1) but we want the network CIDR (ie, 10.10.8.0/24)
@@ -390,7 +507,10 @@ func resourceLibvirtNetworkRead(d *schema.ResourceData, meta interface{}) error
d.Set("addresses", addresses)
}
- // TODO: get any other parameters from the network and save them
+ // TODO: get any other parameters from the network and save them (ie, DNS forwarders...)
+
+ d.Set("dns.0.local_only", networkDef.Domain != nil && strings.ToLower(networkDef.Domain.LocalOnly) == "yes")
+ d.Set("dns.0.enabled", networkDef.DNS != nil && strings.ToLower(networkDef.DNS.Enable) == "yes")
log.Printf("[DEBUG] Network ID %s successfully read", d.Id())
return nil
@@ -444,6 +564,50 @@ func resourceLibvirtNetworkDelete(d *schema.ResourceData, meta interface{}) erro
return nil
}
+func setNetworkIP(address string) (*libvirtxml.NetworkIP, *libvirtxml.NetworkDHCP, error) {
+ _, ipNet, err := net.ParseCIDR(address)
+ if err != nil {
+ return nil, nil, fmt.Errorf("Error parsing addresses definition '%s': %s", address, err)
+ }
+ ones, bits := ipNet.Mask.Size()
+ family := "ipv4"
+ if bits == (net.IPv6len * 8) {
+ family = "ipv6"
+ }
+ ipsRange := 2 ^ bits - 2 ^ ones
+ if ipsRange < 4 {
+ return nil, nil, fmt.Errorf("Netmask seems to be too strict: only %d IPs available (%s)", ipsRange-3, family)
+ }
+
+ // we should calculate the range served by DHCP. For example, for
+ // 192.168.121.0/24 we will serve 192.168.121.2 - 192.168.121.254
+ start, end := networkRange(ipNet)
+
+ // skip the .0, (for the network),
+ start[len(start)-1]++
+
+ // assign the .1 to the host interface
+ dni := &libvirtxml.NetworkIP{
+ Address: start.String(),
+ Prefix: strconv.Itoa(ones),
+ Family: family,
+ }
+
+ start[len(start)-1]++ // then skip the .1
+ end[len(end)-1]-- // and skip the .255 (for broadcast)
+
+ dhcp := &libvirtxml.NetworkDHCP{
+ Ranges: []libvirtxml.NetworkDHCPRange{
+ {
+ Start: start.String(),
+ End: end.String(),
+ },
+ },
+ }
+
+ return dni, dhcp, nil
+}
+
func waitForNetworkActive(network libvirt.Network) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
active, err := network.IsActive()
diff --git a/libvirt/resource_libvirt_network_test.go b/libvirt/resource_libvirt_network_test.go
index f3c0d211..eafbc76f 100644
--- a/libvirt/resource_libvirt_network_test.go
+++ b/libvirt/resource_libvirt_network_test.go
@@ -48,6 +48,124 @@ func TestNetworkAutostart(t *testing.T) {
})
}
+func TestNetworkDNS(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckLibvirtNetworkDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(`
+ resource "libvirt_network" "test_net" {
+ name = "networktest"
+ mode = "nat"
+ domain = "k8s.local"
+ addresses = ["10.17.3.0/24"]
+ dns {
+ enabled = true
+ local_only = true
+
+ forwarders = [
+ "8.8.8.8",
+ "my.domain.com -> 10.10.0.67",
+ "hello.com"
+ ]
+
+ host {
+ address = "10.17.3.2"
+ name = ["server1.com", "server2.com"]
+ }
+ }
+ }`),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.enabled", "true"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.local_only", "true"),
+
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.forwarders.#", "3"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.forwarders.0", "8.8.8.8"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.forwarders.1", "my.domain.com -> 10.10.0.67"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.forwarders.2", "hello.com"),
+
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.host.0.address", "10.17.3.2"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.host.0.name.#", "2"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.host.0.name.0", "server1.com"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.host.0.name.1", "server2.com"),
+ ),
+ },
+ },
+ })
+}
+
+func TestNetworkDHCP(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckLibvirtNetworkDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(`
+ resource "libvirt_network" "test_net" {
+ name = "networktest"
+ mode = "nat"
+ domain = "k8s.local"
+ addresses = ["10.17.3.0/24"]
+ dhcp {
+ enabled = false
+ }
+ }`),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "dhcp.0.enabled", "false"),
+ ),
+ },
+ },
+ })
+}
+
+func TestNetworkStaticRoutes(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckLibvirtNetworkDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(`
+ resource "libvirt_network" "test_net" {
+ name = "networktest"
+ mode = "nat"
+ domain = "k8s.local"
+ addresses = ["10.17.3.0/24"]
+ routes = [
+ "192.168.7.0/24 -> 127.0.0.1",
+ "192.168.9.1/24 -> 127.0.0.1",
+ "192.168.17.1/32 -> 127.0.0.1",
+ "2001:db9:4:1::/64 -> ::1"
+ ]
+ }`),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.0.address", "192.168.7.0"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.0.prefix", "24"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.0.family", "ipv4"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.0.gateway", "127.0.0.1"),
+
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.1.address", "192.168.9.0"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.1.prefix", "24"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.1.family", "ipv4"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.1.gateway", "127.0.0.1"),
+
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.2.address", "192.168.17.1"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.2.prefix", "32"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.2.family", "ipv4"),
+ resource.TestCheckResourceAttr("libvirt_network.test_net", "route.2.gateway", "127.0.0.1"),
+ ),
+ },
+ },
+ })
+}
+
+/*************************
+ * tests helpers
+ ************************/
+
func networkExists(n string, network *libvirt.Network) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
diff --git a/libvirt/utils_net.go b/libvirt/utils_net.go
index 140c4a8d..0f57a3fa 100644
--- a/libvirt/utils_net.go
+++ b/libvirt/utils_net.go
@@ -16,7 +16,7 @@ const (
)
// RandomMACAddress returns a randomized MAC address
-func RandomMACAddress() (string, error) {
+func randomMACAddress() (string, error) {
buf := make([]byte, 6)
_, err := rand.Read(buf)
if err != nil {
@@ -38,7 +38,7 @@ func RandomMACAddress() (string, error) {
}
// RandomPort returns a random port
-func RandomPort() int {
+func randomPort() int {
const minPort = 1024
const maxPort = 65535
@@ -47,7 +47,7 @@ func RandomPort() int {
}
// FreeNetworkInterface returns a free network interface
-func FreeNetworkInterface(basename string) (string, error) {
+func freeNetworkInterface(basename string) (string, error) {
for i := 0; i < maxIfaceNum; i++ {
ifaceName := fmt.Sprintf("%s%d", basename, i)
_, err := net.InterfaceByName(ifaceName)
@@ -59,7 +59,7 @@ func FreeNetworkInterface(basename string) (string, error) {
}
// NetworkRange calculates the first and last IP addresses in an IPNet
-func NetworkRange(network *net.IPNet) (net.IP, net.IP) {
+func networkRange(network *net.IPNet) (net.IP, net.IP) {
netIP := network.IP.To4()
lastIP := net.IPv4(0, 0, 0, 0).To4()
if netIP == nil {
@@ -89,7 +89,7 @@ func (fws *fileWebServer) Start() error {
}
fws.Dir = dir
- fws.Port = RandomPort()
+ fws.Port = randomPort()
fws.URL = fmt.Sprintf("http://127.0.0.1:%d", fws.Port)
handler := http.NewServeMux()
diff --git a/libvirt/utils_net_test.go b/libvirt/utils_net_test.go
index 036b3616..c84c94de 100644
--- a/libvirt/utils_net_test.go
+++ b/libvirt/utils_net_test.go
@@ -6,7 +6,7 @@ import (
)
func TestRandomMACAddress(t *testing.T) {
- mac, err := RandomMACAddress()
+ mac, err := randomMACAddress()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
diff --git a/libvirt/utils_test.go b/libvirt/utils_test.go
index f2cd9aaf..ad0781ec 100644
--- a/libvirt/utils_test.go
+++ b/libvirt/utils_test.go
@@ -29,7 +29,7 @@ func TestIPsRange(t *testing.T) {
t.Errorf("When parsing network: %s", err)
}
- start, end := NetworkRange(net)
+ start, end := networkRange(net)
if start.String() != "192.168.18.0" {
t.Errorf("unexpected range start for '%s': %s", net, start)
}