blob: bc76b77079553102c20cce7604cfcd951e8c07bc [file] [log] [blame]
Dzmitry Stremkouski97927ee2018-08-23 23:20:38 +02001# -*- coding: utf-8 -*-
2
3import json
4import logging
5import os
6import shutil
7import six
8import tempfile
9import yaml
10
11from oslo_utils import uuidutils
12from oslo_utils import fileutils
13from oslo_concurrency import processutils
14
15class ConfigDriveBuilder(object):
16 """Build config drives, optionally as a context manager."""
17
18 def __init__(self, image_file):
19 self.image_file = image_file
20 self.mdfiles=[]
21
22 def __enter__(self):
23 fileutils.delete_if_exists(self.image_file)
24 return self
25
26 def __exit__(self, exctype, excval, exctb):
27 self.make_drive()
28
29 def add_file(self, path, data):
30 self.mdfiles.append((path, data))
31
32 def _add_file(self, basedir, path, data):
33 filepath = os.path.join(basedir, path)
34 dirname = os.path.dirname(filepath)
35 fileutils.ensure_tree(dirname)
36 with open(filepath, 'wb') as f:
37 if isinstance(data, six.text_type):
38 data = data.encode('utf-8')
39 f.write(data)
40
41 def _write_md_files(self, basedir):
42 for data in self.mdfiles:
43 self._add_file(basedir, data[0], data[1])
44
45 def _make_iso9660(self, path, tmpdir):
46
47 processutils.execute('mkisofs',
48 '-o', path,
49 '-ldots',
50 '-allow-lowercase',
51 '-allow-multidot',
52 '-l',
53 '-V', 'config-2',
54 '-r',
55 '-J',
56 '-quiet',
57 tmpdir,
58 attempts=1,
59 run_as_root=False)
60
61 def make_drive(self):
62 """Make the config drive.
63 :raises ProcessExecuteError if a helper process has failed.
64 """
65 try:
66 tmpdir = tempfile.mkdtemp()
67 self._write_md_files(tmpdir)
68 self._make_iso9660(self.image_file, tmpdir)
69 finally:
70 shutil.rmtree(tmpdir)
71
72
73def generate(
74 dst,
75 hostname,
76 domainname,
77 instance_id=None,
78 user_data=None,
79 network_data=None,
80 saltconfig=None
81 ):
82
83 ''' Generate config drive
84
85 :param dst: destination file to place config drive.
86 :param hostname: hostname of Instance.
87 :param domainname: instance domain.
88 :param instance_id: UUID of the instance.
89 :param user_data: custom user data dictionary. type: json
90 :param network_data: custom network info dictionary. type: json
91 :param saltconfig: salt minion configuration. type: json
92
93 '''
94
95 instance_md = {}
96 instance_md['uuid'] = instance_id or uuidutils.generate_uuid()
97 instance_md['hostname'] = '%s.%s' % (hostname, domainname)
98 instance_md['name'] = hostname
99
100 if user_data:
101 user_data = '#cloud-config\n\n' + yaml.dump(yaml.load(user_data), default_flow_style=False)
102 if saltconfig:
103 user_data += yaml.dump(yaml.load(str(saltconfig)), default_flow_style=False)
104
105 data = json.dumps(instance_md)
106
107 with ConfigDriveBuilder(dst) as cfgdrive:
108 cfgdrive.add_file('openstack/latest/meta_data.json', data)
109 if user_data:
110 cfgdrive.add_file('openstack/latest/user_data', user_data)
111 if network_data:
112 cfgdrive.add_file('openstack/latest/network_data.json', network_data)
113 cfgdrive.add_file('openstack/latest/vendor_data.json', '{}')
114 cfgdrive.add_file('openstack/latest/vendor_data2.json', '{}')