| # -*- coding: utf-8 -*- |
| |
| import json |
| import logging |
| import os |
| import shutil |
| import six |
| import tempfile |
| import yaml |
| |
| from oslo_utils import uuidutils |
| from oslo_utils import fileutils |
| from oslo_concurrency import processutils |
| |
| class ConfigDriveBuilder(object): |
| """Build config drives, optionally as a context manager.""" |
| |
| def __init__(self, image_file): |
| self.image_file = image_file |
| self.mdfiles=[] |
| |
| def __enter__(self): |
| fileutils.delete_if_exists(self.image_file) |
| return self |
| |
| def __exit__(self, exctype, excval, exctb): |
| self.make_drive() |
| |
| def add_file(self, path, data): |
| self.mdfiles.append((path, data)) |
| |
| def _add_file(self, basedir, path, data): |
| filepath = os.path.join(basedir, path) |
| dirname = os.path.dirname(filepath) |
| fileutils.ensure_tree(dirname) |
| with open(filepath, 'wb') as f: |
| if isinstance(data, six.text_type): |
| data = data.encode('utf-8') |
| f.write(data) |
| |
| def _write_md_files(self, basedir): |
| for data in self.mdfiles: |
| self._add_file(basedir, data[0], data[1]) |
| |
| def _make_iso9660(self, path, tmpdir): |
| |
| processutils.execute('mkisofs', |
| '-o', path, |
| '-ldots', |
| '-allow-lowercase', |
| '-allow-multidot', |
| '-l', |
| '-V', 'config-2', |
| '-r', |
| '-J', |
| '-quiet', |
| tmpdir, |
| attempts=1, |
| run_as_root=False) |
| |
| def make_drive(self): |
| """Make the config drive. |
| :raises ProcessExecuteError if a helper process has failed. |
| """ |
| try: |
| tmpdir = tempfile.mkdtemp() |
| self._write_md_files(tmpdir) |
| self._make_iso9660(self.image_file, tmpdir) |
| finally: |
| shutil.rmtree(tmpdir) |
| |
| |
| def generate( |
| dst, |
| hostname, |
| domainname, |
| instance_id=None, |
| user_data=None, |
| network_data=None, |
| saltconfig=None |
| ): |
| |
| ''' Generate config drive |
| |
| :param dst: destination file to place config drive. |
| :param hostname: hostname of Instance. |
| :param domainname: instance domain. |
| :param instance_id: UUID of the instance. |
| :param user_data: custom user data dictionary. type: json |
| :param network_data: custom network info dictionary. type: json |
| :param saltconfig: salt minion configuration. type: json |
| |
| ''' |
| |
| instance_md = {} |
| instance_md['uuid'] = instance_id or uuidutils.generate_uuid() |
| instance_md['hostname'] = '%s.%s' % (hostname, domainname) |
| instance_md['name'] = hostname |
| |
| if user_data: |
| user_data = '#cloud-config\n\n' + yaml.dump(yaml.load(user_data), default_flow_style=False) |
| if saltconfig: |
| user_data += yaml.dump(yaml.load(str(saltconfig)), default_flow_style=False) |
| |
| data = json.dumps(instance_md) |
| |
| with ConfigDriveBuilder(dst) as cfgdrive: |
| cfgdrive.add_file('openstack/latest/meta_data.json', data) |
| if user_data: |
| cfgdrive.add_file('openstack/latest/user_data', user_data) |
| if network_data: |
| cfgdrive.add_file('openstack/latest/network_data.json', network_data) |
| cfgdrive.add_file('openstack/latest/vendor_data.json', '{}') |
| cfgdrive.add_file('openstack/latest/vendor_data2.json', '{}') |