From 5a67a556cd9ce07f220010630fad479b7975a8b0 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 20 Jul 2012 11:15:57 -0400 Subject: [PATCH] Script to migrate YAML users to the INI format, so we can deprecate YAML inventory (which we are now doing). --- examples/scripts/yaml_to_ini.py | 206 ++++++++++++++++++++++++++++++++ lib/ansible/inventory/ini.py | 1 + lib/ansible/inventory/yaml.py | 4 + lib/ansible/runner/__init__.py | 4 +- 4 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 examples/scripts/yaml_to_ini.py diff --git a/examples/scripts/yaml_to_ini.py b/examples/scripts/yaml_to_ini.py new file mode 100644 index 0000000000..5b97d14b16 --- /dev/null +++ b/examples/scripts/yaml_to_ini.py @@ -0,0 +1,206 @@ +# (c) 2012, Michael DeHaan +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +import ansible.constants as C +from ansible.inventory.host import Host +from ansible.inventory.group import Group +from ansible import errors +from ansible import utils +import os +import yaml +import sys + +class InventoryParserYaml(object): + ''' Host inventory parser for ansible ''' + + def __init__(self, filename=C.DEFAULT_HOST_LIST): + + sys.stderr.write("WARNING: YAML inventory files are deprecated in 0.6 and will be removed in 0.7, to migrate" + + " download and run https://github.com/ansible/ansible/blob/devel/examples/scripts/yaml_to_ini.py\n") + + fh = open(filename) + data = fh.read() + fh.close() + self._hosts = {} + self._parse(data) + + def _make_host(self, hostname): + + if hostname in self._hosts: + return self._hosts[hostname] + else: + host = Host(hostname) + self._hosts[hostname] = host + return host + + # see file 'test/yaml_hosts' for syntax + + def _parse(self, data): + # FIXME: refactor into subfunctions + + all = Group('all') + + ungrouped = Group('ungrouped') + all.add_child_group(ungrouped) + + self.groups = dict(all=all, ungrouped=ungrouped) + grouped_hosts = [] + + yaml = utils.parse_yaml(data) + + # first add all groups + for item in yaml: + if type(item) == dict and 'group' in item: + group = Group(item['group']) + + for subresult in item.get('hosts',[]): + + if type(subresult) in [ str, unicode ]: + host = self._make_host(subresult) + group.add_host(host) + grouped_hosts.append(host) + elif type(subresult) == dict: + host = self._make_host(subresult['host']) + vars = subresult.get('vars',{}) + if type(vars) == list: + for subitem in vars: + for (k,v) in subitem.items(): + host.set_variable(k,v) + elif type(vars) == dict: + for (k,v) in subresult.get('vars',{}).items(): + host.set_variable(k,v) + else: + raise errors.AnsibleError("unexpected type for variable") + group.add_host(host) + grouped_hosts.append(host) + + vars = item.get('vars',{}) + if type(vars) == dict: + for (k,v) in item.get('vars',{}).items(): + group.set_variable(k,v) + elif type(vars) == list: + for subitem in vars: + if type(subitem) != dict: + raise errors.AnsibleError("expected a dictionary") + for (k,v) in subitem.items(): + group.set_variable(k,v) + + self.groups[group.name] = group + all.add_child_group(group) + + # add host definitions + for item in yaml: + if type(item) in [ str, unicode ]: + host = self._make_host(item) + if host not in grouped_hosts: + ungrouped.add_host(host) + + elif type(item) == dict and 'host' in item: + host = self._make_host(item['host']) + + vars = item.get('vars', {}) + if type(vars)==list: + varlist, vars = vars, {} + for subitem in varlist: + vars.update(subitem) + for (k,v) in vars.items(): + host.set_variable(k,v) + + groups = item.get('groups', {}) + if type(groups) in [ str, unicode ]: + groups = [ groups ] + if type(groups)==list: + for subitem in groups: + if subitem in self.groups: + group = self.groups[subitem] + else: + group = Group(subitem) + self.groups[group.name] = group + all.add_child_group(group) + group.add_host(host) + grouped_hosts.append(host) + + if host not in grouped_hosts: + ungrouped.add_host(host) + + # make sure ungrouped.hosts is the complement of grouped_hosts + ungrouped_hosts = [host for host in ungrouped.hosts if host not in grouped_hosts] + +if __name__ == "__main__": + if len(sys.argv) != 2: + print "usage: yaml_to_ini.py /path/to/ansible/hosts" + sys.exit(1) + + result = "" + + original = sys.argv[1] + yamlp = InventoryParserYaml(filename=sys.argv[1]) + dirname = os.path.dirname(original) + + group_names = [ g.name for g in yamlp.groups.values() ] + + for group_name in sorted(group_names): + + record = yamlp.groups[group_name] + + if group_name == 'all': + continue + + hosts = record.hosts + result = result + "[%s]\n" % record.name + for h in hosts: + result = result + "%s\n" % h.name + result = result + "\n" + + groupfiledir = os.path.join(dirname, "group_vars") + if not os.path.exists(groupfiledir): + print "* creating: %s" % groupfiledir + os.makedirs(groupfiledir) + groupfile = os.path.join(groupfiledir, group_name) + print "* writing group variables for %s into %s" % (group_name, groupfile) + groupfh = open(groupfile, 'w') + groupfh.write(yaml.dump(record.get_variables())) + groupfh.close() + + for (host_name, host_record) in yamlp._hosts.iteritems(): + hostfiledir = os.path.join(dirname, "host_vars") + if not os.path.exists(hostfiledir): + print "* creating: %s" % hostfiledir + os.makedirs(hostfiledir) + hostfile = os.path.join(hostfiledir, host_record.name) + print "* writing host variables for %s into %s" % (host_record.name, hostfile) + hostfh = open(hostfile, 'w') + hostfh.write(yaml.dump(host_record.get_variables())) + hostfh.close() + + + # also need to keep a hash of variables per each host + # and variables per each group + # and write those to disk + + newfilepath = os.path.join(dirname, "hosts.new") + fdh = open(newfilepath, 'w') + fdh.write(result) + fdh.close() + + print "* COMPLETE: review your new inventory file and replace your original when ready" + print "* new inventory file saved as %s" % newfilepath + print "* edit group specific variables in %s/group_vars/" % dirname + print "* edit host specific variables in %s/host_vars/" % dirname + + # now need to write this to disk as (oldname).new + # and inform the user diff --git a/lib/ansible/inventory/ini.py b/lib/ansible/inventory/ini.py index d9718010e7..63260e3c3a 100644 --- a/lib/ansible/inventory/ini.py +++ b/lib/ansible/inventory/ini.py @@ -46,6 +46,7 @@ class InventoryParser(object): self._parse_group_children() self._parse_group_variables() return self.groups + # [webservers] # alpha diff --git a/lib/ansible/inventory/yaml.py b/lib/ansible/inventory/yaml.py index ddf9765340..afeaa40139 100644 --- a/lib/ansible/inventory/yaml.py +++ b/lib/ansible/inventory/yaml.py @@ -20,12 +20,16 @@ from ansible.inventory.host import Host from ansible.inventory.group import Group from ansible import errors from ansible import utils +import sys class InventoryParserYaml(object): ''' Host inventory parser for ansible ''' def __init__(self, filename=C.DEFAULT_HOST_LIST): + sys.stderr.write("WARNING: YAML inventory files are deprecated in 0.6 and will be removed in 0.7, to migrate" + + " download and run https://github.com/ansible/ansible/blob/devel/examples/scripts/yaml_to_ini.py\n") + fh = open(filename) data = fh.read() fh.close() diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 87017db26d..66405ca021 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -508,8 +508,8 @@ class Runner(object): if type(exec_rc) != ReturnData: raise Exception("unexpected return type: %s" % type(exec_rc)) # redundant, right? - #if not exec_rc.comm_ok: - # self.callbacks.on_unreachable(host, exec_rc.result) + if not exec_rc.comm_ok: + self.callbacks.on_unreachable(host, exec_rc.result) return exec_rc except errors.AnsibleError, ae: msg = str(ae)