test_compute_service.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import uuid
  2. import ipaddress
  3. from cloudbridge.cloud.interfaces \
  4. import InvalidConfigurationException
  5. from cloudbridge.cloud.interfaces import InstanceState
  6. from cloudbridge.cloud.interfaces.resources import InstanceType
  7. from cloudbridge.cloud.interfaces.resources import WaitStateException
  8. from test.helpers import ProviderTestBase
  9. import test.helpers as helpers
  10. class CloudComputeServiceTestCase(ProviderTestBase):
  11. def __init__(self, methodName, provider):
  12. super(CloudComputeServiceTestCase, self).__init__(
  13. methodName=methodName, provider=provider)
  14. def test_crud_instance(self):
  15. name = "CBInstCrud-{0}-{1}".format(
  16. self.provider.name,
  17. uuid.uuid4())
  18. inst = helpers.create_test_instance(self.provider, name)
  19. def cleanup_inst(instance):
  20. instance.terminate()
  21. instance.wait_for(
  22. [InstanceState.TERMINATED, InstanceState.UNKNOWN],
  23. terminal_states=[InstanceState.ERROR])
  24. with helpers.cleanup_action(lambda: cleanup_inst(inst)):
  25. inst.wait_till_ready()
  26. all_instances = self.provider.compute.instances.list()
  27. list_instances = [i for i in all_instances if i.name == name]
  28. self.assertTrue(
  29. len(list_instances) == 1,
  30. "List instances does not return the expected instance %s" %
  31. name)
  32. # check iteration
  33. iter_instances = [i for i in self.provider.compute.instances
  34. if i.name == name]
  35. self.assertTrue(
  36. len(iter_instances) == 1,
  37. "Iter instances does not return the expected instance %s" %
  38. name)
  39. # check find
  40. find_instances = self.provider.compute.instances.find(name=name)
  41. self.assertTrue(
  42. len(find_instances) == 1,
  43. "Find instances does not return the expected instance %s" %
  44. name)
  45. # check non-existent find
  46. find_instances = self.provider.compute.instances.find(
  47. name="non_existent")
  48. self.assertTrue(
  49. len(find_instances) == 0,
  50. "Find() for a non-existent image returned %s" % find_instances)
  51. get_inst = self.provider.compute.instances.get(
  52. inst.id)
  53. self.assertTrue(
  54. list_instances[0] ==
  55. get_inst == inst,
  56. "Objects returned by list: {0} and get: {1} are not as "
  57. " expected: {2}" .format(list_instances[0].id,
  58. get_inst.id,
  59. inst.id))
  60. self.assertTrue(
  61. list_instances[0].name ==
  62. get_inst.name == inst.name,
  63. "Names returned by list: {0} and get: {1} are not as "
  64. " expected: {2}" .format(list_instances[0].name,
  65. get_inst.name,
  66. inst.name))
  67. deleted_inst = self.provider.compute.instances.get(
  68. inst.id)
  69. self.assertTrue(
  70. deleted_inst is None or deleted_inst.state in (
  71. InstanceState.TERMINATED,
  72. InstanceState.UNKNOWN),
  73. "Instance %s should have been deleted but still exists." %
  74. name)
  75. def _is_valid_ip(self, address):
  76. try:
  77. ipaddress.ip_address(address)
  78. except ValueError:
  79. return False
  80. return True
  81. def test_instance_properties(self):
  82. name = "CBInstProps-{0}-{1}".format(
  83. self.provider.name,
  84. uuid.uuid4())
  85. kp = self.provider.security.key_pairs.create(name=name)
  86. sg = self.provider.security.security_groups.create(
  87. name=name, description=name)
  88. test_instance = helpers.get_test_instance(self.provider,
  89. name, key_pair=kp,
  90. security_groups=[sg])
  91. def cleanup(inst, kp, sg):
  92. inst.terminate()
  93. inst.wait_for([InstanceState.TERMINATED, InstanceState.UNKNOWN],
  94. terminal_states=[InstanceState.ERROR])
  95. kp.delete()
  96. sg.delete()
  97. with helpers.cleanup_action(lambda: cleanup(test_instance, kp, sg)):
  98. self.assertTrue(
  99. test_instance.id in repr(test_instance),
  100. "repr(obj) should contain the object id so that the object"
  101. " can be reconstructed, but does not. eval(repr(obj)) == obj")
  102. self.assertEqual(
  103. test_instance.name, name,
  104. "Instance name {0} is not equal to the expected name"
  105. " {1}".format(test_instance.name, name))
  106. image_id = helpers.get_provider_test_data(self.provider, "image")
  107. self.assertEqual(test_instance.image_id, image_id,
  108. "Image id {0} is not equal to the expected id"
  109. " {1}".format(test_instance.image_id, image_id))
  110. self.assertEqual(
  111. test_instance.image_id,
  112. helpers.get_provider_test_data(self.provider, "image"))
  113. self.assertIsInstance(test_instance.public_ips, list)
  114. self.assertIsInstance(test_instance.private_ips, list)
  115. self.assertEqual(
  116. test_instance.key_pair_name,
  117. kp.name)
  118. self.assertIsInstance(test_instance.security_groups, list)
  119. self.assertEqual(
  120. test_instance.security_groups[0],
  121. sg)
  122. # Must have either a public or a private ip
  123. ip_private = test_instance.private_ips[0] \
  124. if test_instance.private_ips else None
  125. ip_address = test_instance.public_ips[0] \
  126. if test_instance.public_ips else ip_private
  127. self.assertIsNotNone(
  128. ip_address,
  129. "Instance must have either a public IP or a private IP")
  130. self.assertTrue(
  131. self._is_valid_ip(ip_address),
  132. "Instance must have a valid IP address")
  133. self.assertIsInstance(test_instance.instance_type, InstanceType)
  134. expected_type = helpers.get_provider_test_data(self.provider,
  135. 'instance_type')
  136. self.assertEqual(
  137. test_instance.instance_type.name, expected_type,
  138. "Instance type {0} does not match expected type {1}".format(
  139. test_instance.instance_type.name, expected_type))
  140. def test_block_device_mapping_launch_config(self):
  141. lc = self.provider.compute.instances.create_launch_config()
  142. # specifying an invalid size should raise
  143. # an exception
  144. with self.assertRaises(InvalidConfigurationException):
  145. lc.add_volume_device(size=-1)
  146. # Attempting to add a blank volume without specifying a size
  147. # should raise an exception
  148. with self.assertRaises(InvalidConfigurationException):
  149. lc.add_volume_device(source=None)
  150. # block_devices should be empty so far
  151. self.assertListEqual(
  152. lc.block_devices, [], "No block devices should have been"
  153. " added to mappings list since the configuration was"
  154. " invalid")
  155. # Add a new volume
  156. lc.add_volume_device(size=1, delete_on_terminate=True)
  157. # Override root volume size
  158. image_id = helpers.get_provider_test_data(self.provider, "image")
  159. img = self.provider.compute.images.get(image_id)
  160. lc.add_volume_device(
  161. is_root=True,
  162. source=img,
  163. # TODO: This should be greater than the ami size or tests will fail
  164. # on actual infrastructure. Needs an image.size method
  165. size=2,
  166. delete_on_terminate=True)
  167. # Attempting to add more than one root volume should raise an
  168. # exception.
  169. with self.assertRaises(InvalidConfigurationException):
  170. lc.add_volume_device(size=1, is_root=True)
  171. # Attempting to add an incorrect source should raise an exception
  172. with self.assertRaises(InvalidConfigurationException):
  173. lc.add_volume_device(
  174. source="invalid_source",
  175. delete_on_terminate=True)
  176. # Add all available ephemeral devices
  177. instance_type_name = helpers.get_provider_test_data(
  178. self.provider,
  179. "instance_type")
  180. inst_type = self.provider.compute.instance_types.find(
  181. name=instance_type_name)[0]
  182. for _ in range(inst_type.num_ephemeral_disks):
  183. lc.add_ephemeral_device()
  184. # block_devices should be populated
  185. self.assertTrue(
  186. len(lc.block_devices) == 2 + inst_type.num_ephemeral_disks,
  187. "Expected %d total block devices bit found %d" %
  188. (2 + inst_type.num_ephemeral_disks, len(lc.block_devices)))
  189. def test_block_device_mapping_attachments(self):
  190. name = "CBInstBlkAttch-{0}-{1}".format(
  191. self.provider.name,
  192. uuid.uuid4())
  193. # test_vol = self.provider.block_store.volumes.create(
  194. # name,
  195. # 1,
  196. # helpers.get_provider_test_data(self.provider, "placement"))
  197. # with helpers.cleanup_action(lambda: test_vol.delete()):
  198. # test_vol.wait_till_ready()
  199. # test_snap = test_vol.create_snapshot(name=name,
  200. # description=name)
  201. #
  202. # def cleanup_snap(snap):
  203. # snap.delete()
  204. # snap.wait_for(
  205. # [SnapshotState.UNKNOWN],
  206. # terminal_states=[SnapshotState.ERROR])
  207. #
  208. # with helpers.cleanup_action(lambda: cleanup_snap(test_snap)):
  209. # test_snap.wait_till_ready()
  210. lc = self.provider.compute.instances.create_launch_config()
  211. # Add a new blank volume
  212. # lc.add_volume_device(size=1, delete_on_terminate=True)
  213. # Attach an existing volume
  214. # lc.add_volume_device(size=1, source=test_vol,
  215. # delete_on_terminate=True)
  216. # Add a new volume based on a snapshot
  217. # lc.add_volume_device(size=1, source=test_snap,
  218. # delete_on_terminate=True)
  219. # Override root volume size
  220. image_id = helpers.get_provider_test_data(
  221. self.provider,
  222. "image")
  223. img = self.provider.compute.images.get(image_id)
  224. lc.add_volume_device(
  225. is_root=True,
  226. source=img,
  227. # TODO: This should be greater than the ami size or tests
  228. # will fail on actual infrastructure. Needs an image.size
  229. # method
  230. size=2,
  231. delete_on_terminate=True)
  232. # Add all available ephemeral devices
  233. instance_type_name = helpers.get_provider_test_data(
  234. self.provider,
  235. "instance_type")
  236. inst_type = self.provider.compute.instance_types.find(
  237. name=instance_type_name)[0]
  238. for _ in range(inst_type.num_ephemeral_disks):
  239. lc.add_ephemeral_device()
  240. inst = helpers.create_test_instance(
  241. self.provider,
  242. name,
  243. zone=helpers.get_provider_test_data(
  244. self.provider,
  245. 'placement'),
  246. launch_config=lc)
  247. def cleanup(instance):
  248. instance.terminate()
  249. instance.wait_for(
  250. [InstanceState.TERMINATED, InstanceState.UNKNOWN],
  251. terminal_states=[InstanceState.ERROR])
  252. with helpers.cleanup_action(lambda: cleanup(inst)):
  253. try:
  254. inst.wait_till_ready()
  255. except WaitStateException as e:
  256. self.fail("The block device mapped launch did not "
  257. " complete successfully: %s" % e)
  258. # TODO: Check instance attachments and make sure they
  259. # correspond to requested mappings