import collections import inspect import mock import pytest import yaml from ansible.module_utils.six import string_types from ansible.modules.cloud.openstack import os_server class AnsibleFail(Exception): pass class AnsibleExit(Exception): pass def params_from_doc(func): '''This function extracts the docstring from the specified function, parses it as a YAML document, and returns parameters for the os_server module.''' doc = inspect.getdoc(func) cfg = yaml.load(doc) for task in cfg: for module, params in task.items(): for k, v in params.items(): if k in ['nics'] and isinstance(v, string_types): params[k] = [v] task[module] = collections.defaultdict(str, params) return cfg[0]['os_server'] class FakeCloud(object): ports = [ {'name': 'port1', 'id': '1234'}, {'name': 'port2', 'id': '4321'}, ] networks = [ {'name': 'network1', 'id': '5678'}, {'name': 'network2', 'id': '8765'}, ] images = [ {'name': 'cirros', 'id': '1'}, {'name': 'fedora', 'id': '2'}, ] flavors = [ {'name': 'm1.small', 'id': '1', 'flavor_ram': 1024}, {'name': 'm1.tiny', 'id': '2', 'flavor_ram': 512}, ] def _find(self, source, name): for item in source: if item['name'] == name or item['id'] == name: return item def get_image_id(self, name, exclude=None): image = self._find(self.images, name) if image: return image['id'] def get_flavor(self, name): return self._find(self.flavors, name) def get_flavor_by_ram(self, ram, include=None): for flavor in self.flavors: if flavor['ram'] >= ram and (include is None or include in flavor['name']): return flavor def get_port(self, name): return self._find(self.ports, name) def get_network(self, name): return self._find(self.networks, name) def get_openstack_vars(self, server): return server create_server = mock.MagicMock() class TestNetworkArgs(object): '''This class exercises the _network_args function of the os_server module. For each test, we parse the YAML document contained in the docstring to retrieve the module parameters for the test.''' def setup_method(self, method): self.cloud = FakeCloud() self.module = mock.MagicMock() self.module.params = params_from_doc(method) def test_nics_string_net_id(self): ''' - os_server: nics: net-id=1234 ''' args = os_server._network_args(self.module, self.cloud) assert(args[0]['net-id'] == '1234') def test_nics_string_net_id_list(self): ''' - os_server: nics: net-id=1234,net-id=4321 ''' args = os_server._network_args(self.module, self.cloud) assert(args[0]['net-id'] == '1234') assert(args[1]['net-id'] == '4321') def test_nics_string_port_id(self): ''' - os_server: nics: port-id=1234 ''' args = os_server._network_args(self.module, self.cloud) assert(args[0]['port-id'] == '1234') def test_nics_string_net_name(self): ''' - os_server: nics: net-name=network1 ''' args = os_server._network_args(self.module, self.cloud) assert(args[0]['net-id'] == '5678') def test_nics_string_port_name(self): ''' - os_server: nics: port-name=port1 ''' args = os_server._network_args(self.module, self.cloud) assert(args[0]['port-id'] == '1234') def test_nics_structured_net_id(self): ''' - os_server: nics: - net-id: '1234' ''' args = os_server._network_args(self.module, self.cloud) assert(args[0]['net-id'] == '1234') def test_nics_structured_mixed(self): ''' - os_server: nics: - net-id: '1234' - port-name: port1 - 'net-name=network1,port-id=4321' ''' args = os_server._network_args(self.module, self.cloud) assert(args[0]['net-id'] == '1234') assert(args[1]['port-id'] == '1234') assert(args[2]['net-id'] == '5678') assert(args[3]['port-id'] == '4321') class TestCreateServer(object): def setup_method(self, method): self.cloud = FakeCloud() self.module = mock.MagicMock() self.module.params = params_from_doc(method) self.module.fail_json.side_effect = AnsibleFail() self.module.exit_json.side_effect = AnsibleExit() self.meta = mock.MagicMock() self.meta.gett_hostvars_from_server.return_value = { 'id': '1234' } os_server.meta = self.meta def test_create_server(self): ''' - os_server: image: cirros flavor: m1.tiny nics: - net-name: network1 meta: - key: value ''' with pytest.raises(AnsibleExit): os_server._create_server(self.module, self.cloud) assert(self.cloud.create_server.call_count == 1) assert(self.cloud.create_server.call_args[1]['image'] == self.cloud.get_image_id('cirros')) assert(self.cloud.create_server.call_args[1]['flavor'] == self.cloud.get_flavor('m1.tiny')['id']) assert(self.cloud.create_server.call_args[1]['nics'][0]['net-id'] == self.cloud.get_network('network1')['id']) def test_create_server_bad_flavor(self): ''' - os_server: image: cirros flavor: missing_flavor nics: - net-name: network1 ''' with pytest.raises(AnsibleFail): os_server._create_server(self.module, self.cloud) assert('missing_flavor' in self.module.fail_json.call_args[1]['msg']) def test_create_server_bad_nic(self): ''' - os_server: image: cirros flavor: m1.tiny nics: - net-name: missing_network ''' with pytest.raises(AnsibleFail): os_server._create_server(self.module, self.cloud) assert('missing_network' in self.module.fail_json.call_args[1]['msg'])