From b4dfcc2d286d87de8a030cf986e0d3bb0e3f3255 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Mon, 17 Nov 2014 19:34:56 -0800 Subject: [PATCH] Start laying out how the pieces of code that parse inventory information into ansible will work --- v2/ansible/plugins/inventory/__init__.py | 59 ++++++++++++++++++++++ v2/ansible/plugins/inventory/aggregate.py | 61 +++++++++++++++++++++++ v2/ansible/plugins/inventory/directory.py | 52 +++++++++++++++++++ v2/ansible/plugins/inventory/ini.py | 53 ++++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 v2/ansible/plugins/inventory/aggregate.py create mode 100644 v2/ansible/plugins/inventory/directory.py create mode 100644 v2/ansible/plugins/inventory/ini.py diff --git a/v2/ansible/plugins/inventory/__init__.py b/v2/ansible/plugins/inventory/__init__.py index 785fc459921..41e8578ee70 100644 --- a/v2/ansible/plugins/inventory/__init__.py +++ b/v2/ansible/plugins/inventory/__init__.py @@ -15,7 +15,66 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . +############################################# + # Make coding more python3-ish from __future__ import (absolute_import, division, print_function) __metaclass__ = type +from abc import ABCMeta, abstractmethod + +class InventoryParser: + '''Abstract Base Class for retrieving inventory information + + Any InventoryParser functions by taking an inven_source. The caller then + calls the parser() method. Once parser is called, the caller can access + InventoryParser.hosts for a mapping of Host objects and + InventoryParser.Groups for a mapping of Group objects. + ''' + __metaclass__ = ABCMeta + + def __init__(self, inven_source): + ''' + InventoryParser contructors take a source of inventory information + that they will parse the host and group information from. + ''' + self.inven_source = inven_source + self.reset_parser() + + @abstractmethod + def reset_parser(self): + ''' + InventoryParsers generally cache their data once parser() is + called. This method initializes any parser state before calling parser + again. + ''' + self.hosts = dict() + self.groups = dict() + self.parsed = False + + def _merge(self, target, addition): + ''' + This method is provided to InventoryParsers to merge host or group + dicts since it may take several passes to get all of the data + + Example usage: + self.hosts = self.from_ini(filename) + new_hosts = self.from_script(scriptname) + self._merge(self.hosts, new_hosts) + ''' + for i in addition: + if i in target: + target[i].merge(addition[i]) + else: + target[i] = addition[i] + + @abstractmethod + def parse(self, refresh=False): + if refresh: + self.reset_parser() + if self.parsed: + return self.parsed + + # Parse self.inven_sources here + pass + diff --git a/v2/ansible/plugins/inventory/aggregate.py b/v2/ansible/plugins/inventory/aggregate.py new file mode 100644 index 00000000000..6bdf2ddcb67 --- /dev/null +++ b/v2/ansible/plugins/inventory/aggregate.py @@ -0,0 +1,61 @@ +# (c) 2012-2014, 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 . + +############################################# + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os + +from . import InventoryParser +#from . ini import InventoryIniParser +#from . script import InventoryScriptParser + +class InventoryAggregateParser(InventoryParser): + + def __init__(self, inven_sources): + self.inven_source = inven_sources + self.hosts = dict() + self.groups = dict() + + def reset_parser(self): + super(InventoryAggregateParser, self).reset_parser() + + def parse(self, refresh=False): + # InventoryDirectoryParser is a InventoryAggregateParser so we avoid + # a circular import by importing here + from . directory import InventoryAggregateParser + if super(InventoryAggregateParser, self).parse(refresh): + return self.parsed + + for entry in self.inven_sources: + if os.path.sep in entry: + # file or directory + if os.path.isdir(entry): + parser = directory.InventoryDirectoryParser(filename=entry) + elif utils.is_executable(entry): + parser = InventoryScriptParser(filename=entry) + else: + parser = InventoryIniParser(filename=entry) + else: + # hostname + parser = HostnameParser(hostname=entry) + hosts, groups = parser.parse() + self._merge(self.hosts, hosts) + self._merge(self.groups, groups) diff --git a/v2/ansible/plugins/inventory/directory.py b/v2/ansible/plugins/inventory/directory.py new file mode 100644 index 00000000000..d340ed75387 --- /dev/null +++ b/v2/ansible/plugins/inventory/directory.py @@ -0,0 +1,52 @@ +# (c) 2012-2014, 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 . + +############################################# + +# Make coding more python3-ish +from __future__ import (division, print_function) +__metaclass__ = type + +import os + +from . aggregate import InventoryAggregateParser + +class InventoryDirectoryParser(InventoryAggregateParser): + + def __init__(self, inven_directory): + directory = inven_directory + names = os.listdir(inven_directory) + filtered_names = [] + + # Clean up the list of filenames + for filename in names: + # Skip files that end with certain extensions or characters + if any(filename.endswith(ext) for ext in ("~", ".orig", ".bak", ".ini", ".retry", ".pyc", ".pyo")): + continue + # Skip hidden files + if filename.startswith('.') and not filename.startswith('.{0}'.format(os.path.sep)): + continue + # These are things inside of an inventory basedir + if filename in ("host_vars", "group_vars", "vars_plugins"): + continue + fullpath = os.path.join(directory, filename) + new_names.append(fullpath) + + super(InventoryDirectoryParser, self).__init__(new_names) + + def parse(self): + return super(InventoryDirectoryParser, self).parse() diff --git a/v2/ansible/plugins/inventory/ini.py b/v2/ansible/plugins/inventory/ini.py new file mode 100644 index 00000000000..2cc062b9596 --- /dev/null +++ b/v2/ansible/plugins/inventory/ini.py @@ -0,0 +1,53 @@ +# (c) 2012-2014, 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 . + +############################################# + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os + +from . import InventoryParser + +class InventoryIniParser(InventoryAggregateParser): + + def __init__(self, inven_directory): + directory = inven_directory + names = os.listdir(inven_directory) + filtered_names = [] + + # Clean up the list of filenames + for filename in names: + # Skip files that end with certain extensions or characters + if any(filename.endswith(ext) for ext in ("~", ".orig", ".bak", ".ini", ".retry", ".pyc", ".pyo")): + continue + # Skip hidden files + if filename.startswith('.') and not filename.startswith('.{0}'.format(os.path.sep)): + continue + # These are things inside of an inventory basedir + if filename in ("host_vars", "group_vars", "vars_plugins"): + continue + fullpath = os.path.join(directory, filename) + new_names.append(fullpath) + + super(InventoryDirectoryParser, self).__init__(new_names) + + def parse(self): + return super(InventoryDirectoryParser, self).parse() +