← Back to team overview

cloud-init-dev team mailing list archive

[Merge] lp:~stgraber/cloud-init/lxd-bridge into lp:cloud-init

 

Stéphane Graber has proposed merging lp:~stgraber/cloud-init/lxd-bridge into lp:cloud-init.

Requested reviews:
  cloud init development team (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~stgraber/cloud-init/lxd-bridge/+merge/291475

This adds basic support for lxd-bridge configuration.

It exposes the most useful debconf keys as cloud-init configuration keys.

I've succesfuly tested all 3 modes with a locally patched-up cloud-init
and everything looks fine here.
-- 
Your team cloud init development team is requested to review the proposed merge of lp:~stgraber/cloud-init/lxd-bridge into lp:cloud-init.
=== modified file 'cloudinit/config/cc_lxd.py'
--- cloudinit/config/cc_lxd.py	2016-03-03 23:16:13 +0000
+++ cloudinit/config/cc_lxd.py	2016-04-11 04:11:12 +0000
@@ -30,9 +30,23 @@
       storage_create_loop: <size>
       storage_pool: <name>
       trust_password: <password>
+    bridge:
+      mode: <new, existing or none>
+      name: <name>
+      ipv4_address: <ip addr>
+      ipv4_netmask: <cidr>
+      ipv4_dhcp_first: <ip addr>
+      ipv4_dhcp_last: <ip addr>
+      ipv4_dhcp_leases: <size>
+      ipv4_nat: <bool>
+      ipv6_address: <ip addr>
+      ipv6_netmask: <cidr>
+      ipv6_nat: <bool>
+      domain: <domain>
 """
 
 from cloudinit import util
+import os
 
 
 def handle(name, cfg, cloud, log, args):
@@ -46,22 +60,24 @@
                  type(lxd_cfg))
         return
 
+    # Grab the configuration
     init_cfg = lxd_cfg.get('init')
     if not isinstance(init_cfg, dict):
         log.warn("lxd/init config must be a dictionary. found a '%s'",
                  type(init_cfg))
         init_cfg = {}
 
-    if not init_cfg:
-        log.debug("no lxd/init config. disabled.")
-        return
+    bridge_cfg = lxd_cfg.get('bridge')
+    if not isinstance(bridge_cfg, dict):
+        log.warn("lxd/bridge config must be a dictionary. found a '%s'",
+                 type(bridge_cfg))
+        bridge_cfg = {}
 
+    # Install the needed packages
     packages = []
-    # Ensure lxd is installed
     if not util.which("lxd"):
         packages.append('lxd')
 
-    # if using zfs, get the utils
     if init_cfg.get("storage_backend") == "zfs" and not util.which('zfs'):
         packages.append('zfs')
 
@@ -73,13 +89,78 @@
             return
 
     # Set up lxd if init config is given
-    init_keys = (
-        'network_address', 'network_port', 'storage_backend',
-        'storage_create_device', 'storage_create_loop',
-        'storage_pool', 'trust_password')
-    cmd = ['lxd', 'init', '--auto']
-    for k in init_keys:
-        if init_cfg.get(k):
-            cmd.extend(["--%s=%s" %
-                        (k.replace('_', '-'), str(init_cfg[k]))])
-    util.subp(cmd)
+    if init_cfg:
+        init_keys = (
+            'network_address', 'network_port', 'storage_backend',
+            'storage_create_device', 'storage_create_loop',
+            'storage_pool', 'trust_password')
+        cmd = ['lxd', 'init', '--auto']
+        for k in init_keys:
+            if init_cfg.get(k):
+                cmd.extend(["--%s=%s" %
+                            (k.replace('_', '-'), str(init_cfg[k]))])
+        util.subp(cmd)
+
+    # Set up lxd-bridge if bridge config is given
+    if bridge_cfg:
+        debconf = {}
+
+        if bridge_cfg.get("mode") == "none":
+            debconf["lxd/setup-bridge"] = "false"
+            debconf["lxd/bridge-name"] = ""
+
+        elif bridge_cfg.get("mode") == "existing":
+            debconf["lxd/setup-bridge"] = "false"
+            debconf["lxd/use-existing-bridge"] = "true"
+            debconf["lxd/bridge-name"] = bridge_cfg.get("name")
+
+        elif bridge_cfg.get("mode") == "new":
+            debconf["lxd/setup-bridge"] = "true"
+            debconf["lxd/bridge-name"] = bridge_cfg.get("name", "lxdbr0")
+            if bridge_cfg.get("ipv4_address"):
+                debconf["lxd/bridge-ipv4"] = "true"
+                debconf["lxd/bridge-ipv4-address"] = \
+                    bridge_cfg.get("ipv4_address")
+                debconf["lxd/bridge-ipv4-netmask"] = \
+                    bridge_cfg.get("ipv4_netmask")
+                debconf["lxd/bridge-ipv4-dhcp-first"] = \
+                    bridge_cfg.get("ipv4_dhcp_first")
+                debconf["lxd/bridge-ipv4-dhcp-last"] = \
+                    bridge_cfg.get("ipv4_dhcp_last")
+                debconf["lxd/bridge-ipv4-dhcp-leases"] = \
+                    bridge_cfg.get("ipv4_dhcp_leases")
+                debconf["lxd/bridge-ipv4-nat"] = \
+                    bridge_cfg.get("ipv4_nat", "true")
+
+            if bridge_cfg.get("ipv6_address"):
+                debconf["lxd/bridge-ipv6"] = "true"
+                debconf["lxd/bridge-ipv6-address"] = \
+                    bridge_cfg.get("ipv6_address")
+                debconf["lxd/bridge-ipv6-netmask"] = \
+                    bridge_cfg.get("ipv6_netmask")
+                debconf["lxd/bridge-ipv6-nat"] = \
+                    bridge_cfg.get("ipv6_nat", "false")
+
+        else:
+            log.warn("invalid bridge mode \"%s\"" % bridge_cfg.get("mode"))
+            return
+
+        # Update debconf database
+        try:
+            log.debug("Setting lxd debconf-set-selections")
+            for k, v in debconf.items():
+                util.subp(['debconf-communicate'], "set %s %s\n" % (k, v))
+        except:
+            util.logexc(log, "Failed to run debconf-communicate for lxd")
+
+        # Remove the existing configuration file (forces re-generation)
+        if os.path.exists("/etc/default/lxd-bridge"):
+            os.remove("/etc/default/lxd-bridge")
+
+        # Run reconfigure
+        try:
+            log.debug("Running dpkg-reconfigure for lxd")
+            util.subp(['dpkg-reconfigure', 'lxd',
+                       '--frontend=noninteractive'])
+        except:
+            util.logexc(log, "Failed to run dpkg-reconfigure for lxd")

=== modified file 'doc/examples/cloud-config-lxd.txt'
--- doc/examples/cloud-config-lxd.txt	2016-03-01 05:19:55 +0000
+++ doc/examples/cloud-config-lxd.txt	2016-04-11 04:11:12 +0000
@@ -12,6 +12,20 @@
 #     storage_create_loop: set up loop based storage with size in GB
 #     storage_pool: name of storage pool to use or create
 #     trust_password: password required to add new clients
+#   bridge: dict of options for the lxd bridge
+#     mode: one of "new", "existing" or "none". Defaults to "new"
+#     name: the name of the bridge. Defaults to "lxdbr0"
+#     ipv4_address: an IPv4 address (e.g. 10.0.8.1)
+#     ipv4_netmask: a CIDR mask value (e.g. 24)
+#     ipv4_dhcp_first: the first IP of the DHCP range (e.g. 10.0.8.2)
+#     ipv4_dhcp_last: the last IP of the DHCP range (e.g. 10.0.8.254)
+#     ipv4_dhcp_leases: the size of the DHCP pool (e.g. 250)
+#     ipv4_nat: either "true" or "false"
+#     ipv6_address: an IPv6 address (e.g. fd98:9e0:3744::1)
+#     ipv6_netmask: a CIDR mask value (e.g. 64)
+#     ipv6_nat: either "true" or "false"
+#     domain: domain name to use for the bridge
+
 
 lxd:
   init:
@@ -20,6 +34,19 @@
     storage_backend: zfs
     storage_pool: datapool
     storage_create_loop: 10
+  bridge:
+    mode: new
+    name: lxdbr0
+    ipv4_address: 10.0.8.1
+    ipv4_netmask: 24
+    ipv4_dhcp_first: 10.0.8.2
+    ipv4_dhcp_last: 10.0.8.3
+    ipv4_dhcp_leases: 250
+    ipv4_nat: true
+    ipv6_address: fd98:9e0:3744::1
+    ipv6_netmask: 64
+    ipv6_nat: true
+    domain: lxd
 
 
 # The simplist working configuration is


Follow ups