e97a00de9e
Currently the machine_type will not work if the instance type is set in ovirt. In that case, inst.get_instance_type will be an object and will fails while converting to json. This only work if the instance type is not set in ovirt where inst.get_instance_type is a Null value. The current change make sure that correct "instance type" is passed when instance is set in ovirt and Null when it's not set in ovirt.
(cherry picked from commit 1f3d82dd18
)
292 lines
9.7 KiB
Python
Executable file
292 lines
9.7 KiB
Python
Executable file
#!/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 <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
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
|
|
$ contrib/inventory/ovirt.py --host my_instance
|
|
|
|
Author: Josha Inglis <jinglis@iix.net> based on the gce.py by Eric Johnson <erjohnso@google.com>
|
|
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['url'])
|
|
kwargs['username'] = next(val for val in [os.environ.get('OVIRT_EMAIL'), os.environ.get('OVIRT_USERNAME'), kwargs['username']] if val is not None)
|
|
kwargs['password'] = next(val for val in [os.environ.get('OVIRT_PASS'), os.environ.get('OVIRT_PASSWORD'), kwargs['password']] if val is not None)
|
|
|
|
# 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': self.get_machine_type(inst),
|
|
'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()]
|
|
|
|
def get_machine_type(self,inst):
|
|
inst_type = inst.get_instance_type()
|
|
if inst_type:
|
|
return self.driver.instancetypes.get(id=inst_type.id).name
|
|
|
|
# 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()
|