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()
+