initial checkin of kernel commandline cloud-config url support

This commit is contained in:
Scott Moser 2012-04-04 21:35:32 -04:00
parent 55f58896d0
commit ceeedf3a03
5 changed files with 171 additions and 3 deletions

View File

@ -137,7 +137,27 @@ class CloudInit:
if ds_deps != None:
self.ds_deps = ds_deps
self.sysconfig = sysconfig
if DataSource.DEP_NETWORK in self.ds_deps:
target = "%s.d/%s" % (self.sysconfig, "91_kernel_cmdline_url.cfg")
if os.path.exists(target):
log.debug("cmdline: %s existed" % target)
else:
try:
(key, url, content) = get_cmdline_url()
if key and content:
util.write_file(target, content, mode=0600)
log.debug("cmdline: wrote %s from %s, %s" %
(target, key, url))
elif key:
log.debug("cmdline: %s, %s had no cloud-config" %
(key, url))
except Exception:
util.logexc(log)
log.warn("cmdline: exception occurred while reading")
self.cfg = self.read_cfg()
def read_cfg(self):
@ -639,3 +659,27 @@ class InternalPartHandler:
def handle_part(self, data, ctype, filename, payload, frequency):
return(self.handler(data, ctype, filename, payload, frequency))
def get_cmdline_url(names=('cloud-config-url', 'url'),
starts="#cloud-config", cmdline=None):
if cmdline == None:
cmdline = util.get_cmdline()
data = util.keyval_str_to_dict(cmdline)
url = None
key = None
for key in names:
if key in data:
url = data[key]
break
if url == None:
return (None, None, None)
contents = util.readurl(url)
if contents.startswith(starts):
return (key, url, contents)
return (key, url, None)

View File

@ -840,3 +840,16 @@ def wait_for_url(urls, max_wait=None, timeout=None,
time.sleep(sleeptime)
return False
def keyval_str_to_dict(kvstring):
ret = {}
for tok in kvstring.split():
try:
(key, val) = tok.split("=", 1)
except ValueError:
key = tok
val = True
ret[key] = val
return(ret)

48
doc/kernel-cmdline.txt Normal file
View File

@ -0,0 +1,48 @@
In order to allow an ephemeral, or otherwise pristine image to
receive some configuration, cloud-init will read a url directed by
the kernel command line and proceed as if its data had previously existed.
This allows for configuring a meta-data service, or some other data.
Note, that usage of the kernel command line is somewhat of a last resort,
as it requires knowing in advance the correct command line or modifying
the boot loader to append data.
For example, when 'cloud-init start' runs, it will check to
see if if one of 'cloud-config-url' or 'url' appear in key/value fashion
in the kernel command line as in:
root=/dev/sda ro url=http://foo.bar.zee/abcde
Cloud-init will then read the contents of the given url.
If the content starts with '#cloud-config', it will store
that data to the local filesystem in a static filename
'/etc/cloud/cloud.cfg.d/91_kernel_cmdline_url.cfg', and consider it as
part of the config from that point forward.
If that file exists already, it will not be overwritten, and the url parameters
completely ignored.
Then, when the DataSource runs, it will find that config already available.
So, in able to configure the MAAS DataSource by controlling the kernel
command line from outside the image, you can append:
url=http://your.url.here/abcdefg
or
cloud-config-url=http://your.url.here/abcdefg
Then, have the following content at that url:
#cloud-config
datasource:
MAAS:
metadata_url: http://mass-host.localdomain/source
consumer_key: Xh234sdkljf
token_key: kjfhgb3n
token_secret: 24uysdfx1w4
Notes:
* Because 'url=' is so very generic, in order to avoid false positives,
cloud-init requires the content to start with '#cloud-config' in order
for it to be considered.
* The url= is un-authed http GET, and contains credentials
It could be set up to be randomly generated and also check source
address in order to be more secure

View File

@ -2,8 +2,8 @@ from mocker import MockerTestCase, ANY, ARGS, KWARGS
import os
from cloudinit import (partwalker_handle_handler, handler_handle_part,
handler_register)
from cloudinit.util import write_file, logexc
handler_register, get_cmdline_url)
from cloudinit.util import write_file, logexc, readurl
class TestPartwalkerHandleHandler(MockerTestCase):
@ -193,3 +193,50 @@ class TestHandlerHandlePart(MockerTestCase):
handler_handle_part(mod_mock, self.data, self.ctype, self.filename,
self.payload, self.frequency)
class TestCmdlineUrl(MockerTestCase):
def test_invalid_content(self):
url = "http://example.com/foo"
key = "mykey"
payload = "0"
cmdline = "ro %s=%s bar=1" % (key, url)
mock_readurl = self.mocker.replace(readurl, passthrough=False)
mock_readurl(url)
self.mocker.result(payload)
self.mocker.replay()
self.assertEqual((key, url, None),
get_cmdline_url(names=[key], starts="xxxxxx", cmdline=cmdline))
def test_valid_content(self):
url = "http://example.com/foo"
key = "mykey"
payload = "xcloud-config\nmydata: foo\nbar: wark\n"
cmdline = "ro %s=%s bar=1" % (key, url)
mock_readurl = self.mocker.replace(readurl, passthrough=False)
mock_readurl(url)
self.mocker.result(payload)
self.mocker.replay()
self.assertEqual((key, url, payload),
get_cmdline_url(names=[key], starts="xcloud-config",
cmdline=cmdline))
def test_no_key_found(self):
url = "http://example.com/foo"
key = "mykey"
cmdline = "ro %s=%s bar=1" % (key, url)
self.mocker.replace(readurl, passthrough=False)
self.mocker.replay()
self.assertEqual((None, None, None),
get_cmdline_url(names=["does-not-appear"],
starts="#cloud-config", cmdline=cmdline))
# vi: ts=4 expandtab

View File

@ -6,7 +6,8 @@ import os
import stat
from cloudinit.util import (mergedict, get_cfg_option_list_or_str, write_file,
delete_dir_contents)
delete_dir_contents, get_cmdline,
keyval_str_to_dict)
class TestMergeDict(TestCase):
@ -248,3 +249,18 @@ class TestDeleteDirContents(TestCase):
delete_dir_contents(self.tmp)
self.assertDirEmpty(self.tmp)
class TestKeyValStrings(TestCase):
def test_keyval_str_to_dict(self):
expected = {'1': 'one', '2': 'one+one', 'ro': True}
cmdline = "1=one ro 2=one+one"
self.assertEqual(expected, keyval_str_to_dict(cmdline))
class TestGetCmdline(TestCase):
def test_cmdline_reads_debug_env(self):
os.environ['DEBUG_PROC_CMDLINE'] = 'abcd 123'
self.assertEqual(os.environ['DEBUG_PROC_CMDLINE'], get_cmdline())
# vi: ts=4 expandtab