From 3a24aa9a70cf625aa9c9eff545f31c4fafeaa7bf Mon Sep 17 00:00:00 2001 From: Jeroen Hoekx Date: Sat, 14 Apr 2012 15:45:24 +0200 Subject: [PATCH] Add YAML inventory format. See test/yaml_hosts for an example. Hosts can be part of multiple groups. Groups can also have variables, inherited by the hosts. There is no variable scope, last variable seen wins. --- lib/ansible/inventory.py | 72 ++++++++++++++++++++++++++++++++++++++ test/TestInventory.py | 74 ++++++++++++++++++++++++++++++++++++++++ test/yaml_hosts | 26 ++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 test/yaml_hosts diff --git a/lib/ansible/inventory.py b/lib/ansible/inventory.py index 3f97460e51..c3678d64a5 100644 --- a/lib/ansible/inventory.py +++ b/lib/ansible/inventory.py @@ -97,6 +97,8 @@ class Inventory(object): results = [] groups = dict(ungrouped=[]) lines = file(self.inventory_file).read().split("\n") + if "---" in lines: + return self._parse_yaml() group_name = 'ungrouped' for item in lines: item = item.lstrip().rstrip() @@ -154,6 +156,76 @@ class Inventory(object): # ***************************************************** + def _parse_yaml(self): + """ Load the inventory from a yaml file. + + returns hosts and groups""" + data = utils.parse_yaml_from_file(self.inventory_file) + + if type(data) != list: + raise errors.AnsibleError("YAML inventory should be a list.") + + hosts = [] + groups = {} + + for item in data: + if type(item) == dict: + if "group" in item: + group_name = item["group"] + + group_vars = [] + if "vars" in item: + group_vars = item["vars"] + + group_hosts = [] + if "hosts" in item: + for host in item["hosts"]: + host_name = self._parse_yaml_host(host, group_vars) + group_hosts.append(host_name) + + groups[group_name] = group_hosts + hosts.extend(group_hosts) + + # or a host + elif "host" in item: + host_name = self._parse_yaml_host(item) + hosts.append(host_name) + else: + host_name = self._parse_yaml_host(item) + hosts.append(host_name) + + # filter duplicate hosts + output_hosts = [] + for host in hosts: + if host not in output_hosts: + output_hosts.append(host) + + return output_hosts, groups + + def _parse_yaml_host(self, item, variables=[]): + def set_variables(host, variables): + for variable in variables: + if len(variable) != 1: + raise AnsibleError("Only one item expected in %s"%(variable)) + k, v = variable.items()[0] + self._set_variable(host, k, v) + + if type(item) in [str, unicode]: + set_variables(item, variables) + return item + elif type(item) == dict: + if "host" in item: + host_name = item["host"] + set_variables(host_name, variables) + + if "vars" in item: + set_variables(host_name, item["vars"]) + + return host_name + else: + raise AnsibleError("Unknown item in inventory: %s"%(item)) + + def _get_variables_from_script(self, host, extra_vars=None): ''' support per system variabes from external variable scripts, see web docs ''' diff --git a/test/TestInventory.py b/test/TestInventory.py index 2f86f50728..a0b0b74d16 100644 --- a/test/TestInventory.py +++ b/test/TestInventory.py @@ -12,6 +12,7 @@ class TestInventory(unittest.TestCase): self.inventory_file = os.path.join(self.test_dir, 'simple_hosts') self.inventory_script = os.path.join(self.test_dir, 'inventory_api.py') + self.inventory_yaml = os.path.join(self.test_dir, 'yaml_hosts') os.chmod(self.inventory_script, 0755) @@ -26,6 +27,9 @@ class TestInventory(unittest.TestCase): def script_inventory(self): return Inventory( self.inventory_script ) + def yaml_inventory(self): + return Inventory( self.inventory_yaml ) + def test_simple(self): inventory = self.simple_inventory() hosts = inventory.list_hosts() @@ -149,6 +153,76 @@ class TestInventory(unittest.TestCase): assert vars == {"hammer":True, "simple": "yes"} + ### Tests for yaml inventory file + + def test_yaml(self): + inventory = self.yaml_inventory() + hosts = inventory.list_hosts() + print hosts + expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki'] + assert hosts == expected_hosts + + def test_yaml_all(self): + inventory = self.yaml_inventory() + hosts = inventory.list_hosts('all') + + expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki'] + assert hosts == expected_hosts + + def test_yaml_norse(self): + inventory = self.yaml_inventory() + hosts = inventory.list_hosts("norse") + + expected_hosts=['thor', 'odin', 'loki'] + assert hosts == expected_hosts + + def test_yaml_combined(self): + inventory = self.yaml_inventory() + hosts = inventory.list_hosts("norse:greek") + + expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki'] + assert hosts == expected_hosts + + def test_yaml_restrict(self): + inventory = self.yaml_inventory() + + restricted_hosts = ['hera', 'poseidon', 'thor'] + expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki'] + + inventory.restrict_to(restricted_hosts) + hosts = inventory.list_hosts("norse:greek") + + assert hosts == restricted_hosts + + inventory.lift_restriction() + hosts = inventory.list_hosts("norse:greek") + + assert hosts == expected_hosts + + def test_yaml_vars(self): + inventory = self.yaml_inventory() + vars = inventory.get_variables('thor') + + assert vars == {"hammer":True} + + def test_yaml_host_vars(self): + inventory = self.yaml_inventory() + vars = inventory.get_variables('saturn') + + assert vars == {"moon":"titan"} + + def test_yaml_extra_vars(self): + inventory = self.yaml_inventory() + vars = inventory.get_variables('thor', 'a=5') + + assert vars == {"hammer":True} + + def test_yaml_port(self): + inventory = self.yaml_inventory() + vars = inventory.get_variables('hera') + + assert vars == {'ansible_ssh_port': 3000} + ### Test Runner class method def test_class_method(self): diff --git a/test/yaml_hosts b/test/yaml_hosts new file mode 100644 index 0000000000..95f278b91a --- /dev/null +++ b/test/yaml_hosts @@ -0,0 +1,26 @@ +--- + +- jupiter +- host: saturn + vars: + - moon: titan + +- group: greek + hosts: + - zeus + - hera + - poseidon + vars: + - ansible_ssh_port: 3000 + +- group: norse + hosts: + - host: thor + vars: + - hammer: True + - odin + - loki + +- group: multiple + hosts: + - saturn