common.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # Copyright 2016 Cloudbase Solutions Srl
  2. # All Rights Reserved.
  3. import collections
  4. import math
  5. import time
  6. import uuid
  7. from oslo_utils import units
  8. from coriolis import exception
  9. from coriolis import utils
  10. MIGRATION_TMP_FORMAT = "migration_tmp_%s"
  11. NOVA_API_VERSION = 2
  12. GLANCE_API_VERSION = 1
  13. NEUTRON_API_VERSION = '2.0'
  14. CINDER_API_VERSION = 2
  15. GlanceImage = collections.namedtuple(
  16. "GlanceImage", "id format size path os_type")
  17. def get_unique_name():
  18. return MIGRATION_TMP_FORMAT % str(uuid.uuid4())
  19. def create_image(glance, name, disk_path, disk_format, container_format,
  20. hypervisor_type):
  21. properties = {}
  22. if hypervisor_type:
  23. properties["hypervisor_type"] = hypervisor_type
  24. if glance.version == 1:
  25. return _create_image_v1(glance, name, disk_path, disk_format,
  26. container_format, properties)
  27. elif glance.version == 2:
  28. return _create_image_v2(glance, name, disk_path, disk_format,
  29. container_format, properties)
  30. else:
  31. raise NotImplementedError("Unsupported Glance version")
  32. @utils.retry_on_error()
  33. def _create_image_v2(glance, name, disk_path, disk_format, container_format,
  34. properties):
  35. image = glance.images.create(
  36. name=name,
  37. disk_format=disk_format,
  38. container_format=container_format,
  39. **properties)
  40. try:
  41. with open(disk_path, 'rb') as f:
  42. glance.images.upload(image.id, f)
  43. return image
  44. except:
  45. glance.images.delete(image.id)
  46. raise
  47. @utils.retry_on_error()
  48. def _create_image_v1(glance, name, disk_path, disk_format, container_format,
  49. properties):
  50. with open(disk_path, 'rb') as f:
  51. return glance.images.create(
  52. name=name,
  53. disk_format=disk_format,
  54. container_format=container_format,
  55. properties=properties,
  56. data=f)
  57. @utils.retry_on_error()
  58. def wait_for_image(nova, image_id, expected_status='ACTIVE'):
  59. image = nova.images.get(image_id)
  60. while image.status not in [expected_status, 'ERROR']:
  61. time.sleep(2)
  62. image = nova.images.get(image.id)
  63. if image.status != expected_status:
  64. raise exception.CoriolisException(
  65. "Image is in status: %s" % image.status)
  66. @utils.retry_on_error()
  67. def wait_for_instance(nova, instance, expected_status='ACTIVE'):
  68. instance = nova.servers.get(instance.id)
  69. while instance.status not in [expected_status, 'ERROR']:
  70. time.sleep(2)
  71. instance = nova.servers.get(instance.id)
  72. if instance.status != expected_status:
  73. raise exception.CoriolisException(
  74. "VM is in status: %s" % instance.status)
  75. @utils.retry_on_error()
  76. def wait_for_instance_deletion(nova, instance_id):
  77. instances = nova.servers.findall(id=instance_id)
  78. while instances and instances[0].status != 'ERROR':
  79. time.sleep(2)
  80. instances = nova.servers.findall(id=instance_id)
  81. if instances:
  82. raise exception.CoriolisException(
  83. "VM is in status: %s" % instances[0].status)
  84. @utils.retry_on_error()
  85. def find_volume(cinder, volume_id):
  86. volumes = cinder.volumes.findall(id=volume_id)
  87. if volumes:
  88. return volumes[0]
  89. @utils.retry_on_error()
  90. def extend_volume(cinder, volume_id, new_size):
  91. volume_size_gb = math.ceil(new_size / units.Gi)
  92. cinder.volumes.extend(volume_id, volume_size_gb)
  93. @utils.retry_on_error()
  94. def get_volume_from_snapshot(cinder, snapshot_id):
  95. snapshot = cinder.volume_snapshots.get(snapshot_id)
  96. return cinder.volumes.get(snapshot.volume_id)
  97. @utils.retry_on_error()
  98. def create_volume(cinder, size, name, image_ref=None, snapshot_id=None):
  99. if snapshot_id:
  100. volume_size_gb = None
  101. else:
  102. volume_size_gb = math.ceil(size / units.Gi)
  103. return cinder.volumes.create(
  104. size=volume_size_gb,
  105. name=name,
  106. imageRef=image_ref,
  107. snapshot_id=snapshot_id)
  108. @utils.retry_on_error(terminal_exceptions=[exception.NotFound])
  109. def get_flavor(nova, flavor_name):
  110. flavors = nova.flavors.findall(name=flavor_name)
  111. if not flavors:
  112. raise exception.FlavorNotFound(flavor_name=flavor_name)
  113. return flavors[0]
  114. @utils.retry_on_error(terminal_exceptions=[exception.NotFound])
  115. def get_image(glance, image_name):
  116. images = glance.images.findall(name=image_name)
  117. if not images:
  118. raise exception.ImageNotFound(image_name=image_name)
  119. return images[0]
  120. @utils.retry_on_error(terminal_exceptions=[exception.NotFound])
  121. def check_floating_ip_pool(nova, pool_name):
  122. if not nova.floating_ip_pools.findall(name=pool_name):
  123. raise exception.FloatingIPPoolNotFound(pool_name=pool_name)
  124. @utils.retry_on_error(terminal_exceptions=[exception.NotFound])
  125. def wait_for_volume(cinder, volume_id, expected_status='available'):
  126. volumes = cinder.volumes.findall(id=volume_id)
  127. if not volumes:
  128. raise exception.VolumeNotFound(volume_id=volume_id)
  129. volume = volumes[0]
  130. while volume.status not in [expected_status, 'error']:
  131. time.sleep(2)
  132. volume = cinder.volumes.get(volume.id)
  133. if volume.status != expected_status:
  134. raise exception.CoriolisException(
  135. "Volume is in status: %s" % volume.status)
  136. @utils.retry_on_error()
  137. def delete_volume(cinder, volume_id):
  138. volumes = cinder.volumes.findall(id=volume_id)
  139. for volume in volumes:
  140. volume.delete()
  141. @utils.retry_on_error()
  142. def create_volume_snapshot(cinder, volume_id, name):
  143. return cinder.volume_snapshots.create(volume_id, name=name)
  144. @utils.retry_on_error(terminal_exceptions=[exception.NotFound])
  145. def wait_for_volume_snapshot(cinder, snapshot_id,
  146. expected_status='available'):
  147. snapshots = cinder.volume_snapshots.findall(id=snapshot_id)
  148. if not snapshots:
  149. if expected_status == 'deleted':
  150. return
  151. raise exception.VolumeSnapshotNotFound(snapshot_id=snapshot_id)
  152. snapshot = snapshots[0]
  153. while snapshot.status not in [expected_status, 'error']:
  154. time.sleep(2)
  155. if expected_status == 'deleted':
  156. snapshots = cinder.volume_snapshots.findall(id=snapshot_id)
  157. if not snapshots:
  158. return
  159. snapshot = snapshots[0]
  160. else:
  161. snapshot = cinder.volume_snapshots.get(snapshot.id)
  162. if snapshot.status != expected_status:
  163. raise exception.CoriolisException(
  164. "Volume snapshot is in status: %s" % snapshot.status)
  165. @utils.retry_on_error()
  166. def delete_volume_snapshot(cinder, snapshot_id):
  167. snapshots = cinder.volume_snapshots.findall(id=snapshot_id)
  168. for snapshot in snapshots:
  169. return cinder.volume_snapshots.delete(snapshot.id)