New version of salt-formula from Saltstack
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 satır
4.3KB

  1. # -*- coding: utf-8 -*-
  2. import errno
  3. import json
  4. import logging
  5. import os
  6. import shutil
  7. import six
  8. import subprocess
  9. import tempfile
  10. import uuid
  11. import yaml
  12. LOG = logging.getLogger(__name__)
  13. class ConfigDriveBuilder(object):
  14. """Build config drives, optionally as a context manager."""
  15. def __init__(self, image_file):
  16. self.image_file = image_file
  17. self.mdfiles=[]
  18. def __enter__(self):
  19. self._delete_if_exists(self.image_file)
  20. return self
  21. def __exit__(self, exctype, excval, exctb):
  22. self.make_drive()
  23. @staticmethod
  24. def _ensure_tree(path):
  25. try:
  26. os.makedirs(path)
  27. except OSError as e:
  28. if e.errno == errno.EEXIST and os.path.isdir(path):
  29. pass
  30. else:
  31. raise
  32. @staticmethod
  33. def _delete_if_exists(path):
  34. try:
  35. os.unlink(path)
  36. except OSError as e:
  37. if e.errno != errno.ENOENT:
  38. raise
  39. def add_file(self, path, data):
  40. self.mdfiles.append((path, data))
  41. def _add_file(self, basedir, path, data):
  42. filepath = os.path.join(basedir, path)
  43. dirname = os.path.dirname(filepath)
  44. self._ensure_tree(dirname)
  45. with open(filepath, 'wb') as f:
  46. if isinstance(data, six.text_type):
  47. data = data.encode('utf-8')
  48. f.write(data)
  49. def _write_md_files(self, basedir):
  50. for data in self.mdfiles:
  51. self._add_file(basedir, data[0], data[1])
  52. def _make_iso9660(self, path, tmpdir):
  53. cmd = ['mkisofs',
  54. '-o', path,
  55. '-ldots',
  56. '-allow-lowercase',
  57. '-allow-multidot',
  58. '-l',
  59. '-V', 'config-2',
  60. '-r',
  61. '-J',
  62. '-quiet',
  63. tmpdir]
  64. try:
  65. LOG.info('Running cmd (subprocess): %s', cmd)
  66. _pipe = subprocess.PIPE
  67. obj = subprocess.Popen(cmd,
  68. stdin=_pipe,
  69. stdout=_pipe,
  70. stderr=_pipe,
  71. close_fds=True)
  72. (stdout, stderr) = obj.communicate()
  73. obj.stdin.close()
  74. _returncode = obj.returncode
  75. LOG.debug('Cmd "%s" returned: %s', cmd, _returncode)
  76. if _returncode != 0:
  77. output = 'Stdout: %s\nStderr: %s' % (stdout, stderr)
  78. LOG.error('The command "%s" failed. %s',
  79. cmd, output)
  80. raise subprocess.CalledProcessError(cmd=cmd,
  81. returncode=_returncode,
  82. output=output)
  83. except OSError as err:
  84. LOG.error('Got an OSError in the command: "%s". Errno: %s', cmd,
  85. err.errno)
  86. raise
  87. def make_drive(self):
  88. """Make the config drive.
  89. :raises CalledProcessError if a helper process has failed.
  90. """
  91. try:
  92. tmpdir = tempfile.mkdtemp()
  93. self._write_md_files(tmpdir)
  94. self._make_iso9660(self.image_file, tmpdir)
  95. finally:
  96. shutil.rmtree(tmpdir)
  97. def generate(dst, hostname, domainname, instance_id=None, user_data=None,
  98. network_data=None):
  99. ''' Generate config drive
  100. :param dst: destination file to place config drive.
  101. :param hostname: hostname of Instance.
  102. :param domainname: instance domain.
  103. :param instance_id: UUID of the instance.
  104. :param user_data: custom user data dictionary.
  105. :param network_data: custom network info dictionary.
  106. '''
  107. instance_md = {}
  108. instance_md['uuid'] = instance_id or str(uuid.uuid4())
  109. instance_md['hostname'] = '%s.%s' % (hostname, domainname)
  110. instance_md['name'] = hostname
  111. if user_data:
  112. user_data = '#cloud-config\n\n' + yaml.dump(user_data, default_flow_style=False)
  113. data = json.dumps(instance_md)
  114. with ConfigDriveBuilder(dst) as cfgdrive:
  115. cfgdrive.add_file('openstack/latest/meta_data.json', data)
  116. if user_data:
  117. cfgdrive.add_file('openstack/latest/user_data', user_data)
  118. if network_data:
  119. cfgdrive.add_file('openstack/latest/network_data.json', json.dumps(network_data))
  120. LOG.debug('Config drive was built %s' % dst)