Commit 763be4ec authored by Scott Moser's avatar Scott Moser

lxd: adds basic support for dpkg based lxd-bridge configuration.

It exposes the most useful debconf keys as cloud-init configuration keys.
parents 822ac18b 860d98bc
......@@ -30,6 +30,19 @@ Example config:
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
......@@ -46,22 +59,24 @@ def handle(name, cfg, cloud, log, args):
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 +88,89 @@ def handle(name, cfg, cloud, log, args):
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
dconf_comm = "debconf-communicate"
if bridge_cfg and util.which(dconf_comm):
debconf = bridge_to_debconf(bridge_cfg)
# Update debconf database
try:
log.debug("Setting lxd debconf via " + dconf_comm)
data = "\n".join(["set %s %s" % (k, v)
for k, v in debconf.items()]) + "\n"
util.subp(['debconf-communicate'], data)
except:
util.logexc(log, "Failed to run '%s' for lxd with" % dconf_comm)
# Remove the existing configuration file (forces re-generation)
util.del_file("/etc/default/lxd-bridge")
# Run reconfigure
log.debug("Running dpkg-reconfigure for lxd")
util.subp(['dpkg-reconfigure', 'lxd',
'--frontend=noninteractive'])
elif bridge_cfg:
raise RuntimeError(
"Unable to configure lxd bridge without %s." + dconf_comm)
def bridge_to_debconf(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"
if bridge_cfg.get("name"):
debconf["lxd/bridge-name"] = bridge_cfg.get("name")
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")
if bridge_cfg.get("domain"):
debconf["lxd/bridge-domain"] = bridge_cfg.get("domain")
else:
raise Exception("invalid bridge mode \"%s\"" % bridge_cfg.get("mode"))
return debconf
......@@ -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 @@ lxd:
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
......
......@@ -73,3 +73,62 @@ class TestLxd(t_help.TestCase):
cc_lxd.handle('cc_lxd', {'package_update': True}, cc, LOG, [])
self.assertFalse(cc.distro.install_packages.called)
self.assertFalse(mock_util.subp.called)
def test_lxd_debconf_new_full(self):
data = {"mode": "new",
"name": "testbr0",
"ipv4_address": "10.0.8.1",
"ipv4_netmask": "24",
"ipv4_dhcp_first": "10.0.8.2",
"ipv4_dhcp_last": "10.0.8.254",
"ipv4_dhcp_leases": "250",
"ipv4_nat": "true",
"ipv6_address": "fd98:9e0:3744::1",
"ipv6_netmask": "64",
"ipv6_nat": "true",
"domain": "lxd"}
self.assertEquals(
cc_lxd.bridge_to_debconf(data),
{"lxd/setup-bridge": "true",
"lxd/bridge-name": "testbr0",
"lxd/bridge-ipv4": "true",
"lxd/bridge-ipv4-address": "10.0.8.1",
"lxd/bridge-ipv4-netmask": "24",
"lxd/bridge-ipv4-dhcp-first": "10.0.8.2",
"lxd/bridge-ipv4-dhcp-last": "10.0.8.254",
"lxd/bridge-ipv4-dhcp-leases": "250",
"lxd/bridge-ipv4-nat": "true",
"lxd/bridge-ipv6": "true",
"lxd/bridge-ipv6-address": "fd98:9e0:3744::1",
"lxd/bridge-ipv6-netmask": "64",
"lxd/bridge-ipv6-nat": "true",
"lxd/bridge-domain": "lxd"})
def test_lxd_debconf_new_partial(self):
data = {"mode": "new",
"ipv6_address": "fd98:9e0:3744::1",
"ipv6_netmask": "64",
"ipv6_nat": "true"}
self.assertEquals(
cc_lxd.bridge_to_debconf(data),
{"lxd/setup-bridge": "true",
"lxd/bridge-ipv6": "true",
"lxd/bridge-ipv6-address": "fd98:9e0:3744::1",
"lxd/bridge-ipv6-netmask": "64",
"lxd/bridge-ipv6-nat": "true"})
def test_lxd_debconf_existing(self):
data = {"mode": "existing",
"name": "testbr0"}
self.assertEquals(
cc_lxd.bridge_to_debconf(data),
{"lxd/setup-bridge": "false",
"lxd/use-existing-bridge": "true",
"lxd/bridge-name": "testbr0"})
def test_lxd_debconf_none(self):
data = {"mode": "none"}
self.assertEquals(
cc_lxd.bridge_to_debconf(data),
{"lxd/setup-bridge": "false",
"lxd/bridge-name": ""})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment