test_block_store_service.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import time
  2. from cloudbridge.base import helpers as cb_helpers
  3. from cloudbridge.factory import ProviderList
  4. from cloudbridge.interfaces import SnapshotState
  5. from cloudbridge.interfaces import VolumeState
  6. from cloudbridge.interfaces.provider import TestMockHelperMixin
  7. from cloudbridge.interfaces.resources import AttachmentInfo
  8. from cloudbridge.interfaces.resources import Snapshot
  9. from cloudbridge.interfaces.resources import Volume
  10. from tests import helpers
  11. from tests.helpers import ProviderTestBase
  12. from tests.helpers import standard_interface_tests as sit
  13. class CloudBlockStoreServiceTestCase(ProviderTestBase):
  14. _multiprocess_can_split_ = True
  15. @helpers.skipIfNoService(['storage.volumes', 'storage.volumes'])
  16. def test_storage_services_event_pattern(self):
  17. # pylint:disable=protected-access
  18. self.assertEqual(
  19. self.provider.storage.volumes._service_event_pattern,
  20. "provider.storage.volumes",
  21. "Event pattern for {} service should be '{}', "
  22. "but found '{}'.".format("volumes",
  23. "provider.storage.volumes",
  24. self.provider.storage.volumes.
  25. _service_event_pattern))
  26. # pylint:disable=protected-access
  27. self.assertEqual(
  28. self.provider.storage.snapshots._service_event_pattern,
  29. "provider.storage.snapshots",
  30. "Event pattern for {} service should be '{}', "
  31. "but found '{}'.".format("snapshots",
  32. "provider.storage.snapshots",
  33. self.provider.storage.snapshots.
  34. _service_event_pattern))
  35. @helpers.skipIfNoService(['storage.volumes'])
  36. def test_crud_volume(self):
  37. def create_vol(label):
  38. return self.provider.storage.volumes.create(
  39. label, 1)
  40. def cleanup_vol(vol):
  41. if vol:
  42. vol.delete()
  43. vol.wait_for([VolumeState.DELETED, VolumeState.UNKNOWN],
  44. terminal_states=[VolumeState.ERROR])
  45. vol.refresh()
  46. self.assertTrue(
  47. vol.state == VolumeState.UNKNOWN,
  48. "Volume.state must be unknown when refreshing after a "
  49. "delete but got %s"
  50. % vol.state)
  51. sit.check_crud(self, self.provider.storage.volumes, Volume,
  52. "cb-createvol", create_vol, cleanup_vol)
  53. @helpers.skipIfNoService(['storage.volumes'])
  54. def test_attach_detach_volume(self):
  55. label = "cb-attachvol-{0}".format(helpers.get_uuid())
  56. # Declare these variables and late binding will allow
  57. # the cleanup method access to the most current values
  58. test_instance = None
  59. with cb_helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
  60. test_instance)):
  61. subnet = helpers.get_or_create_default_subnet(
  62. self.provider)
  63. test_instance = helpers.get_test_instance(
  64. self.provider, label, subnet=subnet)
  65. test_vol = self.provider.storage.volumes.create(
  66. label, 1)
  67. with cb_helpers.cleanup_action(lambda: test_vol.delete()):
  68. test_vol.wait_till_ready()
  69. test_vol.attach(test_instance, '/dev/sda2')
  70. test_vol.wait_for(
  71. [VolumeState.IN_USE],
  72. terminal_states=[VolumeState.ERROR, VolumeState.DELETED])
  73. test_vol.detach()
  74. test_vol.wait_for(
  75. [VolumeState.AVAILABLE],
  76. terminal_states=[VolumeState.ERROR, VolumeState.DELETED])
  77. @helpers.skipIfNoService(['storage.volumes'])
  78. def test_volume_properties(self):
  79. label = "cb-volprops-{0}".format(helpers.get_uuid())
  80. vol_desc = 'newvoldesc1'
  81. # Declare these variables and late binding will allow
  82. # the cleanup method access to the most current values
  83. test_instance = None
  84. with cb_helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
  85. test_instance)):
  86. subnet = helpers.get_or_create_default_subnet(
  87. self.provider)
  88. test_instance = helpers.get_test_instance(
  89. self.provider, label, subnet=subnet)
  90. test_vol = self.provider.storage.volumes.create(
  91. label, 1, description=vol_desc)
  92. with cb_helpers.cleanup_action(lambda: test_vol.delete()):
  93. test_vol.wait_till_ready()
  94. self.assertTrue(
  95. isinstance(test_vol.size, int) and
  96. test_vol.size >= 0,
  97. "Volume.size must be a positive number, but got %s"
  98. % test_vol.size)
  99. self.assertTrue(
  100. test_vol.description is None or
  101. isinstance(test_vol.description, str),
  102. "Volume.description must be None or a string. Got: %s"
  103. % test_vol.description)
  104. self.assertIsNone(test_vol.source)
  105. self.assertIsNotNone(test_vol.create_time)
  106. self.assertIsNotNone(test_vol.zone_id)
  107. self.assertIsNone(test_vol.attachments)
  108. test_vol.attach(test_instance, '/dev/sda2')
  109. test_vol.wait_for(
  110. [VolumeState.IN_USE],
  111. terminal_states=[VolumeState.ERROR, VolumeState.DELETED])
  112. self.assertIsNotNone(test_vol.attachments)
  113. self.assertIsInstance(test_vol.attachments, AttachmentInfo)
  114. self.assertEqual(test_vol.attachments.volume, test_vol)
  115. self.assertEqual(test_vol.attachments.instance_id,
  116. test_instance.id)
  117. if (self.provider.PROVIDER_ID != 'azure' and
  118. self.provider.PROVIDER_ID != 'gcp' and
  119. self.provider.PROVIDER_ID != 'openstack'):
  120. self.assertEqual(test_vol.attachments.device,
  121. "/dev/sda2")
  122. test_vol.detach()
  123. test_vol.label = 'newvolname1'
  124. test_vol.wait_for(
  125. [VolumeState.AVAILABLE],
  126. terminal_states=[VolumeState.ERROR, VolumeState.DELETED])
  127. self.assertEqual(test_vol.label, 'newvolname1')
  128. self.assertEqual(test_vol.description, vol_desc)
  129. self.assertIsNone(test_vol.attachments)
  130. test_vol.wait_for(
  131. [VolumeState.AVAILABLE],
  132. terminal_states=[VolumeState.ERROR, VolumeState.DELETED])
  133. @helpers.skipIfNoService(['storage.snapshots'])
  134. def test_crud_snapshot(self):
  135. # Create a new volume, create a snapshot of the volume, and check
  136. # whether list_snapshots properly detects the new snapshot.
  137. # Delete everything afterwards.
  138. label = "cb-crudsnap-{0}".format(helpers.get_uuid())
  139. test_vol = self.provider.storage.volumes.create(
  140. label, 1)
  141. with cb_helpers.cleanup_action(lambda: test_vol.delete()):
  142. test_vol.wait_till_ready()
  143. def create_snap(label):
  144. return test_vol.create_snapshot(label=label,
  145. description=label)
  146. def cleanup_snap(snap):
  147. if snap:
  148. snap.delete()
  149. snap.wait_for([SnapshotState.UNKNOWN],
  150. terminal_states=[SnapshotState.ERROR])
  151. snap.refresh()
  152. self.assertTrue(
  153. snap.state == SnapshotState.UNKNOWN,
  154. "Snapshot.state must be unknown when refreshing after "
  155. "a delete but got %s"
  156. % snap.state)
  157. sit.check_crud(self, self.provider.storage.snapshots, Snapshot,
  158. "cb-snap", create_snap, cleanup_snap)
  159. # Test creation of a snap via SnapshotService
  160. def create_snap2(label):
  161. return self.provider.storage.snapshots.create(
  162. label=label, volume=test_vol, description=label)
  163. if (self.provider.PROVIDER_ID == ProviderList.AWS and
  164. not isinstance(self.provider, TestMockHelperMixin)):
  165. time.sleep(15) # Or get SnapshotCreationPerVolumeRateExceeded
  166. sit.check_crud(self, self.provider.storage.snapshots, Snapshot,
  167. "cb-snaptwo", create_snap2, cleanup_snap)
  168. @helpers.skipIfNoService(['storage.snapshots'])
  169. def test_snapshot_properties(self):
  170. label = "cb-snapprop-{0}".format(helpers.get_uuid())
  171. test_vol = self.provider.storage.volumes.create(
  172. label, 1)
  173. with cb_helpers.cleanup_action(lambda: test_vol.delete()):
  174. test_vol.wait_till_ready()
  175. snap_label = "cb-snap-{0}".format(label)
  176. test_snap = test_vol.create_snapshot(label=snap_label,
  177. description=snap_label)
  178. def cleanup_snap(snap):
  179. if snap:
  180. snap.delete()
  181. snap.wait_for([SnapshotState.UNKNOWN],
  182. terminal_states=[SnapshotState.ERROR])
  183. with cb_helpers.cleanup_action(lambda: cleanup_snap(test_snap)):
  184. test_snap.wait_till_ready()
  185. self.assertTrue(isinstance(test_vol.size, int))
  186. self.assertEqual(
  187. test_snap.size, test_vol.size,
  188. "Snapshot.size must match original volume's size: %s"
  189. " but is: %s" % (test_vol.size, test_snap.size))
  190. self.assertTrue(
  191. test_vol.description is None or
  192. isinstance(test_vol.description, str),
  193. "Snapshot.description must be None or a string. Got: %s"
  194. % test_vol.description)
  195. self.assertEqual(test_vol.id, test_snap.volume_id)
  196. self.assertIsNotNone(test_vol.create_time)
  197. test_snap.label = 'snapnewname1'
  198. test_snap.description = 'snapnewdescription1'
  199. test_snap.refresh()
  200. self.assertEqual(test_snap.label, 'snapnewname1')
  201. self.assertEqual(test_snap.description, 'snapnewdescription1')
  202. # Test volume creation from a snapshot (via VolumeService)
  203. sv_label = "cb-snapvol-{0}".format(test_snap.name)
  204. snap_vol = self.provider.storage.volumes.create(
  205. sv_label, 1, snapshot=test_snap)
  206. with cb_helpers.cleanup_action(lambda: snap_vol.delete()):
  207. snap_vol.wait_till_ready()
  208. # A volume created from a snapshot should report that
  209. # snapshot as its source, resolved to a Snapshot object
  210. # (not a raw id/URI).
  211. self.assertIsNotNone(
  212. snap_vol.source,
  213. "A volume created from a snapshot must report a "
  214. "source")
  215. self.assertEqual(
  216. snap_vol.source.id, test_snap.id,
  217. "A volume's source should be the snapshot it was "
  218. "created from")
  219. # Test volume creation from a snapshot (via Snapshot)
  220. snap_vol2 = test_snap.create_volume()
  221. with cb_helpers.cleanup_action(lambda: snap_vol2.delete()):
  222. snap_vol2.wait_till_ready()