test_compute_service.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. import ipaddress
  2. from test import helpers
  3. from test.helpers import ProviderTestBase
  4. from test.helpers import standard_interface_tests as sit
  5. from cloudbridge.cloud.interfaces import InstanceState
  6. from cloudbridge.cloud.interfaces import InvalidConfigurationException
  7. from cloudbridge.cloud.interfaces import TestMockHelperMixin
  8. from cloudbridge.cloud.interfaces.exceptions import WaitStateException
  9. from cloudbridge.cloud.interfaces.resources import InstanceType
  10. # from cloudbridge.cloud.interfaces.resources import SnapshotState
  11. import six
  12. class CloudComputeServiceTestCase(ProviderTestBase):
  13. @helpers.skipIfNoService(['compute.instances', 'networking.networks'])
  14. def test_crud_instance(self):
  15. name = "cb_instcrud-{0}".format(helpers.get_uuid())
  16. # Declare these variables and late binding will allow
  17. # the cleanup method access to the most current values
  18. inst = None
  19. net = None
  20. with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
  21. inst, net)):
  22. net, subnet = helpers.create_test_network(self.provider, name)
  23. inst = helpers.get_test_instance(self.provider, name,
  24. subnet=subnet)
  25. sit.check_standard_behaviour(
  26. self, self.provider.compute.instances, inst)
  27. deleted_inst = self.provider.compute.instances.get(
  28. inst.id)
  29. self.assertTrue(
  30. deleted_inst is None or deleted_inst.state in (
  31. InstanceState.TERMINATED,
  32. InstanceState.UNKNOWN),
  33. "Instance %s should have been deleted but still exists." %
  34. name)
  35. def _is_valid_ip(self, address):
  36. try:
  37. ipaddress.ip_address(address)
  38. except ValueError:
  39. return False
  40. return True
  41. @helpers.skipIfNoService(['compute.instances', 'networking.networks',
  42. 'security.security_groups',
  43. 'security.key_pairs'])
  44. def test_instance_properties(self):
  45. name = "cb_inst_props-{0}".format(helpers.get_uuid())
  46. # Declare these variables and late binding will allow
  47. # the cleanup method access to the most current values
  48. test_instance = None
  49. net = None
  50. sg = None
  51. kp = None
  52. with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
  53. test_instance, net, sg, kp)):
  54. net, subnet = helpers.create_test_network(self.provider, name)
  55. kp = self.provider.security.key_pairs.create(name=name)
  56. sg = self.provider.security.security_groups.create(
  57. name=name, description=name, network_id=net.id)
  58. test_instance = helpers.get_test_instance(self.provider,
  59. name, key_pair=kp,
  60. security_groups=[sg],
  61. subnet=subnet)
  62. self.assertEqual(
  63. test_instance.name, name,
  64. "Instance name {0} is not equal to the expected name"
  65. " {1}".format(test_instance.name, name))
  66. image_id = helpers.get_provider_test_data(self.provider, "image")
  67. self.assertEqual(test_instance.image_id, image_id,
  68. "Image id {0} is not equal to the expected id"
  69. " {1}".format(test_instance.image_id, image_id))
  70. self.assertIsInstance(test_instance.zone_id,
  71. six.string_types)
  72. # FIXME: Moto is not returning the instance's placement zone
  73. # find_zone = [zone for zone in
  74. # self.provider.compute.regions.current.zones
  75. # if zone.id == test_instance.zone_id]
  76. # self.assertEqual(len(find_zone), 1,
  77. # "Instance's placement zone could not be "
  78. # " found in zones list")
  79. self.assertEqual(
  80. test_instance.image_id,
  81. helpers.get_provider_test_data(self.provider, "image"))
  82. self.assertIsInstance(test_instance.public_ips, list)
  83. self.assertIsInstance(test_instance.private_ips, list)
  84. self.assertEqual(
  85. test_instance.key_pair_name,
  86. kp.name)
  87. self.assertIsInstance(test_instance.security_groups, list)
  88. self.assertEqual(
  89. test_instance.security_groups[0],
  90. sg)
  91. self.assertIsInstance(test_instance.security_group_ids, list)
  92. self.assertEqual(
  93. test_instance.security_group_ids[0],
  94. sg.id)
  95. # Must have either a public or a private ip
  96. ip_private = test_instance.private_ips[0] \
  97. if test_instance.private_ips else None
  98. ip_address = test_instance.public_ips[0] \
  99. if test_instance.public_ips and test_instance.public_ips[0] \
  100. else ip_private
  101. self.assertIsNotNone(
  102. ip_address,
  103. "Instance must have either a public IP or a private IP")
  104. self.assertTrue(
  105. self._is_valid_ip(ip_address),
  106. "Instance must have a valid IP address")
  107. self.assertIsInstance(test_instance.instance_type_id,
  108. six.string_types)
  109. itype = self.provider.compute.instance_types.get(
  110. test_instance.instance_type_id)
  111. self.assertEqual(
  112. itype, test_instance.instance_type,
  113. "Instance type {0} does not match expected type {1}".format(
  114. itype.name, test_instance.instance_type))
  115. self.assertIsInstance(itype, InstanceType)
  116. expected_type = helpers.get_provider_test_data(self.provider,
  117. 'instance_type')
  118. self.assertEqual(
  119. itype.name, expected_type,
  120. "Instance type {0} does not match expected type {1}".format(
  121. itype.name, expected_type))
  122. @helpers.skipIfNoService(['compute.instances', 'compute.images',
  123. 'compute.instance_types'])
  124. def test_block_device_mapping_launch_config(self):
  125. lc = self.provider.compute.instances.create_launch_config()
  126. # specifying an invalid size should raise
  127. # an exception
  128. with self.assertRaises(InvalidConfigurationException):
  129. lc.add_volume_device(size=-1)
  130. # Attempting to add a blank volume without specifying a size
  131. # should raise an exception
  132. with self.assertRaises(InvalidConfigurationException):
  133. lc.add_volume_device(source=None)
  134. # block_devices should be empty so far
  135. self.assertListEqual(
  136. lc.block_devices, [], "No block devices should have been"
  137. " added to mappings list since the configuration was"
  138. " invalid")
  139. # Add a new volume
  140. lc.add_volume_device(size=1, delete_on_terminate=True)
  141. # Override root volume size
  142. image_id = helpers.get_provider_test_data(self.provider, "image")
  143. img = self.provider.compute.images.get(image_id)
  144. # The size should be greater then the ami size
  145. # and therefore, img.min_disk is used.
  146. lc.add_volume_device(
  147. is_root=True,
  148. source=img,
  149. size=img.min_disk if img and img.min_disk else 2,
  150. delete_on_terminate=True)
  151. # Attempting to add more than one root volume should raise an
  152. # exception.
  153. with self.assertRaises(InvalidConfigurationException):
  154. lc.add_volume_device(size=1, is_root=True)
  155. # Attempting to add an incorrect source should raise an exception
  156. with self.assertRaises(InvalidConfigurationException):
  157. lc.add_volume_device(
  158. source="invalid_source",
  159. delete_on_terminate=True)
  160. # Add all available ephemeral devices
  161. instance_type_name = helpers.get_provider_test_data(
  162. self.provider,
  163. "instance_type")
  164. inst_type = self.provider.compute.instance_types.find(
  165. name=instance_type_name)[0]
  166. for _ in range(inst_type.num_ephemeral_disks):
  167. lc.add_ephemeral_device()
  168. # block_devices should be populated
  169. self.assertTrue(
  170. len(lc.block_devices) == 2 + inst_type.num_ephemeral_disks,
  171. "Expected %d total block devices bit found %d" %
  172. (2 + inst_type.num_ephemeral_disks, len(lc.block_devices)))
  173. @helpers.skipIfNoService(['compute.instances', 'compute.images',
  174. 'compute.instance_types', 'block_store.volumes'])
  175. def test_block_device_mapping_attachments(self):
  176. name = "cb_blkattch-{0}".format(helpers.get_uuid())
  177. # Comment out BDM tests because OpenStack is not stable enough yet
  178. if True:
  179. if True:
  180. # test_vol = self.provider.block_store.volumes.create(
  181. # name,
  182. # 1,
  183. # helpers.get_provider_test_data(self.provider,
  184. # "placement"))
  185. # with helpers.cleanup_action(lambda: test_vol.delete()):
  186. # test_vol.wait_till_ready()
  187. # test_snap = test_vol.create_snapshot(name=name,
  188. # description=name)
  189. #
  190. # def cleanup_snap(snap):
  191. # snap.delete()
  192. # snap.wait_for(
  193. # [SnapshotState.UNKNOWN],
  194. # terminal_states=[SnapshotState.ERROR])
  195. #
  196. # with helpers.cleanup_action(lambda:
  197. # cleanup_snap(test_snap)):
  198. # test_snap.wait_till_ready()
  199. lc = self.provider.compute.instances.create_launch_config()
  200. # # Add a new blank volume
  201. # lc.add_volume_device(size=1, delete_on_terminate=True)
  202. #
  203. # # Attach an existing volume
  204. # lc.add_volume_device(size=1, source=test_vol,
  205. # delete_on_terminate=True)
  206. #
  207. # # Add a new volume based on a snapshot
  208. # lc.add_volume_device(size=1, source=test_snap,
  209. # delete_on_terminate=True)
  210. # Override root volume size
  211. image_id = helpers.get_provider_test_data(
  212. self.provider,
  213. "image")
  214. img = self.provider.compute.images.get(image_id)
  215. # The size should be greater then the ami size
  216. # and therefore, img.min_disk is used.
  217. lc.add_volume_device(
  218. is_root=True,
  219. source=img,
  220. size=img.min_disk if img and img.min_disk else 2,
  221. delete_on_terminate=True)
  222. # Add all available ephemeral devices
  223. instance_type_name = helpers.get_provider_test_data(
  224. self.provider,
  225. "instance_type")
  226. inst_type = self.provider.compute.instance_types.find(
  227. name=instance_type_name)[0]
  228. for _ in range(inst_type.num_ephemeral_disks):
  229. lc.add_ephemeral_device()
  230. net, subnet = helpers.create_test_network(self.provider, name)
  231. with helpers.cleanup_action(lambda:
  232. helpers.delete_test_network(net)):
  233. inst = helpers.create_test_instance(
  234. self.provider,
  235. name,
  236. subnet=subnet,
  237. launch_config=lc)
  238. with helpers.cleanup_action(lambda:
  239. helpers.delete_test_instance(
  240. inst)):
  241. try:
  242. inst.wait_till_ready()
  243. except WaitStateException as e:
  244. self.fail("The block device mapped launch did not "
  245. " complete successfully: %s" % e)
  246. # TODO: Check instance attachments and make sure they
  247. # correspond to requested mappings
  248. @helpers.skipIfNoService(['compute.instances', 'networking.networks',
  249. 'security.security_groups'])
  250. def test_instance_methods(self):
  251. name = "cb_instmethods-{0}".format(helpers.get_uuid())
  252. # Declare these variables and late binding will allow
  253. # the cleanup method access to the most current values
  254. test_inst = None
  255. net = None
  256. sg = None
  257. with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
  258. test_inst, net, sg)):
  259. net, subnet = helpers.create_test_network(self.provider, name)
  260. test_inst = helpers.get_test_instance(self.provider, name,
  261. subnet=subnet)
  262. sg = self.provider.security.security_groups.create(
  263. name=name, description=name, network_id=net.id)
  264. # Check adding a security group to a running instance
  265. test_inst.add_security_group(sg)
  266. test_inst.refresh()
  267. self.assertTrue(
  268. sg in test_inst.security_groups, "Expected security group '%s'"
  269. " to be among instance security_groups: [%s]" %
  270. (sg, test_inst.security_groups))
  271. # Check removing a security group from a running instance
  272. test_inst.remove_security_group(sg)
  273. test_inst.refresh()
  274. self.assertTrue(
  275. sg not in test_inst.security_groups, "Expected security group"
  276. " '%s' to be removed from instance security_groups: [%s]" %
  277. (sg, test_inst.security_groups))
  278. # check floating ips
  279. router = self.provider.networking.routers.create(name, net)
  280. gateway = None
  281. def cleanup_router():
  282. with helpers.cleanup_action(lambda: router.delete()):
  283. with helpers.cleanup_action(lambda: gateway.delete()):
  284. router.detach_subnet(subnet)
  285. router.detach_gateway(gateway)
  286. with helpers.cleanup_action(lambda: cleanup_router()):
  287. router.attach_subnet(subnet)
  288. gateway = (self.provider.networking.gateways
  289. .get_or_create_inet_gateway(name))
  290. router.attach_gateway(gateway)
  291. # check whether adding an elastic ip works
  292. fip = (self.provider.networking.networks
  293. .create_floating_ip())
  294. with helpers.cleanup_action(lambda: fip.delete()):
  295. test_inst.add_floating_ip(fip.public_ip)
  296. test_inst.refresh()
  297. self.assertIn(fip.public_ip, test_inst.public_ips)
  298. if isinstance(self.provider, TestMockHelperMixin):
  299. # TODO: Moto bug does not refresh removed public ip
  300. return
  301. # check whether removing an elastic ip works
  302. test_inst.remove_floating_ip(fip.public_ip)
  303. test_inst.refresh()
  304. self.assertNotIn(fip.public_ip, test_inst.public_ips)