test_openstack.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. """
  2. :codeauthor: `Tyler Johnson <tjohnson@saltstack.com>`
  3. tests.unit.cloud.clouds.openstack_test
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  5. """
  6. from salt.cloud.clouds import openstack
  7. from salt.utils import dictupdate
  8. from tests.support.mixins import LoaderModuleMockMixin
  9. from tests.support.mock import MagicMock, patch
  10. from tests.support.unit import TestCase
  11. # pylint: disable=confusing-with-statement
  12. class MockImage:
  13. name = "image name"
  14. id = "image id"
  15. class MockNode:
  16. name = "node name"
  17. id = "node id"
  18. flavor = MockImage()
  19. status = "node status"
  20. def __init__(self, image):
  21. self.image = image
  22. def __iter__(self):
  23. return iter(())
  24. class MockConn:
  25. def __init__(self, image):
  26. self.node = MockNode(image)
  27. def get_image(self, *args, **kwargs):
  28. return self.node.image
  29. def get_flavor(self, *args, **kwargs):
  30. return self.node.flavor
  31. def get_server(self, *args, **kwargs):
  32. return self.node
  33. def list_servers(self, *args, **kwargs):
  34. return [self.node]
  35. class OpenstackTestCase(TestCase, LoaderModuleMockMixin):
  36. """
  37. Unit TestCase for salt.cloud.clouds.openstack module.
  38. """
  39. def setup_loader_modules(self):
  40. return {
  41. openstack: {
  42. "__active_provider_name__": "",
  43. "__opts__": {
  44. "providers": {
  45. "my-openstack-cloud": {
  46. "openstack": {
  47. "auth": "daenerys",
  48. "region_name": "westeros",
  49. "cloud": "openstack",
  50. }
  51. }
  52. }
  53. },
  54. }
  55. }
  56. def test_get_configured_provider_bad(self):
  57. with patch.dict(openstack.__opts__, {"providers": {}}):
  58. result = openstack.get_configured_provider()
  59. self.assertEqual(result, False)
  60. def test_get_configured_provider_auth(self):
  61. config = {
  62. "region_name": "westeros",
  63. "auth": "daenerys",
  64. }
  65. with patch.dict(
  66. openstack.__opts__,
  67. {"providers": {"my-openstack-cloud": {"openstack": config}}},
  68. ):
  69. result = openstack.get_configured_provider()
  70. self.assertEqual(config, result)
  71. def test_get_configured_provider_cloud(self):
  72. config = {
  73. "region_name": "westeros",
  74. "cloud": "foo",
  75. }
  76. with patch.dict(
  77. openstack.__opts__,
  78. {"providers": {"my-openstack-cloud": {"openstack": config}}},
  79. ):
  80. result = openstack.get_configured_provider()
  81. self.assertEqual(config, result)
  82. def test_get_dependencies(self):
  83. HAS_SHADE = (True, "Please install newer version of shade: >= 1.19.0")
  84. with patch("salt.cloud.clouds.openstack.HAS_SHADE", HAS_SHADE):
  85. result = openstack.get_dependencies()
  86. self.assertEqual(result, True)
  87. def test_get_dependencies_no_shade(self):
  88. HAS_SHADE = (False, "Install pypi module shade >= 1.19.0")
  89. with patch("salt.cloud.clouds.openstack.HAS_SHADE", HAS_SHADE):
  90. result = openstack.get_dependencies()
  91. self.assertEqual(result, False)
  92. def test_list_nodes_full_image_str(self):
  93. node_image = "node image"
  94. conn = MockConn(node_image)
  95. with patch("salt.cloud.clouds.openstack._get_ips", return_value=[]):
  96. ret = openstack.list_nodes_full(conn=conn)
  97. self.assertEqual(ret[conn.node.name]["image"], node_image)
  98. def test_list_nodes_full_image_obj(self):
  99. conn = MockConn(MockImage())
  100. with patch("salt.cloud.clouds.openstack._get_ips", return_value=[]):
  101. ret = openstack.list_nodes_full(conn=conn)
  102. self.assertEqual(ret[conn.node.name]["image"], MockImage.name)
  103. def test_show_instance(self):
  104. conn = MockConn(MockImage())
  105. with patch("salt.cloud.clouds.openstack._get_ips", return_value=[]):
  106. ret = openstack.show_instance(conn.node.name, conn=conn, call="action")
  107. self.assertEqual(ret["image"], MockImage.name)
  108. def test_request_instance_should_use_provided_connection_if_not_None(self):
  109. fake_conn = MagicMock()
  110. patch_get_conn = patch("salt.cloud.clouds.openstack.get_conn", autospec=True)
  111. patch_utils = patch.dict(
  112. openstack.__utils__,
  113. {"cloud.check_name": MagicMock(), "dictupdate.update": dictupdate.update},
  114. )
  115. patch_shade = patch.object(
  116. openstack, "shade.exc.OpenStackCloudException", Exception, create=True
  117. )
  118. with patch_get_conn as fake_get_conn, patch_utils, patch_shade:
  119. openstack.request_instance(
  120. vm_={"name": "fnord", "driver": "fnord"}, conn=fake_conn
  121. )
  122. fake_get_conn.assert_not_called()
  123. def test_request_instance_should_create_conn_if_provided_is_None(self):
  124. none_conn = None
  125. patch_get_conn = patch("salt.cloud.clouds.openstack.get_conn", autospec=True)
  126. patch_utils = patch.dict(
  127. openstack.__utils__,
  128. {"cloud.check_name": MagicMock(), "dictupdate.update": dictupdate.update},
  129. )
  130. patch_shade = patch.object(
  131. openstack, "shade.exc.OpenStackCloudException", Exception, create=True
  132. )
  133. with patch_get_conn as fake_get_conn, patch_utils, patch_shade:
  134. openstack.request_instance(
  135. vm_={"name": "fnord", "driver": "fnord"}, conn=none_conn
  136. )
  137. fake_get_conn.assert_called_once_with()
  138. # According to
  139. # https://docs.openstack.org/shade/latest/user/usage.html#shade.OpenStackCloud.create_server
  140. # the `network` parameter can be:
  141. # (optional) Network dict or name or ID to attach the server to.
  142. # Mutually exclusive with the nics parameter. Can also be be a list of
  143. # network names or IDs or network dicts.
  144. #
  145. # Here we're testing a normal dictionary
  146. def test_request_instance_should_be_able_to_provide_a_dictionary_for_network(self):
  147. fake_conn = MagicMock()
  148. expected_network = {"foo": "bar"}
  149. vm_ = {"name": "fnord", "driver": "fnord", "network": expected_network}
  150. patch_utils = patch.dict(
  151. openstack.__utils__,
  152. {"cloud.check_name": MagicMock(), "dictupdate.update": dictupdate.update},
  153. )
  154. with patch_utils:
  155. openstack.request_instance(vm_=vm_, conn=fake_conn)
  156. call_kwargs = fake_conn.create_server.mock_calls[0][-1]
  157. self.assertDictEqual(call_kwargs["network"], expected_network)
  158. # Here we're testing the list of dictionaries
  159. def test_request_instance_should_be_able_to_provide_a_list_of_dictionaries_for_network(
  160. self,
  161. ):
  162. fake_conn = MagicMock()
  163. expected_network = [{"foo": "bar"}, {"bang": "quux"}]
  164. vm_ = {"name": "fnord", "driver": "fnord", "network": expected_network}
  165. patch_utils = patch.dict(
  166. openstack.__utils__,
  167. {"cloud.check_name": MagicMock(), "dictupdate.update": dictupdate.update},
  168. )
  169. with patch_utils:
  170. openstack.request_instance(vm_=vm_, conn=fake_conn)
  171. call_kwargs = fake_conn.create_server.mock_calls[0][-1]
  172. assert call_kwargs["network"] == expected_network
  173. # Here we're testing for names/IDs
  174. def test_request_instance_should_be_able_to_provide_a_list_of_single_ids_or_names_for_network(
  175. self,
  176. ):
  177. fake_conn = MagicMock()
  178. expected_network = ["foo", "bar", "bang", "fnord1", "fnord2"]
  179. vm_ = {"name": "fnord", "driver": "fnord", "network": expected_network}
  180. patch_utils = patch.dict(
  181. openstack.__utils__,
  182. {"cloud.check_name": MagicMock(), "dictupdate.update": dictupdate.update},
  183. )
  184. with patch_utils:
  185. openstack.request_instance(vm_=vm_, conn=fake_conn)
  186. call_kwargs = fake_conn.create_server.mock_calls[0][-1]
  187. assert call_kwargs["network"] == expected_network
  188. # Testing that we get a dict that we expect for create_server
  189. def test__clean_create_kwargs(self):
  190. params = {
  191. "name": "elmer",
  192. "image": "mirrormirror",
  193. "flavor": "chocolate",
  194. "auto_ip": True,
  195. "ips": ["hihicats"],
  196. "ip_pool": "olympic",
  197. "root_volume": "iamgroot",
  198. "boot_volume": "pussnboots",
  199. "terminate_volume": False,
  200. "volumes": ["lots", "of", "books"],
  201. "meta": {"full": "meta"},
  202. "files": {"shred": "this"},
  203. "reservation_id": "licenseandregistration",
  204. "security_groups": ["wanna", "play", "repeat"],
  205. "key_name": "clortho",
  206. "availability_zone": "callmemaybe",
  207. "block_device_mapping": [{"listof": "dicts"}],
  208. "block_device_mapping_v2": [{"listof": "dicts"}],
  209. "nics": ["thats", "me"],
  210. "scheduler_hints": {"so": "many"},
  211. "config_drive": True,
  212. "disk_config": "donkey",
  213. "admin_pass": "password",
  214. "wait": False,
  215. "timeout": 30,
  216. "reuse_ips": True,
  217. "network": ["also", "a", "dict"],
  218. "boot_from_volume": True,
  219. "volume_size": 30,
  220. "nat_destination": "albuquerque",
  221. "group": "ledzeppelin",
  222. "userdata": "needmoreinput",
  223. "thisgetsdropped": "yup",
  224. }
  225. patch_utils = patch.dict(
  226. openstack.__utils__, {"dictupdate.update": dictupdate.update},
  227. )
  228. with patch_utils:
  229. ret = openstack._clean_create_kwargs(**params)
  230. params.pop("thisgetsdropped")
  231. self.assertDictEqual(params, ret)