diff --git a/plugins/inventory/ovirt.ini b/plugins/inventory/ovirt.ini
new file mode 100644
index 0000000000..a52f9d63ff
--- /dev/null
+++ b/plugins/inventory/ovirt.ini
@@ -0,0 +1,33 @@
+# Copyright 2013 Google Inc.
+#
+# 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 .
+
+
+# Author: Josha Inglis based on the gce.ini by Eric Johnson
+
+[ovirt]
+# ovirt Service Account configuration information can be stored in the
+# libcloud 'secrets.py' file. Ideally, the 'secrets.py' file will already
+# exist in your PYTHONPATH and be picked up automatically with an import
+# statement in the inventory script. However, you can specify an absolute
+# path to the secrets.py file with 'libcloud_secrets' parameter.
+ovirt_api_secrets =
+
+# If you are not going to use a 'secrets.py' file, you can set the necessary
+# authorization parameters here.
+ovirt_url =
+ovirt_username =
+ovirt_password =
diff --git a/plugins/inventory/ovirt.py b/plugins/inventory/ovirt.py
new file mode 100755
index 0000000000..4cb4b09eae
--- /dev/null
+++ b/plugins/inventory/ovirt.py
@@ -0,0 +1,287 @@
+#!/usr/bin/env python
+# Copyright 2015 IIX Inc.
+#
+# 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 .
+
+"""
+ovirt external inventory script
+=================================
+
+Generates inventory that Ansible can understand by making API requests to
+oVirt via the ovirt-engine-sdk-python library.
+
+When run against a specific host, this script returns the following variables
+based on the data obtained from the ovirt_sdk Node object:
+ - ovirt_uuid
+ - ovirt_id
+ - ovirt_image
+ - ovirt_machine_type
+ - ovirt_ips
+ - ovirt_name
+ - ovirt_description
+ - ovirt_status
+ - ovirt_zone
+ - ovirt_tags
+ - ovirt_stats
+
+When run in --list mode, instances are grouped by the following categories:
+
+ - zone:
+ zone group name.
+ - instance tags:
+ An entry is created for each tag. For example, if you have two instances
+ with a common tag called 'foo', they will both be grouped together under
+ the 'tag_foo' name.
+ - network name:
+ the name of the network is appended to 'network_' (e.g. the 'default'
+ network will result in a group named 'network_default')
+ - running status:
+ group name prefixed with 'status_' (e.g. status_up, status_down,..)
+
+Examples:
+ Execute uname on all instances in the us-central1-a zone
+ $ ansible -i ovirt.py us-central1-a -m shell -a "/bin/uname -a"
+
+ Use the ovirt inventory script to print out instance specific information
+ $ plugins/inventory/ovirt.py --host my_instance
+
+Author: Josha Inglis based on the gce.py by Eric Johnson
+Version: 0.0.1
+"""
+
+USER_AGENT_PRODUCT = "Ansible-ovirt_inventory_plugin"
+USER_AGENT_VERSION = "v1"
+
+import sys
+import os
+import argparse
+import ConfigParser
+from collections import defaultdict
+
+try:
+ import json
+except ImportError:
+ # noinspection PyUnresolvedReferences,PyPackageRequirements
+ import simplejson as json
+
+try:
+ # noinspection PyUnresolvedReferences
+ from ovirtsdk.api import API
+ # noinspection PyUnresolvedReferences
+ from ovirtsdk.xml import params
+except ImportError:
+ print("ovirt inventory script requires ovirt-engine-sdk-python")
+ sys.exit(1)
+
+
+class OVirtInventory(object):
+ def __init__(self):
+ # Read settings and parse CLI arguments
+ self.args = self.parse_cli_args()
+ self.driver = self.get_ovirt_driver()
+
+ # Just display data for specific host
+ if self.args.host:
+ print self.json_format_dict(
+ self.node_to_dict(self.get_instance(self.args.host)),
+ pretty=self.args.pretty
+ )
+ sys.exit(0)
+
+ # Otherwise, assume user wants all instances grouped
+ print(
+ self.json_format_dict(
+ data=self.group_instances(),
+ pretty=self.args.pretty
+ )
+ )
+ sys.exit(0)
+
+ @staticmethod
+ def get_ovirt_driver():
+ """
+ Determine the ovirt authorization settings and return a ovirt_sdk driver.
+
+ :rtype : ovirtsdk.api.API
+ """
+ kwargs = {}
+
+ ovirt_ini_default_path = os.path.join(
+ os.path.dirname(os.path.realpath(__file__)), "ovirt.ini")
+ ovirt_ini_path = os.environ.get('OVIRT_INI_PATH', ovirt_ini_default_path)
+
+ # Create a ConfigParser.
+ # This provides empty defaults to each key, so that environment
+ # variable configuration (as opposed to INI configuration) is able
+ # to work.
+ config = ConfigParser.SafeConfigParser(defaults={
+ 'ovirt_url': '',
+ 'ovirt_username': '',
+ 'ovirt_password': '',
+ 'ovirt_api_secrets': '',
+ })
+ if 'ovirt' not in config.sections():
+ config.add_section('ovirt')
+ config.read(ovirt_ini_path)
+
+ # Attempt to get ovirt params from a configuration file, if one
+ # exists.
+ secrets_path = config.get('ovirt', 'ovirt_api_secrets')
+ secrets_found = False
+ try:
+ # noinspection PyUnresolvedReferences,PyPackageRequirements
+ import secrets
+
+ kwargs = getattr(secrets, 'OVIRT_KEYWORD_PARAMS', {})
+ secrets_found = True
+ except ImportError:
+ pass
+
+ if not secrets_found and secrets_path:
+ if not secrets_path.endswith('secrets.py'):
+ err = "Must specify ovirt_sdk secrets file as /absolute/path/to/secrets.py"
+ print(err)
+ sys.exit(1)
+ sys.path.append(os.path.dirname(secrets_path))
+ try:
+ # noinspection PyUnresolvedReferences,PyPackageRequirements
+ import secrets
+
+ kwargs = getattr(secrets, 'OVIRT_KEYWORD_PARAMS', {})
+ except ImportError:
+ pass
+ if not secrets_found:
+ kwargs = {
+ 'url': config.get('ovirt', 'ovirt_url'),
+ 'username': config.get('ovirt', 'ovirt_username'),
+ 'password': config.get('ovirt', 'ovirt_password'),
+ }
+
+ # If the appropriate environment variables are set, they override
+ # other configuration; process those into our args and kwargs.
+ kwargs['url'] = os.environ.get('OVIRT_URL')
+ kwargs['username'] = os.environ.get('OVIRT_EMAIL')
+ kwargs['password'] = os.environ.get('OVIRT_PASS')
+
+ # Retrieve and return the ovirt driver.
+ return API(insecure=True, **kwargs)
+
+ @staticmethod
+ def parse_cli_args():
+ """
+ Command line argument processing
+
+ :rtype : argparse.Namespace
+ """
+
+ parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on ovirt')
+ parser.add_argument('--list', action='store_true', default=True, help='List instances (default: True)')
+ parser.add_argument('--host', action='store', help='Get all information about an instance')
+ parser.add_argument('--pretty', action='store_true', default=False, help='Pretty format (default: False)')
+ return parser.parse_args()
+
+ def node_to_dict(self, inst):
+ """
+ :type inst: params.VM
+ """
+ if inst is None:
+ return {}
+
+ inst.get_custom_properties()
+ ips = [ip.get_address() for ip in inst.get_guest_info().get_ips().get_ip()] \
+ if inst.get_guest_info() is not None else []
+ stats = {}
+ for stat in inst.get_statistics().list():
+ stats[stat.get_name()] = stat.get_values().get_value()[0].get_datum()
+
+ return {
+ 'ovirt_uuid': inst.get_id(),
+ 'ovirt_id': inst.get_id(),
+ 'ovirt_image': inst.get_os().get_type(),
+ 'ovirt_machine_type': inst.get_instance_type(),
+ 'ovirt_ips': ips,
+ 'ovirt_name': inst.get_name(),
+ 'ovirt_description': inst.get_description(),
+ 'ovirt_status': inst.get_status().get_state(),
+ 'ovirt_zone': inst.get_cluster().get_id(),
+ 'ovirt_tags': self.get_tags(inst),
+ 'ovirt_stats': stats,
+ # Hosts don't have a public name, so we add an IP
+ 'ansible_ssh_host': ips[0] if len(ips) > 0 else None
+ }
+
+ @staticmethod
+ def get_tags(inst):
+ """
+ :type inst: params.VM
+ """
+ return [x.get_name() for x in inst.get_tags().list()]
+
+ # noinspection PyBroadException,PyUnusedLocal
+ def get_instance(self, instance_name):
+ """Gets details about a specific instance """
+ try:
+ return self.driver.vms.get(name=instance_name)
+ except Exception as e:
+ return None
+
+ def group_instances(self):
+ """Group all instances"""
+ groups = defaultdict(list)
+ meta = {"hostvars": {}}
+
+ for node in self.driver.vms.list():
+ assert isinstance(node, params.VM)
+ name = node.get_name()
+
+ meta["hostvars"][name] = self.node_to_dict(node)
+
+ zone = node.get_cluster().get_name()
+ groups[zone].append(name)
+
+ tags = self.get_tags(node)
+ for t in tags:
+ tag = 'tag_%s' % t
+ groups[tag].append(name)
+
+ nets = [x.get_name() for x in node.get_nics().list()]
+ for net in nets:
+ net = 'network_%s' % net
+ groups[net].append(name)
+
+ status = node.get_status().get_state()
+ stat = 'status_%s' % status.lower()
+ if stat in groups:
+ groups[stat].append(name)
+ else:
+ groups[stat] = [name]
+
+ groups["_meta"] = meta
+
+ return groups
+
+ @staticmethod
+ def json_format_dict(data, pretty=False):
+ """ Converts a dict to a JSON object and dumps it as a formatted
+ string """
+
+ if pretty:
+ return json.dumps(data, sort_keys=True, indent=2)
+ else:
+ return json.dumps(data)
+
+# Run the script
+OVirtInventory()