New Saltstack Salt formula
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

142 lines
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)