utils.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import functools
  2. import json
  3. import re
  4. import socket
  5. import subprocess
  6. import time
  7. import traceback
  8. from oslo_config import cfg
  9. from oslo_log import log as logging
  10. from coriolis import constants
  11. from coriolis import exception
  12. opts = [
  13. cfg.StrOpt('qemu_img_path',
  14. default='qemu-img',
  15. help='The path of the qemu-img tool.'),
  16. ]
  17. CONF = cfg.CONF
  18. logging.register_options(CONF)
  19. CONF.register_opts(opts)
  20. LOG = logging.getLogger(__name__)
  21. def setup_logging():
  22. logging.setup(CONF, 'coriolis')
  23. def retry_on_error(max_attempts=5, sleep_seconds=0):
  24. def _retry_on_error(func):
  25. @functools.wraps(func)
  26. def _exec_retry(*args, **kwargs):
  27. i = 0
  28. while True:
  29. try:
  30. return func(*args, **kwargs)
  31. except Exception as ex:
  32. i += 1
  33. if i < max_attempts:
  34. LOG.warn("Exception occurred, retrying: %s", ex)
  35. time.sleep(sleep_seconds)
  36. else:
  37. raise
  38. return _exec_retry
  39. return _retry_on_error
  40. def get_udev_net_rules(net_ifaces_info):
  41. content = ""
  42. for name, mac_address in net_ifaces_info:
  43. content += ('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
  44. 'ATTR{address}=="%(mac_address)s", NAME="%(name)s"\n' %
  45. {"name": name, "mac_address": mac_address.lower()})
  46. return content
  47. def get_linux_os_info(ssh):
  48. out = exec_ssh_cmd(ssh, "lsb_release -a || true").decode()
  49. dist_id = re.findall('^Distributor ID:\s(.*)$', out, re.MULTILINE)
  50. release = re.findall('^Release:\s(.*)$', out, re.MULTILINE)
  51. if dist_id and release:
  52. return (dist_id[0], release[0])
  53. @retry_on_error()
  54. def test_ssh_path(ssh, remote_path):
  55. sftp = ssh.open_sftp()
  56. try:
  57. sftp.stat(remote_path)
  58. return True
  59. except IOError as ex:
  60. if ex.args[0] == 2:
  61. return False
  62. raise
  63. @retry_on_error()
  64. def read_ssh_file(ssh, remote_path):
  65. sftp = ssh.open_sftp()
  66. return sftp.open(remote_path, 'rb').read()
  67. @retry_on_error()
  68. def write_ssh_file(ssh, remote_path, content):
  69. sftp = ssh.open_sftp()
  70. sftp.open(remote_path, 'wb').write(content)
  71. @retry_on_error()
  72. def list_ssh_dir(ssh, remote_path):
  73. sftp = ssh.open_sftp()
  74. return sftp.listdir(remote_path)
  75. @retry_on_error()
  76. def exec_ssh_cmd(ssh, cmd):
  77. LOG.debug("Executing SSH command: %s", cmd)
  78. stdin, stdout, stderr = ssh.exec_command(cmd)
  79. exit_code = stdout.channel.recv_exit_status()
  80. std_out = stdout.read()
  81. std_err = stderr.read()
  82. if exit_code:
  83. raise exception.CoriolisException(
  84. "Command \"%s\" failed with exit code: %s\n"
  85. "stdout: %s\nstd_err: %s" %
  86. (cmd, exit_code, std_out, std_err))
  87. return std_out
  88. def exec_ssh_cmd_chroot(ssh, chroot_dir, cmd):
  89. return exec_ssh_cmd(ssh, "sudo chroot %s %s" % (chroot_dir, cmd))
  90. def check_fs(ssh, fs_type, dev_path):
  91. try:
  92. out = exec_ssh_cmd(
  93. ssh, "sudo fsck -p -t %s %s" % (fs_type, dev_path)).decode()
  94. LOG.debug("File system checked:\n%s", out)
  95. except Exception as ex:
  96. LOG.warn("Checking file system returned an error:\n%s", str(ex))
  97. def _check_port_open(host, port):
  98. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  99. try:
  100. s.settimeout(1)
  101. s.connect((host, port))
  102. return True
  103. except (ConnectionRefusedError, socket.timeout, OSError):
  104. return False
  105. finally:
  106. s.close()
  107. def wait_for_port_connectivity(address, port, max_wait=300):
  108. i = 0
  109. while not _check_port_open(address, port) and i < max_wait:
  110. time.sleep(1)
  111. i += 1
  112. if i == max_wait:
  113. raise exception.CoriolisException("Connection failed on port %s" %
  114. port)
  115. def exec_process(args):
  116. p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  117. std_out, std_err = p.communicate()
  118. if p.returncode:
  119. raise exception.CoriolisException(
  120. "Command \"%s\" failed with exit code: %s\nstdout: %s\nstd_err: %s"
  121. % (args, p.returncode, std_out, std_err))
  122. return std_out
  123. def get_disk_info(disk_path):
  124. out = exec_process([CONF.qemu_img_path, 'info', '--output=json',
  125. disk_path])
  126. disk_info = json.loads(out.decode())
  127. if disk_info["format"] == "vpc":
  128. disk_info["format"] = constants.DISK_FORMAT_VHD
  129. return disk_info
  130. def convert_disk_format(disk_path, target_disk_path, target_format):
  131. if target_format == constants.DISK_FORMAT_VHD:
  132. target_format = "vpc"
  133. exec_process([CONF.qemu_img_path, 'convert', '-O', target_format,
  134. disk_path, target_disk_path])
  135. def get_hostname():
  136. return socket.gethostname()
  137. def get_exception_details():
  138. return traceback.format_exc()