Merge lp:~wesley-wiedenmeier/cloud-init/lxd-init into lp:~cloud-init-dev/cloud-init/trunk

Proposed by Scott Moser
Status: Merged
Merged at revision: 1163
Proposed branch: lp:~wesley-wiedenmeier/cloud-init/lxd-init
Merge into: lp:~cloud-init-dev/cloud-init/trunk
Diff against target: 183 lines (+147/-0)
5 files modified
ChangeLog (+1/-0)
cloudinit/config/cc_lxd.py (+66/-0)
config/cloud.cfg (+1/-0)
doc/examples/cloud-config-lxd.txt (+21/-0)
tests/unittests/test_handler/test_handler_lxd.py (+58/-0)
To merge this branch: bzr merge lp:~wesley-wiedenmeier/cloud-init/lxd-init
Reviewer Review Type Date Requested Status
cloud-init Commiters Pending
Review via email: mp+285018@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

Wesley,

Thanks.

Please add a 'doc' in
 doc/examples/cloud-config-lxd.txt

like the other files there.

see inline comments

1159. By Wesley Wiedenmeier

Added example cc_lxd config

1160. By Wesley Wiedenmeier

 - Ensure that lxd is installed before running lxd init.
 - Handle init cfg separately from main cfg to allow multiple sections under lxd
   config to be handled independantly.
 - Check for properly formatted lxd init cfg

1161. By Wesley Wiedenmeier

Use mock in test_handler_lxd.py and add test for lxd installation

1162. By Wesley Wiedenmeier

Merge from trunk and resolve text conflict in ChangeLog

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2016-02-04 22:11:13 +0000
3+++ ChangeLog 2016-02-05 01:21:21 +0000
4@@ -72,6 +72,7 @@
5 - systemd/power_state: fix power_state to work even if cloud-final
6 exited non-zero (LP: #1449318)
7 - SmartOS: Add support for Joyent LX-Brand Zones (LP: #1540965)
8+ - lxd: add support for setting up lxd using 'lxd init' (LP: #1522879)
9 [Robert C Jennings]
10 - systemd: support using systemd-detect-virt to detect container
11 (LP: #1539016) [Martin Pitt]
12
13=== added file 'cloudinit/config/cc_lxd.py'
14--- cloudinit/config/cc_lxd.py 1970-01-01 00:00:00 +0000
15+++ cloudinit/config/cc_lxd.py 2016-02-05 01:21:21 +0000
16@@ -0,0 +1,66 @@
17+# vi: ts=4 expandtab
18+#
19+# Copyright (C) 2016 Canonical Ltd.
20+#
21+# Author: Wesley Wiedenmeier <wesley.wiedenmeier@canonical.com>
22+#
23+# This program is free software: you can redistribute it and/or modify
24+# it under the terms of the GNU General Public License version 3, as
25+# published by the Free Software Foundation.
26+#
27+# This program is distributed in the hope that it will be useful,
28+# but WITHOUT ANY WARRANTY; without even the implied warranty of
29+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30+# GNU General Public License for more details.
31+#
32+# You should have received a copy of the GNU General Public License
33+# along with this program. If not, see <http://www.gnu.org/licenses/>.
34+
35+"""
36+This module initializes lxd using 'lxd init'
37+
38+Example config:
39+ #cloud-config
40+ lxd:
41+ init:
42+ network_address: <ip addr>
43+ network_port: <port>
44+ storage_backend: <zfs/dir>
45+ storage_create_device: <dev>
46+ storage_create_loop: <size>
47+ storage_pool: <name>
48+ trust_password: <password>
49+"""
50+
51+from cloudinit import util
52+
53+
54+def handle(name, cfg, cloud, log, args):
55+ # Get config
56+ lxd_cfg = cfg.get('lxd')
57+ if not lxd_cfg and isinstance(lxd_cfg, dict):
58+ log.debug("Skipping module named %s, not present or disabled by cfg")
59+ return
60+
61+ # Ensure lxd is installed
62+ if not util.which("lxd"):
63+ try:
64+ cloud.distro.install_packages(("lxd",))
65+ except util.ProcessExecutionError as e:
66+ log.warn("no lxd executable and could not install lxd: '%s'" % e)
67+ return
68+
69+ # Set up lxd if init config is given
70+ init_cfg = lxd_cfg.get('init')
71+ if init_cfg:
72+ if not isinstance(init_cfg, dict):
73+ log.warn("lxd init config must be a dict of flag: val pairs")
74+ return
75+ init_keys = ('network_address', 'network_port', 'storage_backend',
76+ 'storage_create_device', 'storage_create_loop',
77+ 'storage_pool', 'trust_password')
78+ cmd = ['lxd', 'init', '--auto']
79+ for k in init_keys:
80+ if init_cfg.get(k):
81+ cmd.extend(["--%s" % k.replace('_', '-'), init_cfg[k]])
82+ util.subp(cmd)
83
84=== modified file 'config/cloud.cfg'
85--- config/cloud.cfg 2015-10-09 16:39:23 +0000
86+++ config/cloud.cfg 2016-02-05 01:21:21 +0000
87@@ -56,6 +56,7 @@
88 - fan
89 - landscape
90 - timezone
91+ - lxd
92 - puppet
93 - chef
94 - salt-minion
95
96=== added file 'doc/examples/cloud-config-lxd.txt'
97--- doc/examples/cloud-config-lxd.txt 1970-01-01 00:00:00 +0000
98+++ doc/examples/cloud-config-lxd.txt 2016-02-05 01:21:21 +0000
99@@ -0,0 +1,21 @@
100+#cloud-config
101+
102+# configure lxd
103+# default: none
104+# all options default to none if not specified
105+# lxd: config sections for lxd
106+# init: dict of options for lxd init, see 'man lxd'
107+# network_address: address for lxd to listen on
108+# network_port: port for lxd to listen on
109+# storage_backend: either 'zfs' or 'dir'
110+# storage_create_device: device based storage using specified device
111+# storage_create_loop: set up loop based storage with size in GB
112+# storage_pool: name of storage pool to use or create
113+# trust_password: password required to add new clients
114+
115+lxd:
116+ init:
117+ network_address: 0.0.0.0
118+ network_port: 8443
119+ storage_backend: zfs
120+ storage_pool: datapool
121
122=== added file 'tests/unittests/test_handler/test_handler_lxd.py'
123--- tests/unittests/test_handler/test_handler_lxd.py 1970-01-01 00:00:00 +0000
124+++ tests/unittests/test_handler/test_handler_lxd.py 2016-02-05 01:21:21 +0000
125@@ -0,0 +1,58 @@
126+from cloudinit.config import cc_lxd
127+from cloudinit import (distros, helpers, cloud)
128+from cloudinit.sources import DataSourceNoCloud
129+from .. import helpers as t_help
130+
131+import logging
132+
133+try:
134+ from unittest import mock
135+except ImportError:
136+ import mock
137+
138+LOG = logging.getLogger(__name__)
139+
140+
141+class TestLxd(t_help.TestCase):
142+ lxd_cfg = {
143+ 'lxd': {
144+ 'init': {
145+ 'network_address': '0.0.0.0',
146+ 'storage_backend': 'zfs',
147+ 'storage_pool': 'poolname',
148+ }
149+ }
150+ }
151+
152+ def setUp(self):
153+ super(TestLxd, self).setUp()
154+
155+ def _get_cloud(self, distro):
156+ cls = distros.fetch(distro)
157+ paths = helpers.Paths({})
158+ d = cls(distro, {}, paths)
159+ ds = DataSourceNoCloud.DataSourceNoCloud({}, d, paths)
160+ cc = cloud.Cloud(ds, paths, {}, d, None)
161+ return cc
162+
163+ @mock.patch("cloudinit.config.cc_lxd.util")
164+ def test_lxd_init(self, mock_util):
165+ cc = self._get_cloud('ubuntu')
166+ mock_util.which.return_value = True
167+ cc_lxd.handle('cc_lxd', self.lxd_cfg, cc, LOG, [])
168+ self.assertTrue(mock_util.which.called)
169+ init_call = mock_util.subp.call_args_list[0][0][0]
170+ self.assertEquals(init_call,
171+ ['lxd', 'init', '--auto', '--network-address',
172+ '0.0.0.0', '--storage-backend', 'zfs',
173+ '--storage-pool', 'poolname'])
174+
175+ @mock.patch("cloudinit.config.cc_lxd.util")
176+ def test_lxd_install(self, mock_util):
177+ cc = self._get_cloud('ubuntu')
178+ cc.distro = mock.MagicMock()
179+ mock_util.which.return_value = None
180+ cc_lxd.handle('cc_lxd', self.lxd_cfg, cc, LOG, [])
181+ self.assertTrue(cc.distro.install_packages.called)
182+ install_pkg = cc.distro.install_packages.call_args_list[0][0][0]
183+ self.assertEquals(install_pkg, ('lxd',))