Add metalsmith_instances instance option config_drive
This has sub-options ``cloud_config` and ``meta_data`` and allows customization of data packaged in the config-drive to be consumed by cloud-init. This is needed by TripleO to provide an equivalent to the current heat/nova based firstboot customization support. Change-Id: Ie384292a3310cb06e908dd9027e9300ca108e7dd
This commit is contained in:
parent
1c06881c32
commit
22a4e4071c
@ -35,11 +35,16 @@ class GenericConfig(object):
|
||||
|
||||
:ivar ssh_keys: List of SSH public keys.
|
||||
:ivar user_data: User data as a string.
|
||||
:ivar meta_data: Dict of data to add to the generated ``meta_data``
|
||||
"""
|
||||
|
||||
def __init__(self, ssh_keys=None, user_data=None):
|
||||
def __init__(self, ssh_keys=None, user_data=None, meta_data=None):
|
||||
self.ssh_keys = ssh_keys or []
|
||||
self.user_data = user_data
|
||||
if meta_data and not isinstance(meta_data, dict):
|
||||
raise TypeError('Custom meta_data must be a dictionary, '
|
||||
'got %r' % meta_data)
|
||||
self.meta_data = meta_data
|
||||
|
||||
def generate(self, node, hostname=None):
|
||||
"""Generate the config drive information.
|
||||
@ -62,17 +67,24 @@ class GenericConfig(object):
|
||||
else:
|
||||
ssh_keys = self.ssh_keys
|
||||
|
||||
metadata = {'public_keys': ssh_keys,
|
||||
'uuid': node.id,
|
||||
'name': node.name,
|
||||
'hostname': hostname,
|
||||
'launch_index': 0,
|
||||
'availability_zone': '',
|
||||
'files': [],
|
||||
'meta': {}}
|
||||
meta_data = {}
|
||||
if self.meta_data:
|
||||
meta_data.update(self.meta_data)
|
||||
|
||||
meta_data.update({
|
||||
'public_keys': ssh_keys,
|
||||
'uuid': node.id,
|
||||
'name': node.name,
|
||||
'hostname': hostname
|
||||
})
|
||||
meta_data.setdefault('launch_index', 0)
|
||||
meta_data.setdefault('availability_zone', '')
|
||||
meta_data.setdefault('files', [])
|
||||
meta_data.setdefault('meta', {})
|
||||
|
||||
user_data = self.populate_user_data()
|
||||
|
||||
return {'meta_data': metadata,
|
||||
return {'meta_data': meta_data,
|
||||
'user_data': user_data}
|
||||
|
||||
def populate_user_data(self):
|
||||
@ -91,15 +103,16 @@ class CloudInitConfig(GenericConfig):
|
||||
Compared to :class:`GenericConfig`, this adds support for managing users.
|
||||
|
||||
:ivar ssh_keys: List of SSH public keys.
|
||||
:ivar user_data: Cloud-init script as a dictionary.
|
||||
:ivar users: Users to add on first boot.
|
||||
:ivar user_data: Cloud-init cloud-config data as a dictionary.
|
||||
:ivar meta_data: Dict of data to add to the generated ``meta_data``
|
||||
"""
|
||||
|
||||
def __init__(self, ssh_keys=None, user_data=None):
|
||||
def __init__(self, ssh_keys=None, user_data=None, meta_data=None):
|
||||
if user_data is not None and not isinstance(user_data, dict):
|
||||
raise TypeError('Custom user data must be a dictionary for '
|
||||
'CloudInitConfig, got %r' % user_data)
|
||||
super(CloudInitConfig, self).__init__(ssh_keys, user_data or {})
|
||||
super(CloudInitConfig, self).__init__(ssh_keys, user_data or {},
|
||||
meta_data=meta_data)
|
||||
self.users = []
|
||||
|
||||
def add_user(self, name, admin=True, password_hash=None, sudo=False,
|
||||
|
@ -78,6 +78,13 @@ class TestGenericConfig(unittest.TestCase):
|
||||
config = self.CLASS(user_data='{"answer": 42}')
|
||||
self._check(config, {}, {"answer": 42}, cloud_init=False)
|
||||
|
||||
def test_custom_metadata(self):
|
||||
config = self.CLASS(meta_data={"foo": "bar"})
|
||||
self._check(config, {"foo": "bar"}, cloud_init=False)
|
||||
|
||||
def test_custom_metadata_not_dict(self):
|
||||
self.assertRaises(TypeError, self.CLASS, meta_data="foobar")
|
||||
|
||||
|
||||
class TestCloudInitConfig(TestGenericConfig):
|
||||
CLASS = instance_config.CloudInitConfig
|
||||
|
@ -126,7 +126,11 @@ class TestMetalsmithInstances(unittest.TestCase):
|
||||
'netboot': True,
|
||||
'ssh_public_keys': 'abcd',
|
||||
'user_name': 'centos',
|
||||
'passwordless_sudo': False
|
||||
'passwordless_sudo': False,
|
||||
'config_drive': {
|
||||
'meta_data': {'foo': 'bar'},
|
||||
'cloud_config': {'bootcmd': ['echo henlo world']}
|
||||
}
|
||||
}, {
|
||||
'name': 'node-3',
|
||||
'hostname': 'overcloud-controller-3',
|
||||
@ -191,8 +195,10 @@ class TestMetalsmithInstances(unittest.TestCase):
|
||||
),
|
||||
])
|
||||
mock_config.assert_has_calls([
|
||||
mock.call(ssh_keys=None),
|
||||
mock.call(ssh_keys='abcd'),
|
||||
mock.call(ssh_keys=None, user_data=None, meta_data=None),
|
||||
mock.call(ssh_keys='abcd',
|
||||
user_data={'bootcmd': ['echo henlo world']},
|
||||
meta_data={'foo': 'bar'})
|
||||
])
|
||||
config.add_user.assert_called_once_with(
|
||||
'centos', admin=True, sudo=False)
|
||||
|
@ -73,6 +73,7 @@ def transform(module, instances, defaults):
|
||||
value(src, 'image_checksum', image, 'checksum')
|
||||
value(src, 'image_kernel', image, 'kernel')
|
||||
value(src, 'image_ramdisk', image, 'ramdisk')
|
||||
value(src, 'config_drive', dest)
|
||||
|
||||
# keys in metalsmith_instances not currently in metalsmith_deployment:
|
||||
# passwordless_sudo
|
||||
|
@ -151,6 +151,23 @@ options:
|
||||
- Allow password-less sudo for the user
|
||||
default: yes
|
||||
type: bool
|
||||
config_drive:
|
||||
description:
|
||||
- Extra data to add to the boot config-drive cloud-init
|
||||
type: dict
|
||||
suboptions:
|
||||
cloud_config:
|
||||
description:
|
||||
- Dict of cloud-init cloud-config data for tasks to run on node
|
||||
boot. The 'users' directive can be used to configure extra
|
||||
users other than the 'user_name' admin user.
|
||||
type: dict
|
||||
meta_data:
|
||||
description:
|
||||
- Extra metadata to include with the config-drive cloud-init
|
||||
metadata. This will be added to the generated metadata
|
||||
'public_keys', 'uuid', 'name', and 'hostname'.
|
||||
type: dict
|
||||
clean_up:
|
||||
description:
|
||||
- Clean up resources on failure
|
||||
@ -330,7 +347,12 @@ def _provision_instance(provisioner, instance, nodes, timeout, wait):
|
||||
|
||||
image = _get_source(instance)
|
||||
ssh_keys = instance.get('ssh_public_keys')
|
||||
config = instance_config.CloudInitConfig(ssh_keys=ssh_keys)
|
||||
config_drive = instance.get('config_drive', {})
|
||||
cloud_config = config_drive.get('cloud_config')
|
||||
meta_data = config_drive.get('meta_data')
|
||||
config = instance_config.CloudInitConfig(ssh_keys=ssh_keys,
|
||||
user_data=cloud_config,
|
||||
meta_data=meta_data)
|
||||
if instance.get('user_name'):
|
||||
config.add_user(instance.get('user_name'), admin=True,
|
||||
sudo=instance.get('passwordless_sudo', True))
|
||||
|
@ -60,6 +60,18 @@ Each instances has the following attributes:
|
||||
list of nodes (UUIDs or names) to be considered for deployment.
|
||||
``capabilities`` (defaults to ``metalsmith_capabilities``)
|
||||
node capabilities to request when scheduling.
|
||||
``config_drive``
|
||||
extra data to add to the boot config-drive cloud-init:
|
||||
|
||||
``cloud_config``
|
||||
Dict of cloud-init cloud-config data for tasks to run on node
|
||||
boot. The 'users' directive can be used to configure extra
|
||||
users other than the 'user_name' admin user.
|
||||
``meta_data``
|
||||
Extra metadata to include with the config-drive cloud-init
|
||||
metadata. This will be added to the generated metadata
|
||||
``public_keys``, ``uuid``, ``name``, and ``hostname``.
|
||||
|
||||
``conductor_group`` (defaults to ``metalsmith_conductor_group``)
|
||||
conductor group to pick nodes from.
|
||||
|
||||
|
7
releasenotes/notes/config_drive-f59bb5d8e684b2ef.yaml
Normal file
7
releasenotes/notes/config_drive-f59bb5d8e684b2ef.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add new metalsmith_instances instance option ``config_drive``.
|
||||
This has sub-options ``cloud_config` and ``meta_data`` and allows
|
||||
customization of data packaged in the config-drive to be consumed by
|
||||
cloud-init.
|
Loading…
x
Reference in New Issue
Block a user