test_compute_service.py 13 KB

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