mirror of https://github.com/ansible/ansible.git
Migrated to ovirt.ovirt
parent
02541a15b2
commit
d1f86d7151
@ -1,868 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import time
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from datetime import datetime
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
from ansible.module_utils.cloud import CloudRetry
|
||||
from ansible.module_utils.common._collections_compat import Mapping
|
||||
|
||||
try:
|
||||
from enum import Enum # enum is a ovirtsdk4 requirement
|
||||
import ovirtsdk4 as sdk
|
||||
import ovirtsdk4.version as sdk_version
|
||||
import ovirtsdk4.types as otypes
|
||||
HAS_SDK = LooseVersion(sdk_version.VERSION) >= LooseVersion('4.3.0')
|
||||
except ImportError:
|
||||
HAS_SDK = False
|
||||
|
||||
|
||||
BYTES_MAP = {
|
||||
'kib': 2**10,
|
||||
'mib': 2**20,
|
||||
'gib': 2**30,
|
||||
'tib': 2**40,
|
||||
'pib': 2**50,
|
||||
}
|
||||
|
||||
|
||||
def check_sdk(module):
|
||||
if not HAS_SDK:
|
||||
module.fail_json(
|
||||
msg='ovirtsdk4 version 4.3.0 or higher is required for this module'
|
||||
)
|
||||
|
||||
|
||||
def get_dict_of_struct(struct, connection=None, fetch_nested=False, attributes=None):
|
||||
"""
|
||||
Convert SDK Struct type into dictionary.
|
||||
"""
|
||||
res = {}
|
||||
|
||||
def resolve_href(value):
|
||||
# Fetch nested values of struct:
|
||||
try:
|
||||
value = connection.follow_link(value)
|
||||
except sdk.Error:
|
||||
value = None
|
||||
nested_obj = dict(
|
||||
(attr, convert_value(getattr(value, attr)))
|
||||
for attr in attributes if getattr(value, attr, None) is not None
|
||||
)
|
||||
nested_obj['id'] = getattr(value, 'id', None)
|
||||
nested_obj['href'] = getattr(value, 'href', None)
|
||||
return nested_obj
|
||||
|
||||
def remove_underscore(val):
|
||||
if val.startswith('_'):
|
||||
val = val[1:]
|
||||
remove_underscore(val)
|
||||
return val
|
||||
|
||||
def convert_value(value):
|
||||
nested = False
|
||||
|
||||
if isinstance(value, sdk.Struct):
|
||||
if not fetch_nested or not value.href:
|
||||
return get_dict_of_struct(value)
|
||||
return resolve_href(value)
|
||||
|
||||
elif isinstance(value, Enum) or isinstance(value, datetime):
|
||||
return str(value)
|
||||
elif isinstance(value, list) or isinstance(value, sdk.List):
|
||||
if isinstance(value, sdk.List) and fetch_nested and value.href:
|
||||
try:
|
||||
value = connection.follow_link(value)
|
||||
nested = True
|
||||
except sdk.Error:
|
||||
value = []
|
||||
|
||||
ret = []
|
||||
for i in value:
|
||||
if isinstance(i, sdk.Struct):
|
||||
if not nested and fetch_nested and i.href:
|
||||
ret.append(resolve_href(i))
|
||||
elif not nested:
|
||||
ret.append(get_dict_of_struct(i))
|
||||
else:
|
||||
nested_obj = dict(
|
||||
(attr, convert_value(getattr(i, attr)))
|
||||
for attr in attributes if getattr(i, attr, None)
|
||||
)
|
||||
nested_obj['id'] = getattr(i, 'id', None)
|
||||
ret.append(nested_obj)
|
||||
elif isinstance(i, Enum):
|
||||
ret.append(str(i))
|
||||
else:
|
||||
ret.append(i)
|
||||
return ret
|
||||
else:
|
||||
return value
|
||||
|
||||
if struct is not None:
|
||||
for key, value in struct.__dict__.items():
|
||||
if value is None:
|
||||
continue
|
||||
|
||||
key = remove_underscore(key)
|
||||
res[key] = convert_value(value)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def engine_version(connection):
|
||||
"""
|
||||
Return string representation of oVirt engine version.
|
||||
"""
|
||||
engine_api = connection.system_service().get()
|
||||
engine_version = engine_api.product_info.version
|
||||
return '%s.%s' % (engine_version.major, engine_version.minor)
|
||||
|
||||
|
||||
def create_connection(auth):
|
||||
"""
|
||||
Create a connection to Python SDK, from task `auth` parameter.
|
||||
If user doesnt't have SSO token the `auth` dictionary has following parameters mandatory:
|
||||
url, username, password
|
||||
|
||||
If user has SSO token the `auth` dictionary has following parameters mandatory:
|
||||
url, token
|
||||
|
||||
The `ca_file` parameter is mandatory in case user want to use secure connection,
|
||||
in case user want to use insecure connection, it's mandatory to send insecure=True.
|
||||
|
||||
:param auth: dictionary which contains needed values for connection creation
|
||||
:return: Python SDK connection
|
||||
"""
|
||||
|
||||
url = auth.get('url')
|
||||
if url is None and auth.get('hostname') is not None:
|
||||
url = 'https://{0}/ovirt-engine/api'.format(auth.get('hostname'))
|
||||
|
||||
return sdk.Connection(
|
||||
url=url,
|
||||
username=auth.get('username'),
|
||||
password=auth.get('password'),
|
||||
ca_file=auth.get('ca_file', None),
|
||||
insecure=auth.get('insecure', False),
|
||||
token=auth.get('token', None),
|
||||
kerberos=auth.get('kerberos', None),
|
||||
headers=auth.get('headers', None),
|
||||
)
|
||||
|
||||
|
||||
def convert_to_bytes(param):
|
||||
"""
|
||||
This method convert units to bytes, which follow IEC standard.
|
||||
|
||||
:param param: value to be converted
|
||||
"""
|
||||
if param is None:
|
||||
return None
|
||||
|
||||
# Get rid of whitespaces:
|
||||
param = ''.join(param.split())
|
||||
|
||||
# Convert to bytes:
|
||||
if len(param) > 3 and param[-3].lower() in ['k', 'm', 'g', 't', 'p']:
|
||||
return int(param[:-3]) * BYTES_MAP.get(param[-3:].lower(), 1)
|
||||
elif param.isdigit():
|
||||
return int(param) * 2**10
|
||||
else:
|
||||
raise ValueError(
|
||||
"Unsupported value(IEC supported): '{value}'".format(value=param)
|
||||
)
|
||||
|
||||
|
||||
def follow_link(connection, link):
|
||||
"""
|
||||
This method returns the entity of the element which link points to.
|
||||
|
||||
:param connection: connection to the Python SDK
|
||||
:param link: link of the entity
|
||||
:return: entity which link points to
|
||||
"""
|
||||
|
||||
if link:
|
||||
return connection.follow_link(link)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_link_name(connection, link):
|
||||
"""
|
||||
This method returns the name of the element which link points to.
|
||||
|
||||
:param connection: connection to the Python SDK
|
||||
:param link: link of the entity
|
||||
:return: name of the entity, which link points to
|
||||
"""
|
||||
|
||||
if link:
|
||||
return connection.follow_link(link).name
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def equal(param1, param2, ignore_case=False):
|
||||
"""
|
||||
Compare two parameters and return if they are equal.
|
||||
This parameter doesn't run equal operation if first parameter is None.
|
||||
With this approach we don't run equal operation in case user don't
|
||||
specify parameter in their task.
|
||||
|
||||
:param param1: user inputted parameter
|
||||
:param param2: value of entity parameter
|
||||
:return: True if parameters are equal or first parameter is None, otherwise False
|
||||
"""
|
||||
if param1 is not None:
|
||||
if ignore_case:
|
||||
return param1.lower() == param2.lower()
|
||||
return param1 == param2
|
||||
return True
|
||||
|
||||
|
||||
def search_by_attributes(service, list_params=None, **kwargs):
|
||||
"""
|
||||
Search for the entity by attributes. Nested entities don't support search
|
||||
via REST, so in case using search for nested entity we return all entities
|
||||
and filter them by specified attributes.
|
||||
"""
|
||||
list_params = list_params or {}
|
||||
# Check if 'list' method support search(look for search parameter):
|
||||
if 'search' in inspect.getargspec(service.list)[0]:
|
||||
res = service.list(
|
||||
# There must be double quotes around name, because some oVirt resources it's possible to create then with space in name.
|
||||
search=' and '.join('{0}="{1}"'.format(k, v) for k, v in kwargs.items()),
|
||||
**list_params
|
||||
)
|
||||
else:
|
||||
res = [
|
||||
e for e in service.list(**list_params) if len([
|
||||
k for k, v in kwargs.items() if getattr(e, k, None) == v
|
||||
]) == len(kwargs)
|
||||
]
|
||||
|
||||
res = res or [None]
|
||||
return res[0]
|
||||
|
||||
|
||||
def search_by_name(service, name, **kwargs):
|
||||
"""
|
||||
Search for the entity by its name. Nested entities don't support search
|
||||
via REST, so in case using search for nested entity we return all entities
|
||||
and filter them by name.
|
||||
|
||||
:param service: service of the entity
|
||||
:param name: name of the entity
|
||||
:return: Entity object returned by Python SDK
|
||||
"""
|
||||
# Check if 'list' method support search(look for search parameter):
|
||||
if 'search' in inspect.getargspec(service.list)[0]:
|
||||
res = service.list(
|
||||
# There must be double quotes around name, because some oVirt resources it's possible to create then with space in name.
|
||||
search='name="{name}"'.format(name=name)
|
||||
)
|
||||
else:
|
||||
res = [e for e in service.list() if e.name == name]
|
||||
|
||||
if kwargs:
|
||||
res = [
|
||||
e for e in service.list() if len([
|
||||
k for k, v in kwargs.items() if getattr(e, k, None) == v
|
||||
]) == len(kwargs)
|
||||
]
|
||||
|
||||
res = res or [None]
|
||||
return res[0]
|
||||
|
||||
|
||||
def get_entity(service, get_params=None):
|
||||
"""
|
||||
Ignore SDK Error in case of getting an entity from service.
|
||||
"""
|
||||
entity = None
|
||||
try:
|
||||
if get_params is not None:
|
||||
entity = service.get(**get_params)
|
||||
else:
|
||||
entity = service.get()
|
||||
except sdk.Error:
|
||||
# We can get here 404, we should ignore it, in case
|
||||
# of removing entity for example.
|
||||
pass
|
||||
return entity
|
||||
|
||||
|
||||
def get_id_by_name(service, name, raise_error=True, ignore_case=False):
|
||||
"""
|
||||
Search an entity ID by it's name.
|
||||
"""
|
||||
entity = search_by_name(service, name)
|
||||
|
||||
if entity is not None:
|
||||
return entity.id
|
||||
|
||||
if raise_error:
|
||||
raise Exception("Entity '%s' was not found." % name)
|
||||
|
||||
|
||||
def wait(
|
||||
service,
|
||||
condition,
|
||||
fail_condition=lambda e: False,
|
||||
timeout=180,
|
||||
wait=True,
|
||||
poll_interval=3,
|
||||
):
|
||||
"""
|
||||
Wait until entity fulfill expected condition.
|
||||
|
||||
:param service: service of the entity
|
||||
:param condition: condition to be fulfilled
|
||||
:param fail_condition: if this condition is true, raise Exception
|
||||
:param timeout: max time to wait in seconds
|
||||
:param wait: if True wait for condition, if False don't wait
|
||||
:param poll_interval: Number of seconds we should wait until next condition check
|
||||
"""
|
||||
# Wait until the desired state of the entity:
|
||||
if wait:
|
||||
start = time.time()
|
||||
while time.time() < start + timeout:
|
||||
# Exit if the condition of entity is valid:
|
||||
entity = get_entity(service)
|
||||
if condition(entity):
|
||||
return
|
||||
elif fail_condition(entity):
|
||||
raise Exception("Error while waiting on result state of the entity.")
|
||||
|
||||
# Sleep for `poll_interval` seconds if none of the conditions apply:
|
||||
time.sleep(float(poll_interval))
|
||||
|
||||
raise Exception("Timeout exceed while waiting on result state of the entity.")
|
||||
|
||||
|
||||
def __get_auth_dict():
|
||||
OVIRT_URL = os.environ.get('OVIRT_URL')
|
||||
OVIRT_HOSTNAME = os.environ.get('OVIRT_HOSTNAME')
|
||||
OVIRT_USERNAME = os.environ.get('OVIRT_USERNAME')
|
||||
OVIRT_PASSWORD = os.environ.get('OVIRT_PASSWORD')
|
||||
OVIRT_TOKEN = os.environ.get('OVIRT_TOKEN')
|
||||
OVIRT_CAFILE = os.environ.get('OVIRT_CAFILE')
|
||||
OVIRT_INSECURE = OVIRT_CAFILE is None
|
||||
|
||||
env_vars = None
|
||||
if OVIRT_URL is None and OVIRT_HOSTNAME is not None:
|
||||
OVIRT_URL = 'https://{0}/ovirt-engine/api'.format(OVIRT_HOSTNAME)
|
||||
if OVIRT_URL and ((OVIRT_USERNAME and OVIRT_PASSWORD) or OVIRT_TOKEN):
|
||||
env_vars = {
|
||||
'url': OVIRT_URL,
|
||||
'username': OVIRT_USERNAME,
|
||||
'password': OVIRT_PASSWORD,
|
||||
'insecure': OVIRT_INSECURE,
|
||||
'token': OVIRT_TOKEN,
|
||||
'ca_file': OVIRT_CAFILE,
|
||||
}
|
||||
if env_vars is not None:
|
||||
auth = dict(default=env_vars, type='dict')
|
||||
else:
|
||||
auth = dict(required=True, type='dict')
|
||||
|
||||
return auth
|
||||
|
||||
|
||||
def ovirt_info_full_argument_spec(**kwargs):
|
||||
"""
|
||||
Extend parameters of info module with parameters which are common to all
|
||||
oVirt info modules.
|
||||
|
||||
:param kwargs: kwargs to be extended
|
||||
:return: extended dictionary with common parameters
|
||||
"""
|
||||
spec = dict(
|
||||
auth=__get_auth_dict(),
|
||||
fetch_nested=dict(default=False, type='bool'),
|
||||
nested_attributes=dict(type='list', default=list()),
|
||||
)
|
||||
spec.update(kwargs)
|
||||
return spec
|
||||
|
||||
|
||||
# Left for third-party module compatibility
|
||||
def ovirt_facts_full_argument_spec(**kwargs):
|
||||
"""
|
||||
This is deprecated. Please use ovirt_info_full_argument_spec instead!
|
||||
|
||||
:param kwargs: kwargs to be extended
|
||||
:return: extended dictionary with common parameters
|
||||
"""
|
||||
return ovirt_info_full_argument_spec(**kwargs)
|
||||
|
||||
|
||||
def ovirt_full_argument_spec(**kwargs):
|
||||
"""
|
||||
Extend parameters of module with parameters which are common to all oVirt modules.
|
||||
|
||||
:param kwargs: kwargs to be extended
|
||||
:return: extended dictionary with common parameters
|
||||
"""
|
||||
spec = dict(
|
||||
auth=__get_auth_dict(),
|
||||
timeout=dict(default=180, type='int'),
|
||||
wait=dict(default=True, type='bool'),
|
||||
poll_interval=dict(default=3, type='int'),
|
||||
fetch_nested=dict(default=False, type='bool'),
|
||||
nested_attributes=dict(type='list', default=list()),
|
||||
)
|
||||
spec.update(kwargs)
|
||||
return spec
|
||||
|
||||
|
||||
def check_params(module):
|
||||
"""
|
||||
Most modules must have either `name` or `id` specified.
|
||||
"""
|
||||
if module.params.get('name') is None and module.params.get('id') is None:
|
||||
module.fail_json(msg='"name" or "id" is required')
|
||||
|
||||
|
||||
def engine_supported(connection, version):
|
||||
return LooseVersion(engine_version(connection)) >= LooseVersion(version)
|
||||
|
||||
|
||||
def check_support(version, connection, module, params):
|
||||
"""
|
||||
Check if parameters used by user are supported by oVirt Python SDK
|
||||
and oVirt engine.
|
||||
"""
|
||||
api_version = LooseVersion(engine_version(connection))
|
||||
version = LooseVersion(version)
|
||||
for param in params:
|
||||
if module.params.get(param) is not None:
|
||||
return LooseVersion(sdk_version.VERSION) >= version and api_version >= version
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class BaseModule(object):
|
||||
"""
|
||||
This is base class for oVirt modules. oVirt modules should inherit this
|
||||
class and override method to customize specific needs of the module.
|
||||
The only abstract method of this class is `build_entity`, which must
|
||||
to be implemented in child class.
|
||||
"""
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
def __init__(self, connection, module, service, changed=False):
|
||||
self._connection = connection
|
||||
self._module = module
|
||||
self._service = service
|
||||
self._changed = changed
|
||||
self._diff = {'after': dict(), 'before': dict()}
|
||||
|
||||
@property
|
||||
def changed(self):
|
||||
return self._changed
|
||||
|
||||
@changed.setter
|
||||
def changed(self, changed):
|
||||
if not self._changed:
|
||||
self._changed = changed
|
||||
|
||||
@abstractmethod
|
||||
def build_entity(self):
|
||||
"""
|
||||
This method should return oVirt Python SDK type, which we want to
|
||||
create or update, initialized by values passed by Ansible module.
|
||||
|
||||
For example if we want to create VM, we will return following:
|
||||
types.Vm(name=self._module.params['vm_name'])
|
||||
|
||||
:return: Specific instance of sdk.Struct.
|
||||
"""
|
||||
pass
|
||||
|
||||
def param(self, name, default=None):
|
||||
"""
|
||||
Return a module parameter specified by it's name.
|
||||
"""
|
||||
return self._module.params.get(name, default)
|
||||
|
||||
def update_check(self, entity):
|
||||
"""
|
||||
This method handle checks whether the entity values are same as values
|
||||
passed to ansible module. By default we don't compare any values.
|
||||
|
||||
:param entity: Entity we want to compare with Ansible module values.
|
||||
:return: True if values are same, so we don't need to update the entity.
|
||||
"""
|
||||
return True
|
||||
|
||||
def pre_create(self, entity):
|
||||
"""
|
||||
This method is called right before entity is created.
|
||||
|
||||
:param entity: Entity to be created or updated.
|
||||
"""
|
||||
pass
|
||||
|
||||
def post_create(self, entity):
|
||||
"""
|
||||
This method is called right after entity is created.
|
||||
|
||||
:param entity: Entity which was created.
|
||||
"""
|
||||
pass
|
||||
|
||||
def post_update(self, entity):
|
||||
"""
|
||||
This method is called right after entity is updated.
|
||||
|
||||
:param entity: Entity which was updated.
|
||||
"""
|
||||
pass
|
||||
|
||||
def diff_update(self, after, update):
|
||||
for k, v in update.items():
|
||||
if isinstance(v, Mapping):
|
||||
after[k] = self.diff_update(after.get(k, dict()), v)
|
||||
else:
|
||||
after[k] = update[k]
|
||||
return after
|
||||
|
||||
def create(
|
||||
self,
|
||||
entity=None,
|
||||
result_state=None,
|
||||
fail_condition=lambda e: False,
|
||||
search_params=None,
|
||||
update_params=None,
|
||||
_wait=None,
|
||||
force_create=False,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Method which is called when state of the entity is 'present'. If user
|
||||
don't provide `entity` parameter the entity is searched using
|
||||
`search_params` parameter. If entity is found it's updated, whether
|
||||
the entity should be updated is checked by `update_check` method.
|
||||
The corresponding updated entity is build by `build_entity` method.
|
||||
|
||||
Function executed after entity is created can optionally be specified
|
||||
in `post_create` parameter. Function executed after entity is updated
|
||||
can optionally be specified in `post_update` parameter.
|
||||
|
||||
:param entity: Entity we want to update, if exists.
|
||||
:param result_state: State which should entity has in order to finish task.
|
||||
:param fail_condition: Function which checks incorrect state of entity, if it returns `True` Exception is raised.
|
||||
:param search_params: Dictionary of parameters to be used for search.
|
||||
:param update_params: The params which should be passed to update method.
|
||||
:param kwargs: Additional parameters passed when creating entity.
|
||||
:return: Dictionary with values returned by Ansible module.
|
||||
"""
|
||||
if entity is None and not force_create:
|
||||
entity = self.search_entity(search_params)
|
||||
|
||||
self.pre_create(entity)
|
||||
|
||||
if entity:
|
||||
# Entity exists, so update it:
|
||||
entity_service = self._service.service(entity.id)
|
||||
if not self.update_check(entity):
|
||||
new_entity = self.build_entity()
|
||||
if not self._module.check_mode:
|
||||
update_params = update_params or {}
|
||||
updated_entity = entity_service.update(
|
||||
new_entity,
|
||||
**update_params
|
||||
)
|
||||
self.post_update(entity)
|
||||
|
||||
# Update diffs only if user specified --diff parameter,
|
||||
# so we don't useless overload API:
|
||||
if self._module._diff:
|
||||
before = get_dict_of_struct(
|
||||
entity,
|
||||
self._connection,
|
||||
fetch_nested=True,
|
||||
attributes=['name'],
|
||||
)
|
||||
after = before.copy()
|
||||
self.diff_update(after, get_dict_of_struct(new_entity))
|
||||
self._diff['before'] = before
|
||||
self._diff['after'] = after
|
||||
|
||||
self.changed = True
|
||||
else:
|
||||
# Entity don't exists, so create it:
|
||||
if not self._module.check_mode:
|
||||
entity = self._service.add(
|
||||
self.build_entity(),
|
||||
**kwargs
|
||||
)
|
||||
self.post_create(entity)
|
||||
self.changed = True
|
||||
|
||||
if not self._module.check_mode:
|
||||
# Wait for the entity to be created and to be in the defined state:
|
||||
entity_service = self._service.service(entity.id)
|
||||
|
||||
def state_condition(entity):
|
||||
return entity
|
||||
|
||||
if result_state:
|
||||
|
||||
def state_condition(entity):
|
||||
return entity and entity.status == result_state
|
||||
|
||||
wait(
|
||||
service=entity_service,
|
||||
condition=state_condition,
|
||||
fail_condition=fail_condition,
|
||||
wait=_wait if _wait is not None else self._module.params['wait'],
|
||||
timeout=self._module.params['timeout'],
|
||||
poll_interval=self._module.params['poll_interval'],
|
||||
)
|
||||
|
||||
return {
|
||||
'changed': self.changed,
|
||||
'id': getattr(entity, 'id', None),
|
||||
type(entity).__name__.lower(): get_dict_of_struct(
|
||||
struct=entity,
|
||||
connection=self._connection,
|
||||
fetch_nested=self._module.params.get('fetch_nested'),
|
||||
attributes=self._module.params.get('nested_attributes'),
|
||||
),
|
||||
'diff': self._diff,
|
||||
}
|
||||
|
||||
def pre_remove(self, entity):
|
||||
"""
|
||||
This method is called right before entity is removed.
|
||||
|
||||
:param entity: Entity which we want to remove.
|
||||
"""
|
||||
pass
|
||||
|
||||
def entity_name(self, entity):
|
||||
return "{e_type} '{e_name}'".format(
|
||||
e_type=type(entity).__name__.lower(),
|
||||
e_name=getattr(entity, 'name', None),
|
||||
)
|
||||
|
||||
def remove(self, entity=None, search_params=None, **kwargs):
|
||||
"""
|
||||
Method which is called when state of the entity is 'absent'. If user
|
||||
don't provide `entity` parameter the entity is searched using
|
||||
`search_params` parameter. If entity is found it's removed.
|
||||
|
||||
Function executed before remove is executed can optionally be specified
|
||||
in `pre_remove` parameter.
|
||||
|
||||
:param entity: Entity we want to remove.
|
||||
:param search_params: Dictionary of parameters to be used for search.
|
||||
:param kwargs: Additional parameters passed when removing entity.
|
||||
:return: Dictionary with values returned by Ansible module.
|
||||
"""
|
||||
if entity is None:
|
||||
entity = self.search_entity(search_params)
|
||||
|
||||
if entity is None:
|
||||
return {
|
||||
'changed': self.changed,
|
||||
'msg': "Entity wasn't found."
|
||||
}
|
||||
|
||||
self.pre_remove(entity)
|
||||
|
||||
entity_service = self._service.service(entity.id)
|
||||
if not self._module.check_mode:
|
||||
entity_service.remove(**kwargs)
|
||||
wait(
|
||||
service=entity_service,
|
||||
condition=lambda entity: not entity,
|
||||
wait=self._module.params['wait'],
|
||||
timeout=self._module.params['timeout'],
|
||||
poll_interval=self._module.params['poll_interval'],
|
||||
)
|
||||
self.changed = True
|
||||
|
||||
return {
|
||||
'changed': self.changed,
|
||||
'id': entity.id,
|
||||
type(entity).__name__.lower(): get_dict_of_struct(
|
||||
struct=entity,
|
||||
connection=self._connection,
|
||||
fetch_nested=self._module.params.get('fetch_nested'),
|
||||
attributes=self._module.params.get('nested_attributes'),
|
||||
),
|
||||
}
|
||||
|
||||
def action(
|
||||
self,
|
||||
action,
|
||||
entity=None,
|
||||
action_condition=lambda e: e,
|
||||
wait_condition=lambda e: e,
|
||||
fail_condition=lambda e: False,
|
||||
pre_action=lambda e: e,
|
||||
post_action=lambda e: None,
|
||||
search_params=None,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
This method is executed when we want to change the state of some oVirt
|
||||
entity. The action to be executed on oVirt service is specified by
|
||||
`action` parameter. Whether the action should be executed can be
|
||||
specified by passing `action_condition` parameter. State which the
|
||||
entity should be in after execution of the action can be specified
|
||||
by `wait_condition` parameter.
|
||||
|
||||
Function executed before an action on entity can optionally be specified
|
||||
in `pre_action` parameter. Function executed after an action on entity can
|
||||
optionally be specified in `post_action` parameter.
|
||||
|
||||
:param action: Action which should be executed by service on entity.
|
||||
:param entity: Entity we want to run action on.
|
||||
:param action_condition: Function which is executed when checking if action should be executed.
|
||||
:param fail_condition: Function which checks incorrect state of entity, if it returns `True` Exception is raised.
|
||||
:param wait_condition: Function which is executed when waiting on result state.
|
||||
:param pre_action: Function which is executed before running the action.
|
||||
:param post_action: Function which is executed after running the action.
|
||||
:param search_params: Dictionary of parameters to be used for search.
|
||||
:param kwargs: Additional parameters passed to action.
|
||||
:return: Dictionary with values returned by Ansible module.
|
||||
"""
|
||||
if entity is None:
|
||||
entity = self.search_entity(search_params)
|
||||
|
||||
entity = pre_action(entity)
|
||||
|
||||
if entity is None:
|
||||
self._module.fail_json(
|
||||
msg="Entity not found, can't run action '{0}'.".format(
|
||||
action
|
||||
)
|
||||
)
|
||||
|
||||
entity_service = self._service.service(entity.id)
|
||||
entity = entity_service.get()
|
||||
if action_condition(entity):
|
||||
if not self._module.check_mode:
|
||||
getattr(entity_service, action)(**kwargs)
|
||||
self.changed = True
|
||||
|
||||
post_action(entity)
|
||||
|
||||
wait(
|
||||
service=self._service.service(entity.id),
|
||||
condition=wait_condition,
|
||||
fail_condition=fail_condition,
|
||||
wait=self._module.params['wait'],
|
||||
timeout=self._module.params['timeout'],
|
||||
poll_interval=self._module.params['poll_interval'],
|
||||
)
|
||||
return {
|
||||
'changed': self.changed,
|
||||
'id': entity.id,
|
||||
type(entity).__name__.lower(): get_dict_of_struct(
|
||||
struct=entity,
|
||||
connection=self._connection,
|
||||
fetch_nested=self._module.params.get('fetch_nested'),
|
||||
attributes=self._module.params.get('nested_attributes'),
|
||||
),
|
||||
'diff': self._diff,
|
||||
}
|
||||
|
||||
def wait_for_import(self, condition=lambda e: True):
|
||||
if self._module.params['wait']:
|
||||
start = time.time()
|
||||
timeout = self._module.params['timeout']
|
||||
poll_interval = self._module.params['poll_interval']
|
||||
while time.time() < start + timeout:
|
||||
entity = self.search_entity()
|
||||
if entity and condition(entity):
|
||||
return entity
|
||||
time.sleep(poll_interval)
|
||||
|
||||
def search_entity(self, search_params=None, list_params=None):
|
||||
"""
|
||||
Always first try to search by `ID`, if ID isn't specified,
|
||||
check if user constructed special search in `search_params`,
|
||||
if not search by `name`.
|
||||
"""
|
||||
entity = None
|
||||
|
||||
if 'id' in self._module.params and self._module.params['id'] is not None:
|
||||
entity = get_entity(self._service.service(self._module.params['id']), get_params=list_params)
|
||||
elif search_params is not None:
|
||||
entity = search_by_attributes(self._service, list_params=list_params, **search_params)
|
||||
elif self._module.params.get('name') is not None:
|
||||
entity = search_by_attributes(self._service, list_params=list_params, name=self._module.params['name'])
|
||||
|
||||
return entity
|
||||
|
||||
def _get_major(self, full_version):
|
||||
if full_version is None or full_version == "":
|
||||
return None
|
||||
if isinstance(full_version, otypes.Version):
|
||||
return int(full_version.major)
|
||||
return int(full_version.split('.')[0])
|
||||
|
||||
def _get_minor(self, full_version):
|
||||
if full_version is None or full_version == "":
|
||||
return None
|
||||
if isinstance(full_version, otypes.Version):
|
||||
return int(full_version.minor)
|
||||
return int(full_version.split('.')[1])
|
||||
|
||||
|
||||
def _sdk4_error_maybe():
|
||||
"""
|
||||
Allow for ovirtsdk4 not being installed.
|
||||
"""
|
||||
if HAS_SDK:
|
||||
return sdk.Error
|
||||
return type(None)
|
||||
|
||||
|
||||
class OvirtRetry(CloudRetry):
|
||||
base_class = _sdk4_error_maybe()
|
||||
|
||||
@staticmethod
|
||||
def status_code_from_exception(error):
|
||||
return error.code
|
||||
|
||||
@staticmethod
|
||||
def found(response_code, catch_extra_error_codes=None):
|
||||
# This is a list of error codes to retry.
|
||||
retry_on = [
|
||||
# HTTP status: Conflict
|
||||
409,
|
||||
]
|
||||
if catch_extra_error_codes:
|
||||
retry_on.extend(catch_extra_error_codes)
|
||||
|
||||
return response_code in retry_on
|
@ -1,329 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_affinity_group
|
||||
short_description: Module to manage affinity groups in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author:
|
||||
- Ondra Machacek (@machacekondra)
|
||||
description:
|
||||
- "This module manage affinity groups in oVirt/RHV. It can also manage assignments
|
||||
of those groups to VMs."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the affinity group to manage.
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- Should the affinity group be present or absent.
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
cluster:
|
||||
description:
|
||||
- Name of the cluster of the affinity group.
|
||||
description:
|
||||
description:
|
||||
- Description of the affinity group.
|
||||
host_enforcing:
|
||||
description:
|
||||
- If I(yes) VM cannot start on host if it does not satisfy the C(host_rule).
|
||||
- This parameter is support since oVirt/RHV 4.1 version.
|
||||
type: bool
|
||||
host_rule:
|
||||
description:
|
||||
- If I(positive) I(all) VMs in this group should run on the this host.
|
||||
- If I(negative) I(no) VMs in this group should run on the this host.
|
||||
- If I(disabled) this affinity group doesn't take effect.
|
||||
- This parameter is support since oVirt/RHV 4.1 version.
|
||||
choices: [ disabled, negative, positive ]
|
||||
vm_enforcing:
|
||||
description:
|
||||
- If I(yes) VM cannot start if it does not satisfy the C(vm_rule).
|
||||
type: bool
|
||||
vm_rule:
|
||||
description:
|
||||
- If I(positive) I(all) VMs in this group should run on the host defined by C(host_rule).
|
||||
- If I(negative) I(no) VMs in this group should run on the host defined by C(host_rule).
|
||||
- If I(disabled) this affinity group doesn't take effect.
|
||||
choices: [ disabled, negative, positive ]
|
||||
vms:
|
||||
description:
|
||||
- List of the VMs names, which should have assigned this affinity group.
|
||||
hosts:
|
||||
description:
|
||||
- List of the hosts names, which should have assigned this affinity group.
|
||||
- This parameter is support since oVirt/RHV 4.1 version.
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
- name: Create(if not exists) and assign affinity group to VMs vm1 and vm2 and host host1
|
||||
ovirt_affinity_group:
|
||||
name: mygroup
|
||||
cluster: mycluster
|
||||
vm_enforcing: true
|
||||
vm_rule: positive
|
||||
host_enforcing: true
|
||||
host_rule: positive
|
||||
vms:
|
||||
- vm1
|
||||
- vm2
|
||||
hosts:
|
||||
- host1
|
||||
|
||||
- name: Detach VMs from affinity group and disable VM rule
|
||||
ovirt_affinity_group:
|
||||
name: mygroup
|
||||
cluster: mycluster
|
||||
vm_enforcing: false
|
||||
vm_rule: disabled
|
||||
host_enforcing: true
|
||||
host_rule: positive
|
||||
vms: []
|
||||
hosts:
|
||||
- host1
|
||||
- host2
|
||||
|
||||
- name: Remove affinity group
|
||||
ovirt_affinity_group:
|
||||
state: absent
|
||||
cluster: mycluster
|
||||
name: mygroup
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the affinity group which is managed
|
||||
returned: On success if affinity group is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
affinity_group:
|
||||
description: "Dictionary of all the affinity group attributes. Affinity group attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/affinity_group."
|
||||
returned: On success if affinity group is found.
|
||||
type: str
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
check_support,
|
||||
create_connection,
|
||||
get_id_by_name,
|
||||
equal,
|
||||
engine_supported,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
class AffinityGroupsModule(BaseModule):
|
||||
|
||||
def __init__(self, vm_ids, host_ids, *args, **kwargs):
|
||||
super(AffinityGroupsModule, self).__init__(*args, **kwargs)
|
||||
self._vm_ids = vm_ids
|
||||
self._host_ids = host_ids
|
||||
|
||||
def update_vms(self, affinity_group):
|
||||
"""
|
||||
This method iterate via the affinity VM assignments and datech the VMs
|
||||
which should not be attached to affinity and attach VMs which should be
|
||||
attached to affinity.
|
||||
"""
|
||||
assigned_vms = self.assigned_vms(affinity_group)
|
||||
to_remove = [vm for vm in assigned_vms if vm not in self._vm_ids]
|
||||
to_add = [vm for vm in self._vm_ids if vm not in assigned_vms]
|
||||
ag_service = self._service.group_service(affinity_group.id)
|
||||
for vm in to_remove:
|
||||
ag_service.vms_service().vm_service(vm).remove()
|
||||
for vm in to_add:
|
||||
# API return <action> element instead of VM element, so we
|
||||
# need to WA this issue, for oVirt/RHV versions having this bug:
|
||||
try:
|
||||
ag_service.vms_service().add(otypes.Vm(id=vm))
|
||||
except ValueError as ex:
|
||||
if 'complete' not in str(ex):
|
||||
raise ex
|
||||
|
||||
def post_create(self, affinity_group):
|
||||
self.update_vms(affinity_group)
|
||||
|
||||
def post_update(self, affinity_group):
|
||||
self.update_vms(affinity_group)
|
||||
|
||||
def build_entity(self):
|
||||
affinity_group = otypes.AffinityGroup(
|
||||
name=self._module.params['name'],
|
||||
description=self._module.params['description'],
|
||||
positive=(
|
||||
self._module.params['vm_rule'] == 'positive'
|
||||
) if self._module.params['vm_rule'] is not None else None,
|
||||
enforcing=(
|
||||
self._module.params['vm_enforcing']
|
||||
) if self._module.params['vm_enforcing'] is not None else None,
|
||||
)
|
||||
|
||||
# Those attributes are Supported since 4.1:
|
||||
if not engine_supported(self._connection, '4.1'):
|
||||
return affinity_group
|
||||
|
||||
affinity_group.hosts_rule = otypes.AffinityRule(
|
||||
positive=(
|
||||
self.param('host_rule') == 'positive'
|
||||
) if self.param('host_rule') is not None else None,
|
||||
enforcing=self.param('host_enforcing'),
|
||||
) if (
|
||||
self.param('host_enforcing') is not None or
|
||||
self.param('host_rule') is not None
|
||||
) else None
|
||||
|
||||
affinity_group.vms_rule = otypes.AffinityRule(
|
||||
positive=(
|
||||
self.param('vm_rule') == 'positive'
|
||||
) if self.param('vm_rule') is not None else None,
|
||||
enforcing=self.param('vm_enforcing'),
|
||||
enabled=(
|
||||
self.param('vm_rule') in ['negative', 'positive']
|
||||
) if self.param('vm_rule') is not None else None,
|
||||
) if (
|
||||
self.param('vm_enforcing') is not None or
|
||||
self.param('vm_rule') is not None
|
||||
) else None
|
||||
|
||||
affinity_group.hosts = [
|
||||
otypes.Host(id=host_id) for host_id in self._host_ids
|
||||
] if self._host_ids is not None else None
|
||||
|
||||
return affinity_group
|
||||
|
||||
def assigned_vms(self, affinity_group):
|
||||
if getattr(affinity_group.vms, 'href', None):
|
||||
return sorted([
|
||||
vm.id for vm in self._connection.follow_link(affinity_group.vms)
|
||||
])
|
||||
else:
|
||||
return sorted([vm.id for vm in affinity_group.vms])
|
||||
|
||||
def update_check(self, entity):
|
||||
assigned_vms = self.assigned_vms(entity)
|
||||
do_update = (
|
||||
equal(self.param('description'), entity.description) and equal(self.param('vm_enforcing'), entity.enforcing) and equal(
|
||||
self.param('vm_rule') == 'positive' if self.param('vm_rule') else None,
|
||||
entity.positive
|
||||
) and equal(self._vm_ids, assigned_vms)
|
||||
)
|
||||
# Following attributes is supported since 4.1,
|
||||
# so return if it doesn't exist:
|
||||
if not engine_supported(self._connection, '4.1'):
|
||||
return do_update
|
||||
|
||||
# Following is supported since 4.1:
|
||||
return do_update and (
|
||||
equal(
|
||||
self.param('host_rule') == 'positive' if self.param('host_rule') else None,
|
||||
entity.hosts_rule.positive) and equal(self.param('host_enforcing'), entity.hosts_rule.enforcing) and equal(
|
||||
self.param('vm_rule') in ['negative', 'positive'] if self.param('vm_rule') else None,
|
||||
entity.vms_rule.enabled) and equal(self._host_ids, sorted([host.id for host in entity.hosts]))
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
cluster=dict(type='str', required=True),
|
||||
name=dict(type='str', required=True),
|
||||
description=dict(type='str'),
|
||||
vm_enforcing=dict(type='bool'),
|
||||
vm_rule=dict(type='str', choices=['disabled', 'negative', 'positive']),
|
||||
host_enforcing=dict(type='bool'),
|
||||
host_rule=dict(type='str', choices=['disabled', 'negative', 'positive']),
|
||||
vms=dict(type='list'),
|
||||
hosts=dict(type='list'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
# Check if unsupported parameters were passed:
|
||||
supported_41 = ('host_enforcing', 'host_rule', 'hosts')
|
||||
if not check_support(
|
||||
version='4.1',
|
||||
connection=connection,
|
||||
module=module,
|
||||
params=supported_41,
|
||||
):
|
||||
module.fail_json(
|
||||
msg='Following parameters are supported since 4.1: {params}'.format(
|
||||
params=supported_41,
|
||||
)
|
||||
)
|
||||
clusters_service = connection.system_service().clusters_service()
|
||||
vms_service = connection.system_service().vms_service()
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
cluster_name = module.params['cluster']
|
||||
cluster = search_by_name(clusters_service, cluster_name)
|
||||
if cluster is None:
|
||||
raise Exception("Cluster '%s' was not found." % cluster_name)
|
||||
cluster_service = clusters_service.cluster_service(cluster.id)
|
||||
affinity_groups_service = cluster_service.affinity_groups_service()
|
||||
|
||||
# Fetch VM ids which should be assigned to affinity group:
|
||||
vm_ids = sorted([
|
||||
get_id_by_name(vms_service, vm_name)
|
||||
for vm_name in module.params['vms']
|
||||
]) if module.params['vms'] is not None else None
|
||||
# Fetch host ids which should be assigned to affinity group:
|
||||
host_ids = sorted([
|
||||
get_id_by_name(hosts_service, host_name)
|
||||
for host_name in module.params['hosts']
|
||||
]) if module.params['hosts'] is not None else None
|
||||
|
||||
affinity_groups_module = AffinityGroupsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=affinity_groups_service,
|
||||
vm_ids=vm_ids,
|
||||
host_ids=host_ids,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = affinity_groups_module.create()
|
||||
elif state == 'absent':
|
||||
ret = affinity_groups_module.remove()
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,210 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_affinity_label
|
||||
short_description: Module to manage affinity labels in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "This module manage affinity labels in oVirt/RHV. It can also manage assignments
|
||||
of those labels to hosts and VMs."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the affinity label to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the affinity label be present or absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
cluster:
|
||||
description:
|
||||
- "Name of the cluster where vms and hosts resides."
|
||||
vms:
|
||||
description:
|
||||
- "List of the VMs names, which should have assigned this affinity label."
|
||||
hosts:
|
||||
description:
|
||||
- "List of the hosts names, which should have assigned this affinity label."
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create(if not exists) and assign affinity label to vms vm1 and vm2 and host host1
|
||||
- ovirt_affinity_label:
|
||||
name: mylabel
|
||||
cluster: mycluster
|
||||
vms:
|
||||
- vm1
|
||||
- vm2
|
||||
hosts:
|
||||
- host1
|
||||
|
||||
# To detach all VMs from label
|
||||
- ovirt_affinity_label:
|
||||
name: mylabel
|
||||
cluster: mycluster
|
||||
vms: []
|
||||
|
||||
# Remove affinity label
|
||||
- ovirt_affinity_label:
|
||||
state: absent
|
||||
name: mylabel
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the affinity label which is managed
|
||||
returned: On success if affinity label is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
affinity_label:
|
||||
description: "Dictionary of all the affinity label attributes. Affinity label attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/affinity_label."
|
||||
type: dict
|
||||
returned: On success if affinity label is found.
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from collections import defaultdict
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
ovirt_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
class AffinityLabelsModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.AffinityLabel(name=self._module.params['name'])
|
||||
|
||||
def post_create(self, entity):
|
||||
self.update_check(entity)
|
||||
|
||||
def pre_remove(self, entity):
|
||||
self._module.params['vms'] = []
|
||||
self._module.params['hosts'] = []
|
||||
self.update_check(entity)
|
||||
|
||||
def _update_label_assignments(self, entity, name, label_obj_type):
|
||||
objs_service = getattr(self._connection.system_service(), '%s_service' % name)()
|
||||
if self._module.params[name] is not None:
|
||||
objs = self._connection.follow_link(getattr(entity, name))
|
||||
objs_names = defaultdict(list)
|
||||
for obj in objs:
|
||||
labeled_entity = objs_service.service(obj.id).get()
|
||||
if self._module.params['cluster'] is None:
|
||||
objs_names[labeled_entity.name].append(obj.id)
|
||||
elif self._connection.follow_link(labeled_entity.cluster).name == self._module.params['cluster']:
|
||||
objs_names[labeled_entity.name].append(obj.id)
|
||||
|
||||
for obj in self._module.params[name]:
|
||||
if obj not in objs_names:
|
||||
for obj_id in objs_service.list(
|
||||
search='name=%s and cluster=%s' % (obj, self._module.params['cluster'])
|
||||
):
|
||||
label_service = getattr(self._service.service(entity.id), '%s_service' % name)()
|
||||
if not self._module.check_mode:
|
||||
label_service.add(**{
|
||||
name[:-1]: label_obj_type(id=obj_id.id)
|
||||
})
|
||||
self.changed = True
|
||||
|
||||
for obj in objs_names:
|
||||
if obj not in self._module.params[name]:
|
||||
label_service = getattr(self._service.service(entity.id), '%s_service' % name)()
|
||||
if not self._module.check_mode:
|
||||
for obj_id in objs_names[obj]:
|
||||
label_service.service(obj_id).remove()
|
||||
self.changed = True
|
||||
|
||||
def update_check(self, entity):
|
||||
self._update_label_assignments(entity, 'vms', otypes.Vm)
|
||||
self._update_label_assignments(entity, 'hosts', otypes.Host)
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
cluster=dict(default=None),
|
||||
name=dict(default=None, required=True),
|
||||
vms=dict(default=None, type='list'),
|
||||
hosts=dict(default=None, type='list'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
('state', 'present', ['cluster']),
|
||||
],
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
affinity_labels_service = connection.system_service().affinity_labels_service()
|
||||
affinity_labels_module = AffinityLabelsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=affinity_labels_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = affinity_labels_module.create()
|
||||
elif state == 'absent':
|
||||
ret = affinity_labels_module.remove()
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,182 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_affinity_label_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV affinity labels
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV affinity labels."
|
||||
- This module was called C(ovirt_affinity_label_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_affinity_label_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_affinity_labels), which
|
||||
contains a list of affinity labels. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the affinity labels which should be listed."
|
||||
vm:
|
||||
description:
|
||||
- "Name of the VM, which affinity labels should be listed."
|
||||
host:
|
||||
description:
|
||||
- "Name of the host, which affinity labels should be listed."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all affinity labels, which names start with C(label):
|
||||
- ovirt_affinity_label_info:
|
||||
name: label*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_affinity_labels }}"
|
||||
|
||||
# Gather information about all affinity labels, which are assigned to VMs
|
||||
# which names start with C(postgres):
|
||||
- ovirt_affinity_label_info:
|
||||
vm: postgres*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_affinity_labels }}"
|
||||
|
||||
# Gather information about all affinity labels, which are assigned to hosts
|
||||
# which names start with C(west):
|
||||
- ovirt_affinity_label_info:
|
||||
host: west*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_affinity_labels }}"
|
||||
|
||||
# Gather information about all affinity labels, which are assigned to hosts
|
||||
# which names start with C(west) or VMs which names start with C(postgres):
|
||||
- ovirt_affinity_label_info:
|
||||
host: west*
|
||||
vm: postgres*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_affinity_labels }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_affinity_labels:
|
||||
description: "List of dictionaries describing the affinity labels. Affinity labels attributes are mapped to dictionary keys,
|
||||
all affinity labels attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/affinity_label."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import fnmatch
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
name=dict(default=None),
|
||||
host=dict(default=None),
|
||||
vm=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_affinity_label_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_affinity_label_facts' module has been renamed to 'ovirt_affinity_label_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
affinity_labels_service = connection.system_service().affinity_labels_service()
|
||||
labels = []
|
||||
all_labels = affinity_labels_service.list()
|
||||
if module.params['name']:
|
||||
labels.extend([
|
||||
l for l in all_labels
|
||||
if fnmatch.fnmatch(l.name, module.params['name'])
|
||||
])
|
||||
if module.params['host']:
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
if search_by_name(hosts_service, module.params['host']) is None:
|
||||
raise Exception("Host '%s' was not found." % module.params['host'])
|
||||
labels.extend([
|
||||
label
|
||||
for label in all_labels
|
||||
for host in connection.follow_link(label.hosts)
|
||||
if fnmatch.fnmatch(hosts_service.service(host.id).get().name, module.params['host'])
|
||||
])
|
||||
if module.params['vm']:
|
||||
vms_service = connection.system_service().vms_service()
|
||||
if search_by_name(vms_service, module.params['vm']) is None:
|
||||
raise Exception("Vm '%s' was not found." % module.params['vm'])
|
||||
labels.extend([
|
||||
label
|
||||
for label in all_labels
|
||||
for vm in connection.follow_link(label.vms)
|
||||
if fnmatch.fnmatch(vms_service.service(vm.id).get().name, module.params['vm'])
|
||||
])
|
||||
|
||||
if not (module.params['vm'] or module.params['host'] or module.params['name']):
|
||||
labels = all_labels
|
||||
|
||||
result = dict(
|
||||
ovirt_affinity_labels=[
|
||||
get_dict_of_struct(
|
||||
struct=l,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for l in labels
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,98 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_api_info
|
||||
short_description: Retrieve information about the oVirt/RHV API
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.5"
|
||||
description:
|
||||
- "Retrieve information about the oVirt/RHV API."
|
||||
- This module was called C(ovirt_api_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_api_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_api),
|
||||
which contains a information about oVirt/RHV API. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information oVirt API:
|
||||
- ovirt_api_info:
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_api }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_api:
|
||||
description: "Dictionary describing the oVirt API information.
|
||||
Api attributes are mapped to dictionary keys,
|
||||
all API attributes can be found at following
|
||||
url: https://ovirt.example.com/ovirt-engine/api/model#types/api."
|
||||
returned: On success.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec()
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_api_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_api_facts' module has been renamed to 'ovirt_api_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
api = connection.system_service().get()
|
||||
result = dict(
|
||||
ovirt_api=get_dict_of_struct(
|
||||
struct=api,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
)
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,300 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_auth
|
||||
short_description: "Module to manage authentication to oVirt/RHV"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.2"
|
||||
description:
|
||||
- "This module authenticates to oVirt/RHV engine and creates SSO token, which should be later used in
|
||||
all other oVirt/RHV modules, so all modules don't need to perform login and logout.
|
||||
This module returns an Ansible fact called I(ovirt_auth). Every module can use this
|
||||
fact as C(auth) parameter, to perform authentication."
|
||||
options:
|
||||
state:
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
description:
|
||||
- "Specifies if a token should be created or revoked."
|
||||
username:
|
||||
required: False
|
||||
description:
|
||||
- "The name of the user. For example: I(admin@internal)
|
||||
Default value is set by I(OVIRT_USERNAME) environment variable."
|
||||
password:
|
||||
required: False
|
||||
description:
|
||||
- "The password of the user. Default value is set by I(OVIRT_PASSWORD) environment variable."
|
||||
token:
|
||||
required: False
|
||||
description:
|
||||
- "SSO token to be used instead of login with username/password.
|
||||
Default value is set by I(OVIRT_TOKEN) environment variable."
|
||||
version_added: 2.5
|
||||
url:
|
||||
required: False
|
||||
description:
|
||||
- "A string containing the API URL of the server.
|
||||
For example: I(https://server.example.com/ovirt-engine/api).
|
||||
Default value is set by I(OVIRT_URL) environment variable."
|
||||
- "Either C(url) or C(hostname) is required."
|
||||
hostname:
|
||||
required: False
|
||||
description:
|
||||
- "A string containing the hostname of the server.
|
||||
For example: I(server.example.com).
|
||||
Default value is set by I(OVIRT_HOSTNAME) environment variable."
|
||||
- "Either C(url) or C(hostname) is required."
|
||||
version_added: "2.6"
|
||||
insecure:
|
||||
required: False
|
||||
description:
|
||||
- "A boolean flag that indicates if the server TLS certificate and host name should be checked."
|
||||
type: bool
|
||||
ca_file:
|
||||
required: False
|
||||
description:
|
||||
- "A PEM file containing the trusted CA certificates. The
|
||||
certificate presented by the server will be verified using these CA
|
||||
certificates. If C(ca_file) parameter is not set, system wide
|
||||
CA certificate store is used.
|
||||
Default value is set by I(OVIRT_CAFILE) environment variable."
|
||||
timeout:
|
||||
required: False
|
||||
description:
|
||||
- "The maximum total time to wait for the response, in
|
||||
seconds. A value of zero (the default) means wait forever. If
|
||||
the timeout expires before the response is received an exception
|
||||
will be raised."
|
||||
compress:
|
||||
required: False
|
||||
description:
|
||||
- "A boolean flag indicating if the SDK should ask
|
||||
the server to send compressed responses. The default is I(True).
|
||||
Note that this is a hint for the server, and that it may return
|
||||
uncompressed data even when this parameter is set to I(True)."
|
||||
type: bool
|
||||
kerberos:
|
||||
required: False
|
||||
description:
|
||||
- "A boolean flag indicating if Kerberos authentication
|
||||
should be used instead of the default basic authentication."
|
||||
type: bool
|
||||
headers:
|
||||
required: False
|
||||
description:
|
||||
- "A dictionary of HTTP headers to be added to each API call."
|
||||
version_added: "2.4"
|
||||
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- ovirt-engine-sdk-python >= 4.3.0
|
||||
notes:
|
||||
- "Everytime you use ovirt_auth module to obtain ticket, you need to also revoke the ticket,
|
||||
when you no longer need it, otherwise the ticket would be revoked by engine when it expires.
|
||||
For an example of how to achieve that, please take a look at I(examples) section."
|
||||
- "In order to use this module you have to install oVirt/RHV Python SDK.
|
||||
To ensure it's installed with correct version you can create the following task:
|
||||
I(pip: name=ovirt-engine-sdk-python version=4.3.0)"
|
||||
- "Note that in oVirt/RHV 4.1 if you want to use a user which is not administrator
|
||||
you must enable the I(ENGINE_API_FILTER_BY_DEFAULT) variable in engine. In
|
||||
oVirt/RHV 4.2 and later it's enabled by default."
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- block:
|
||||
# Create a vault with `ovirt_password` variable which store your
|
||||
# oVirt/RHV user's password, and include that yaml file with variable:
|
||||
- include_vars: ovirt_password.yml
|
||||
|
||||
- name: Obtain SSO token with using username/password credentials
|
||||
ovirt_auth:
|
||||
url: https://ovirt.example.com/ovirt-engine/api
|
||||
username: admin@internal
|
||||
ca_file: ca.pem
|
||||
password: "{{ ovirt_password }}"
|
||||
|
||||
# Previous task generated I(ovirt_auth) fact, which you can later use
|
||||
# in different modules as follows:
|
||||
- ovirt_vm:
|
||||
auth: "{{ ovirt_auth }}"
|
||||
state: absent
|
||||
name: myvm
|
||||
|
||||
always:
|
||||
- name: Always revoke the SSO token
|
||||
ovirt_auth:
|
||||
state: absent
|
||||
ovirt_auth: "{{ ovirt_auth }}"
|
||||
|
||||
# When user will set following environment variables:
|
||||
# OVIRT_URL = https://fqdn/ovirt-engine/api
|
||||
# OVIRT_USERNAME = admin@internal
|
||||
# OVIRT_PASSWORD = the_password
|
||||
# User can login the oVirt using environment variable instead of variables
|
||||
# in yaml file.
|
||||
# This is mainly useful when using Ansible Tower or AWX, as it will work
|
||||
# for Red Hat Virtualization credentials type.
|
||||
- name: Obtain SSO token
|
||||
ovirt_auth:
|
||||
state: present
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_auth:
|
||||
description: Authentication facts, needed to perform authentication to oVirt/RHV.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
token:
|
||||
description: SSO token which is used for connection to oVirt/RHV engine.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "kdfVWp9ZgeewBXV-iq3Js1-xQJZPSEQ334FLb3eksoEPRaab07DhZ8ED8ghz9lJd-MQ2GqtRIeqhvhCkrUWQPw"
|
||||
url:
|
||||
description: URL of the oVirt/RHV engine API endpoint.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "https://ovirt.example.com/ovirt-engine/api"
|
||||
ca_file:
|
||||
description: CA file, which is used to verify SSL/TLS connection.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "ca.pem"
|
||||
insecure:
|
||||
description: Flag indicating if insecure connection is used.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: False
|
||||
timeout:
|
||||
description: Number of seconds to wait for response.
|
||||
returned: success
|
||||
type: int
|
||||
sample: 0
|
||||
compress:
|
||||
description: Flag indicating if compression is used for connection.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: True
|
||||
kerberos:
|
||||
description: Flag indicating if kerberos is used for authentication.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: False
|
||||
headers:
|
||||
description: Dictionary of HTTP headers to be added to each API call.
|
||||
returned: success
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import os
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4 as sdk
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import check_sdk
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
url=dict(default=None),
|
||||
hostname=dict(default=None),
|
||||
username=dict(default=None),
|
||||
password=dict(default=None, no_log=True),
|
||||
ca_file=dict(default=None, type='path'),
|
||||
insecure=dict(required=False, type='bool', default=None),
|
||||
timeout=dict(required=False, type='int', default=0),
|
||||
compress=dict(required=False, type='bool', default=True),
|
||||
kerberos=dict(required=False, type='bool', default=False),
|
||||
headers=dict(required=False, type='dict'),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
token=dict(default=None),
|
||||
ovirt_auth=dict(required=None, type='dict'),
|
||||
),
|
||||
required_if=[
|
||||
('state', 'absent', ['ovirt_auth']),
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
check_sdk(module)
|
||||
|
||||
state = module.params.get('state')
|
||||
if state == 'present':
|
||||
params = module.params
|
||||
elif state == 'absent':
|
||||
params = module.params['ovirt_auth']
|
||||
|
||||
def get_required_parameter(param, env_var, required=False):
|
||||
var = params.get(param) or os.environ.get(env_var)
|
||||
if not var and required and state == 'present':
|
||||
module.fail_json(msg="'%s' is a required parameter." % param)
|
||||
|
||||
return var
|
||||
|
||||
url = get_required_parameter('url', 'OVIRT_URL', required=False)
|
||||
hostname = get_required_parameter('hostname', 'OVIRT_HOSTNAME', required=False)
|
||||
if url is None and hostname is None:
|
||||
module.fail_json(msg="You must specify either 'url' or 'hostname'.")
|
||||
|
||||
if url is None and hostname is not None:
|
||||
url = 'https://{0}/ovirt-engine/api'.format(hostname)
|
||||
|
||||
username = get_required_parameter('username', 'OVIRT_USERNAME')
|
||||
password = get_required_parameter('password', 'OVIRT_PASSWORD')
|
||||
token = get_required_parameter('token', 'OVIRT_TOKEN')
|
||||
ca_file = get_required_parameter('ca_file', 'OVIRT_CAFILE')
|
||||
insecure = params.get('insecure') if params.get('insecure') is not None else not bool(ca_file)
|
||||
|
||||
connection = sdk.Connection(
|
||||
url=url,
|
||||
username=username,
|
||||
password=password,
|
||||
ca_file=ca_file,
|
||||
insecure=insecure,
|
||||
timeout=params.get('timeout'),
|
||||
compress=params.get('compress'),
|
||||
kerberos=params.get('kerberos'),
|
||||
headers=params.get('headers'),
|
||||
token=token,
|
||||
)
|
||||
try:
|
||||
token = connection.authenticate()
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
ansible_facts=dict(
|
||||
ovirt_auth=dict(
|
||||
token=token,
|
||||
url=url,
|
||||
ca_file=ca_file,
|
||||
insecure=insecure,
|
||||
timeout=params.get('timeout'),
|
||||
compress=params.get('compress'),
|
||||
kerberos=params.get('kerberos'),
|
||||
headers=params.get('headers'),
|
||||
) if state == 'present' else dict()
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
# Close the connection, but don't revoke token
|
||||
connection.close(logout=state == 'absent')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,745 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_cluster
|
||||
short_description: Module to manage clusters in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage clusters in oVirt/RHV"
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the cluster to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- "Name of the cluster to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the cluster be present or absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
data_center:
|
||||
description:
|
||||
- "Datacenter name where cluster reside."
|
||||
description:
|
||||
description:
|
||||
- "Description of the cluster."
|
||||
comment:
|
||||
description:
|
||||
- "Comment of the cluster."
|
||||
network:
|
||||
description:
|
||||
- "Management network of cluster to access cluster hosts."
|
||||
ballooning:
|
||||
description:
|
||||
- "If I(True) enable memory balloon optimization. Memory balloon is used to
|
||||
re-distribute / reclaim the host memory based on VM needs
|
||||
in a dynamic way."
|
||||
type: bool
|
||||
virt:
|
||||
description:
|
||||
- "If I(True), hosts in this cluster will be used to run virtual machines."
|
||||
type: bool
|
||||
gluster:
|
||||
description:
|
||||
- "If I(True), hosts in this cluster will be used as Gluster Storage
|
||||
server nodes, and not for running virtual machines."
|
||||
- "By default the cluster is created for virtual machine hosts."
|
||||
type: bool
|
||||
threads_as_cores:
|
||||
description:
|
||||
- "If I(True) the exposed host threads would be treated as cores
|
||||
which can be utilized by virtual machines."
|
||||
type: bool
|
||||
ksm:
|
||||
description:
|
||||
- "I I(True) MoM enables to run Kernel Same-page Merging I(KSM) when
|
||||
necessary and when it can yield a memory saving benefit that
|
||||
outweighs its CPU cost."
|
||||
type: bool
|
||||
ksm_numa:
|
||||
description:
|
||||
- "If I(True) enables KSM C(ksm) for best performance inside NUMA nodes."
|
||||
type: bool
|
||||
ha_reservation:
|
||||
description:
|
||||
- "If I(True) enables the oVirt/RHV to monitor cluster capacity for highly
|
||||
available virtual machines."
|
||||
type: bool
|
||||
trusted_service:
|
||||
description:
|
||||
- "If I(True) enables integration with an OpenAttestation server."
|
||||
type: bool
|
||||
vm_reason:
|
||||
description:
|
||||
- "If I(True) enables an optional reason field when a virtual machine
|
||||
is shut down from the Manager, allowing the administrator to
|
||||
provide an explanation for the maintenance."
|
||||
type: bool
|
||||
host_reason:
|
||||
description:
|
||||
- "If I(True) enables an optional reason field when a host is placed
|
||||
into maintenance mode from the Manager, allowing the administrator
|
||||
to provide an explanation for the maintenance."
|
||||
type: bool
|
||||
memory_policy:
|
||||
description:
|
||||
- "I(disabled) - Disables memory page sharing."
|
||||
- "I(server) - Sets the memory page sharing threshold to 150% of the system memory on each host."
|
||||
- "I(desktop) - Sets the memory page sharing threshold to 200% of the system memory on each host."
|
||||
choices: ['disabled', 'server', 'desktop']
|
||||
rng_sources:
|
||||
description:
|
||||
- "List that specify the random number generator devices that all hosts in the cluster will use."
|
||||
- "Supported generators are: I(hwrng) and I(random)."
|
||||
spice_proxy:
|
||||
description:
|
||||
- "The proxy by which the SPICE client will connect to virtual machines."
|
||||
- "The address must be in the following format: I(protocol://[host]:[port])"
|
||||
fence_enabled:
|
||||
description:
|
||||
- "If I(True) enables fencing on the cluster."
|
||||
- "Fencing is enabled by default."
|
||||
type: bool
|
||||
fence_skip_if_gluster_bricks_up:
|
||||
description:
|
||||
- "A flag indicating if fencing should be skipped if Gluster bricks are up and running in the host being fenced."
|
||||
- "This flag is optional, and the default value is `false`."
|
||||
type: bool
|
||||
version_added: "2.8"
|
||||
fence_skip_if_gluster_quorum_not_met:
|
||||
description:
|
||||
- "A flag indicating if fencing should be skipped if Gluster bricks are up and running and Gluster quorum will not
|
||||
be met without those bricks."
|
||||
- "This flag is optional, and the default value is `false`."
|
||||
type: bool
|
||||
version_added: "2.8"
|
||||
fence_skip_if_sd_active:
|
||||
description:
|
||||
- "If I(True) any hosts in the cluster that are Non Responsive
|
||||
and still connected to storage will not be fenced."
|
||||
type: bool
|
||||
fence_skip_if_connectivity_broken:
|
||||
description:
|
||||
- "If I(True) fencing will be temporarily disabled if the percentage
|
||||
of hosts in the cluster that are experiencing connectivity issues
|
||||
is greater than or equal to the defined threshold."
|
||||
- "The threshold can be specified by C(fence_connectivity_threshold)."
|
||||
type: bool
|
||||
fence_connectivity_threshold:
|
||||
description:
|
||||
- "The threshold used by C(fence_skip_if_connectivity_broken)."
|
||||
resilience_policy:
|
||||
description:
|
||||
- "The resilience policy defines how the virtual machines are prioritized in the migration."
|
||||
- "Following values are supported:"
|
||||
- "C(do_not_migrate) - Prevents virtual machines from being migrated. "
|
||||
- "C(migrate) - Migrates all virtual machines in order of their defined priority."
|
||||
- "C(migrate_highly_available) - Migrates only highly available virtual machines to prevent overloading other hosts."
|
||||
choices: ['do_not_migrate', 'migrate', 'migrate_highly_available']
|
||||
migration_bandwidth:
|
||||
description:
|
||||
- "The bandwidth settings define the maximum bandwidth of both outgoing and incoming migrations per host."
|
||||
- "Following bandwidth options are supported:"
|
||||
- "C(auto) - Bandwidth is copied from the I(rate limit) [Mbps] setting in the data center host network QoS."
|
||||
- "C(hypervisor_default) - Bandwidth is controlled by local VDSM setting on sending host."
|
||||
- "C(custom) - Defined by user (in Mbps)."
|
||||
choices: ['auto', 'hypervisor_default', 'custom']
|
||||
migration_bandwidth_limit:
|
||||
description:
|
||||
- "Set the I(custom) migration bandwidth limit."
|
||||
- "This parameter is used only when C(migration_bandwidth) is I(custom)."
|
||||
migration_auto_converge:
|
||||
description:
|
||||
- "If I(True) auto-convergence is used during live migration of virtual machines."
|
||||
- "Used only when C(migration_policy) is set to I(legacy)."
|
||||
- "Following options are supported:"
|
||||
- "C(true) - Override the global setting to I(true)."
|
||||
- "C(false) - Override the global setting to I(false)."
|
||||
- "C(inherit) - Use value which is set globally."
|
||||
choices: ['true', 'false', 'inherit']
|
||||
migration_compressed:
|
||||
description:
|
||||
- "If I(True) compression is used during live migration of the virtual machine."
|
||||
- "Used only when C(migration_policy) is set to I(legacy)."
|
||||
- "Following options are supported:"
|
||||
- "C(true) - Override the global setting to I(true)."
|
||||
- "C(false) - Override the global setting to I(false)."
|
||||
- "C(inherit) - Use value which is set globally."
|
||||
choices: ['true', 'false', 'inherit']
|
||||
migration_policy:
|
||||
description:
|
||||
- "A migration policy defines the conditions for live migrating
|
||||
virtual machines in the event of host failure."
|
||||
- "Following policies are supported:"
|
||||
- "C(legacy) - Legacy behavior of 3.6 version."
|
||||
- "C(minimal_downtime) - Virtual machines should not experience any significant downtime."
|
||||
- "C(suspend_workload) - Virtual machines may experience a more significant downtime."
|
||||
- "C(post_copy) - Virtual machines should not experience any significant downtime.
|
||||
If the VM migration is not converging for a long time, the migration will be switched to post-copy.
|
||||
Added in version I(2.4)."
|
||||
choices: ['legacy', 'minimal_downtime', 'suspend_workload', 'post_copy']
|
||||
serial_policy:
|
||||
description:
|
||||
- "Specify a serial number policy for the virtual machines in the cluster."
|
||||
- "Following options are supported:"
|
||||
- "C(vm) - Sets the virtual machine's UUID as its serial number."
|
||||
- "C(host) - Sets the host's UUID as the virtual machine's serial number."
|
||||
- "C(custom) - Allows you to specify a custom serial number in C(serial_policy_value)."
|
||||
serial_policy_value:
|
||||
description:
|
||||
- "Allows you to specify a custom serial number."
|
||||
- "This parameter is used only when C(serial_policy) is I(custom)."
|
||||
scheduling_policy:
|
||||
description:
|
||||
- "Name of the scheduling policy to be used for cluster."
|
||||
scheduling_policy_properties:
|
||||
description:
|
||||
- "Custom scheduling policy properties of the cluster."
|
||||
- "These optional properties override the properties of the
|
||||
scheduling policy specified by the C(scheduling_policy) parameter."
|
||||
version_added: "2.6"
|
||||
cpu_arch:
|
||||
description:
|
||||
- "CPU architecture of cluster."
|
||||
choices: ['x86_64', 'ppc64', 'undefined']
|
||||
cpu_type:
|
||||
description:
|
||||
- "CPU codename. For example I(Intel SandyBridge Family)."
|
||||
switch_type:
|
||||
description:
|
||||
- "Type of switch to be used by all networks in given cluster.
|
||||
Either I(legacy) which is using linux bridge or I(ovs) using
|
||||
Open vSwitch."
|
||||
choices: ['legacy', 'ovs']
|
||||
compatibility_version:
|
||||
description:
|
||||
- "The compatibility version of the cluster. All hosts in this
|
||||
cluster must support at least this compatibility version."
|
||||
mac_pool:
|
||||
description:
|
||||
- "MAC pool to be used by this cluster."
|
||||
- "C(Note:)"
|
||||
- "This is supported since oVirt version 4.1."
|
||||
version_added: 2.4
|
||||
external_network_providers:
|
||||
description:
|
||||
- "List of references to the external network providers available
|
||||
in the cluster. If the automatic deployment of the external
|
||||
network provider is supported, the networks of the referenced
|
||||
network provider are available on every host in the cluster."
|
||||
- "This is supported since oVirt version 4.2."
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Name of the external network provider. Either C(name) or C(id) is required.
|
||||
id:
|
||||
description:
|
||||
- ID of the external network provider. Either C(name) or C(id) is required.
|
||||
version_added: 2.5
|
||||
firewall_type:
|
||||
description:
|
||||
- "The type of firewall to be used on hosts in this cluster."
|
||||
- "Up to version 4.1, it was always I(iptables). Since version 4.2, you can choose between I(iptables) and I(firewalld).
|
||||
For clusters with a compatibility version of 4.2 and higher, the default firewall type is I(firewalld)."
|
||||
type: str
|
||||
version_added: 2.8
|
||||
choices: ['firewalld', 'iptables']
|
||||
gluster_tuned_profile:
|
||||
description:
|
||||
- "The name of the U(https://fedorahosted.org/tuned) to set on all the hosts in the cluster. This is not mandatory
|
||||
and relevant only for clusters with Gluster service."
|
||||
- "Could be for example I(virtual-host), I(rhgs-sequential-io), I(rhgs-random-io)"
|
||||
version_added: 2.8
|
||||
type: str
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create cluster
|
||||
- ovirt_cluster:
|
||||
data_center: mydatacenter
|
||||
name: mycluster
|
||||
cpu_type: Intel SandyBridge Family
|
||||
description: mycluster
|
||||
compatibility_version: 4.0
|
||||
|
||||
# Create virt service cluster:
|
||||
- ovirt_cluster:
|
||||
data_center: mydatacenter
|
||||
name: mycluster
|
||||
cpu_type: Intel Nehalem Family
|
||||
description: mycluster
|
||||
switch_type: legacy
|
||||
compatibility_version: 4.0
|
||||
ballooning: true
|
||||
gluster: false
|
||||
threads_as_cores: true
|
||||
ha_reservation: true
|
||||
trusted_service: false
|
||||
host_reason: false
|
||||
vm_reason: true
|
||||
ksm_numa: true
|
||||
memory_policy: server
|
||||
rng_sources:
|
||||
- hwrng
|
||||
- random
|
||||
|
||||
# Create cluster with default network provider
|
||||
- ovirt_cluster:
|
||||
name: mycluster
|
||||
data_center: Default
|
||||
cpu_type: Intel SandyBridge Family
|
||||
external_network_providers:
|
||||
- name: ovirt-provider-ovn
|
||||
|
||||
# Remove cluster
|
||||
- ovirt_cluster:
|
||||
state: absent
|
||||
name: mycluster
|
||||
|
||||
# Change cluster Name
|
||||
- ovirt_cluster:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new_cluster_name"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the cluster which is managed
|
||||
returned: On success if cluster is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
cluster:
|
||||
description: "Dictionary of all the cluster attributes. Cluster attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/cluster."
|
||||
type: dict
|
||||
returned: On success if cluster is found.
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
get_id_by_name,
|
||||
)
|
||||
|
||||
|
||||
class ClustersModule(BaseModule):
|
||||
|
||||
def __get_major(self, full_version):
|
||||
if full_version is None:
|
||||
return None
|
||||
if isinstance(full_version, otypes.Version):
|
||||
return full_version.major
|
||||
return int(full_version.split('.')[0])
|
||||
|
||||
def __get_minor(self, full_version):
|
||||
if full_version is None:
|
||||
return None
|
||||
if isinstance(full_version, otypes.Version):
|
||||
return full_version.minor
|
||||
return int(full_version.split('.')[1])
|
||||
|
||||
def param(self, name, default=None):
|
||||
return self._module.params.get(name, default)
|
||||
|
||||
def _get_memory_policy(self):
|
||||
memory_policy = self.param('memory_policy')
|
||||
if memory_policy == 'desktop':
|
||||
return 200
|
||||
elif memory_policy == 'server':
|
||||
return 150
|
||||
elif memory_policy == 'disabled':
|
||||
return 100
|
||||
|
||||
def _get_policy_id(self):
|
||||
# These are hardcoded IDs, once there is API, please fix this.
|
||||
# legacy - 00000000-0000-0000-0000-000000000000
|
||||
# minimal downtime - 80554327-0569-496b-bdeb-fcbbf52b827b
|
||||
# suspend workload if needed - 80554327-0569-496b-bdeb-fcbbf52b827c
|
||||
# post copy - a7aeedb2-8d66-4e51-bb22-32595027ce71
|
||||
migration_policy = self.param('migration_policy')
|
||||
if migration_policy == 'legacy':
|
||||
return '00000000-0000-0000-0000-000000000000'
|
||||
elif migration_policy == 'minimal_downtime':
|
||||
return '80554327-0569-496b-bdeb-fcbbf52b827b'
|
||||
elif migration_policy == 'suspend_workload':
|
||||
return '80554327-0569-496b-bdeb-fcbbf52b827c'
|
||||
elif migration_policy == 'post_copy':
|
||||
return 'a7aeedb2-8d66-4e51-bb22-32595027ce71'
|
||||
|
||||
def _get_sched_policy(self):
|
||||
sched_policy = None
|
||||
if self.param('scheduling_policy'):
|
||||
sched_policies_service = self._connection.system_service().scheduling_policies_service()
|
||||
sched_policy = search_by_name(sched_policies_service, self.param('scheduling_policy'))
|
||||
if not sched_policy:
|
||||
raise Exception("Scheduling policy '%s' was not found" % self.param('scheduling_policy'))
|
||||
|
||||
return sched_policy
|
||||
|
||||
def _get_mac_pool(self):
|
||||
mac_pool = None
|
||||
if self._module.params.get('mac_pool'):
|
||||
mac_pool = search_by_name(
|
||||
self._connection.system_service().mac_pools_service(),
|
||||
self._module.params.get('mac_pool'),
|
||||
)
|
||||
|
||||
return mac_pool
|
||||
|
||||
def _get_external_network_providers(self):
|
||||
return self.param('external_network_providers') or []
|
||||
|
||||
def _get_external_network_provider_id(self, external_provider):
|
||||
return external_provider.get('id') or get_id_by_name(
|
||||
self._connection.system_service().openstack_network_providers_service(),
|
||||
external_provider.get('name')
|
||||
)
|
||||
|
||||
def _get_external_network_providers_entity(self):
|
||||
if self.param('external_network_providers') is not None:
|
||||
return [otypes.ExternalProvider(id=self._get_external_network_provider_id(external_provider))
|
||||
for external_provider in self.param('external_network_providers')]
|
||||
|
||||
def build_entity(self):
|
||||
sched_policy = self._get_sched_policy()
|
||||
return otypes.Cluster(
|
||||
id=self.param('id'),
|
||||
name=self.param('name'),
|
||||
comment=self.param('comment'),
|
||||
description=self.param('description'),
|
||||
ballooning_enabled=self.param('ballooning'),
|
||||
gluster_service=self.param('gluster'),
|
||||
virt_service=self.param('virt'),
|
||||
threads_as_cores=self.param('threads_as_cores'),
|
||||
ha_reservation=self.param('ha_reservation'),
|
||||
trusted_service=self.param('trusted_service'),
|
||||
optional_reason=self.param('vm_reason'),
|
||||
maintenance_reason_required=self.param('host_reason'),
|
||||
scheduling_policy=otypes.SchedulingPolicy(
|
||||
id=sched_policy.id,
|
||||
) if sched_policy else None,
|
||||
serial_number=otypes.SerialNumber(
|
||||
policy=otypes.SerialNumberPolicy(self.param('serial_policy')),
|
||||
value=self.param('serial_policy_value'),
|
||||
) if (
|
||||
self.param('serial_policy') is not None or
|
||||
self.param('serial_policy_value') is not None
|
||||
) else None,
|
||||
migration=otypes.MigrationOptions(
|
||||
auto_converge=otypes.InheritableBoolean(
|
||||
self.param('migration_auto_converge'),
|
||||
) if self.param('migration_auto_converge') else None,
|
||||
bandwidth=otypes.MigrationBandwidth(
|
||||
assignment_method=otypes.MigrationBandwidthAssignmentMethod(
|
||||
self.param('migration_bandwidth'),
|
||||
) if self.param('migration_bandwidth') else None,
|
||||
custom_value=self.param('migration_bandwidth_limit'),
|
||||
) if (
|
||||
self.param('migration_bandwidth') or
|
||||
self.param('migration_bandwidth_limit')
|
||||
) else None,
|
||||
compressed=otypes.InheritableBoolean(
|
||||
self.param('migration_compressed'),
|
||||
) if self.param('migration_compressed') else None,
|
||||
policy=otypes.MigrationPolicy(
|
||||
id=self._get_policy_id()
|
||||
) if self.param('migration_policy') else None,
|
||||
) if (
|
||||
self.param('migration_bandwidth') is not None or
|
||||
self.param('migration_bandwidth_limit') is not None or
|
||||
self.param('migration_auto_converge') is not None or
|
||||
self.param('migration_compressed') is not None or
|
||||
self.param('migration_policy') is not None
|
||||
) else None,
|
||||
error_handling=otypes.ErrorHandling(
|
||||
on_error=otypes.MigrateOnError(
|
||||
self.param('resilience_policy')
|
||||
),
|
||||
) if self.param('resilience_policy') else None,
|
||||
fencing_policy=otypes.FencingPolicy(
|
||||
enabled=self.param('fence_enabled'),
|
||||
skip_if_gluster_bricks_up=self.param('fence_skip_if_gluster_bricks_up'),
|
||||
skip_if_gluster_quorum_not_met=self.param('fence_skip_if_gluster_quorum_not_met'),
|
||||
skip_if_connectivity_broken=otypes.SkipIfConnectivityBroken(
|
||||
enabled=self.param('fence_skip_if_connectivity_broken'),
|
||||
threshold=self.param('fence_connectivity_threshold'),
|
||||
) if (
|
||||
self.param('fence_skip_if_connectivity_broken') is not None or
|
||||
self.param('fence_connectivity_threshold') is not None
|
||||
) else None,
|
||||
skip_if_sd_active=otypes.SkipIfSdActive(
|
||||
enabled=self.param('fence_skip_if_sd_active'),
|
||||
) if self.param('fence_skip_if_sd_active') is not None else None,
|
||||
) if (
|
||||
self.param('fence_enabled') is not None or
|
||||
self.param('fence_skip_if_sd_active') is not None or
|
||||
self.param('fence_skip_if_connectivity_broken') is not None or
|
||||
self.param('fence_skip_if_gluster_bricks_up') is not None or
|
||||
self.param('fence_skip_if_gluster_quorum_not_met') is not None or
|
||||
self.param('fence_connectivity_threshold') is not None
|
||||
) else None,
|
||||
display=otypes.Display(
|
||||
proxy=self.param('spice_proxy'),
|
||||
) if self.param('spice_proxy') else None,
|
||||
required_rng_sources=[
|
||||
otypes.RngSource(rng) for rng in self.param('rng_sources')
|
||||
] if self.param('rng_sources') else None,
|
||||
memory_policy=otypes.MemoryPolicy(
|
||||
over_commit=otypes.MemoryOverCommit(
|
||||
percent=self._get_memory_policy(),
|
||||
),
|
||||
) if self.param('memory_policy') else None,
|
||||
ksm=otypes.Ksm(
|
||||
enabled=self.param('ksm'),
|
||||
merge_across_nodes=not self.param('ksm_numa'),
|
||||
) if (
|
||||
self.param('ksm_numa') is not None or
|
||||
self.param('ksm') is not None
|
||||
) else None,
|
||||
data_center=otypes.DataCenter(
|
||||
name=self.param('data_center'),
|
||||
) if self.param('data_center') else None,
|
||||
management_network=otypes.Network(
|
||||
name=self.param('network'),
|
||||
) if self.param('network') else None,
|
||||
cpu=otypes.Cpu(
|
||||
architecture=otypes.Architecture(
|
||||
self.param('cpu_arch')
|
||||
) if self.param('cpu_arch') else None,
|
||||
type=self.param('cpu_type'),
|
||||
) if (
|
||||
self.param('cpu_arch') or self.param('cpu_type')
|
||||
) else None,
|
||||
version=otypes.Version(
|
||||
major=self.__get_major(self.param('compatibility_version')),
|
||||
minor=self.__get_minor(self.param('compatibility_version')),
|
||||
) if self.param('compatibility_version') else None,
|
||||
switch_type=otypes.SwitchType(
|
||||
self.param('switch_type')
|
||||
) if self.param('switch_type') else None,
|
||||
mac_pool=otypes.MacPool(
|
||||
id=get_id_by_name(self._connection.system_service().mac_pools_service(), self.param('mac_pool'))
|
||||
) if self.param('mac_pool') else None,
|
||||
external_network_providers=self._get_external_network_providers_entity(),
|
||||
custom_scheduling_policy_properties=[
|
||||
otypes.Property(
|
||||
name=sp.get('name'),
|
||||
value=str(sp.get('value')),
|
||||
) for sp in self.param('scheduling_policy_properties') if sp
|
||||
] if self.param('scheduling_policy_properties') is not None else None,
|
||||
firewall_type=otypes.FirewallType(
|
||||
self.param('firewall_type')
|
||||
) if self.param('firewall_type') else None,
|
||||
gluster_tuned_profile=self.param('gluster_tuned_profile'),
|
||||
)
|
||||
|
||||
def _matches_entity(self, item, entity):
|
||||
return equal(item.get('id'), entity.id) and equal(item.get('name'), entity.name)
|
||||
|
||||
def _update_check_external_network_providers(self, entity):
|
||||
if self.param('external_network_providers') is None:
|
||||
return True
|
||||
if entity.external_network_providers is None:
|
||||
return not self.param('external_network_providers')
|
||||
entity_providers = self._connection.follow_link(entity.external_network_providers)
|
||||
entity_provider_ids = [provider.id for provider in entity_providers]
|
||||
entity_provider_names = [provider.name for provider in entity_providers]
|
||||
for provider in self._get_external_network_providers():
|
||||
if provider.get('id'):
|
||||
if provider.get('id') not in entity_provider_ids:
|
||||
return False
|
||||
elif provider.get('name') and provider.get('name') not in entity_provider_names:
|
||||
return False
|
||||
for entity_provider in entity_providers:
|
||||
if not any([self._matches_entity(provider, entity_provider)
|
||||
for provider in self._get_external_network_providers()]):
|
||||
return False
|
||||
return True
|
||||
|
||||
def update_check(self, entity):
|
||||
sched_policy = self._get_sched_policy()
|
||||
migration_policy = getattr(entity.migration, 'policy', None)
|
||||
cluster_cpu = getattr(entity, 'cpu', dict())
|
||||
|
||||
def check_custom_scheduling_policy_properties():
|
||||
if self.param('scheduling_policy_properties'):
|
||||
current = []
|
||||
if entity.custom_scheduling_policy_properties:
|
||||
current = [(sp.name, str(sp.value)) for sp in entity.custom_scheduling_policy_properties]
|
||||
passed = [(sp.get('name'), str(sp.get('value'))) for sp in self.param('scheduling_policy_properties') if sp]
|
||||
for p in passed:
|
||||
if p not in current:
|
||||
return False
|
||||
return True
|
||||
|
||||
return (
|
||||
check_custom_scheduling_policy_properties() and
|
||||
equal(self.param('name'), entity.name) and
|
||||
equal(self.param('comment'), entity.comment) and
|
||||
equal(self.param('description'), entity.description) and
|
||||
equal(self.param('switch_type'), str(entity.switch_type)) and
|
||||
equal(self.param('cpu_arch'), str(getattr(cluster_cpu, 'architecture', None))) and
|
||||
equal(self.param('cpu_type'), getattr(cluster_cpu, 'type', None)) and
|
||||
equal(self.param('ballooning'), entity.ballooning_enabled) and
|
||||
equal(self.param('gluster'), entity.gluster_service) and
|
||||
equal(self.param('virt'), entity.virt_service) and
|
||||
equal(self.param('threads_as_cores'), entity.threads_as_cores) and
|
||||
equal(self.param('ksm_numa'), not entity.ksm.merge_across_nodes) and
|
||||
equal(self.param('ksm'), entity.ksm.enabled) and
|
||||
equal(self.param('ha_reservation'), entity.ha_reservation) and
|
||||
equal(self.param('trusted_service'), entity.trusted_service) and
|
||||
equal(self.param('host_reason'), entity.maintenance_reason_required) and
|
||||
equal(self.param('vm_reason'), entity.optional_reason) and
|
||||
equal(self.param('spice_proxy'), getattr(entity.display, 'proxy', None)) and
|
||||
equal(self.param('fence_enabled'), entity.fencing_policy.enabled) and
|
||||
equal(self.param('fence_skip_if_gluster_bricks_up'), entity.fencing_policy.skip_if_gluster_bricks_up) and
|
||||
equal(self.param('fence_skip_if_gluster_quorum_not_met'), entity.fencing_policy.skip_if_gluster_quorum_not_met) and
|
||||
equal(self.param('fence_skip_if_sd_active'), entity.fencing_policy.skip_if_sd_active.enabled) and
|
||||
equal(self.param('fence_skip_if_connectivity_broken'), entity.fencing_policy.skip_if_connectivity_broken.enabled) and
|
||||
equal(self.param('fence_connectivity_threshold'), entity.fencing_policy.skip_if_connectivity_broken.threshold) and
|
||||
equal(self.param('resilience_policy'), str(entity.error_handling.on_error)) and
|
||||
equal(self.param('migration_bandwidth'), str(entity.migration.bandwidth.assignment_method)) and
|
||||
equal(self.param('migration_auto_converge'), str(entity.migration.auto_converge)) and
|
||||
equal(self.param('migration_compressed'), str(entity.migration.compressed)) and
|
||||
equal(self.param('serial_policy'), str(getattr(entity.serial_number, 'policy', None))) and
|
||||
equal(self.param('serial_policy_value'), getattr(entity.serial_number, 'value', None)) and
|
||||
equal(self.param('scheduling_policy'), getattr(self._connection.follow_link(entity.scheduling_policy), 'name', None)) and
|
||||
equal(self.param('firewall_type'), str(entity.firewall_type)) and
|
||||
equal(self.param('gluster_tuned_profile'), getattr(entity, 'gluster_tuned_profile', None)) and
|
||||
equal(self._get_policy_id(), getattr(migration_policy, 'id', None)) and
|
||||
equal(self._get_memory_policy(), entity.memory_policy.over_commit.percent) and
|
||||
equal(self.__get_minor(self.param('compatibility_version')), self.__get_minor(entity.version)) and
|
||||
equal(self.__get_major(self.param('compatibility_version')), self.__get_major(entity.version)) and
|
||||
equal(
|
||||
self.param('migration_bandwidth_limit') if self.param('migration_bandwidth') == 'custom' else None,
|
||||
entity.migration.bandwidth.custom_value
|
||||
) and
|
||||
equal(
|
||||
sorted(self.param('rng_sources')) if self.param('rng_sources') else None,
|
||||
sorted([
|
||||
str(source) for source in entity.required_rng_sources
|
||||
])
|
||||
) and
|
||||
equal(
|
||||
get_id_by_name(self._connection.system_service().mac_pools_service(), self.param('mac_pool'), raise_error=False),
|
||||
entity.mac_pool.id
|
||||
) and
|
||||
self._update_check_external_network_providers(entity)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(default=None, required=True),
|
||||
id=dict(default=None),
|
||||
ballooning=dict(default=None, type='bool', aliases=['balloon']),
|
||||
gluster=dict(default=None, type='bool'),
|
||||
virt=dict(default=None, type='bool'),
|
||||
threads_as_cores=dict(default=None, type='bool'),
|
||||
ksm_numa=dict(default=None, type='bool'),
|
||||
ksm=dict(default=None, type='bool'),
|
||||
ha_reservation=dict(default=None, type='bool'),
|
||||
trusted_service=dict(default=None, type='bool'),
|
||||
vm_reason=dict(default=None, type='bool'),
|
||||
host_reason=dict(default=None, type='bool'),
|
||||
memory_policy=dict(default=None, choices=['disabled', 'server', 'desktop'], aliases=['performance_preset']),
|
||||
rng_sources=dict(default=None, type='list'),
|
||||
spice_proxy=dict(default=None),
|
||||
fence_enabled=dict(default=None, type='bool'),
|
||||
fence_skip_if_gluster_bricks_up=dict(default=None, type='bool'),
|
||||
fence_skip_if_gluster_quorum_not_met=dict(default=None, type='bool'),
|
||||
fence_skip_if_sd_active=dict(default=None, type='bool'),
|
||||
fence_skip_if_connectivity_broken=dict(default=None, type='bool'),
|
||||
fence_connectivity_threshold=dict(default=None, type='int'),
|
||||
resilience_policy=dict(default=None, choices=['migrate_highly_available', 'migrate', 'do_not_migrate']),
|
||||
migration_bandwidth=dict(default=None, choices=['auto', 'hypervisor_default', 'custom']),
|
||||
migration_bandwidth_limit=dict(default=None, type='int'),
|
||||
migration_auto_converge=dict(default=None, choices=['true', 'false', 'inherit']),
|
||||
migration_compressed=dict(default=None, choices=['true', 'false', 'inherit']),
|
||||
migration_policy=dict(
|
||||
default=None,
|
||||
choices=['legacy', 'minimal_downtime', 'suspend_workload', 'post_copy']
|
||||
),
|
||||
serial_policy=dict(default=None, choices=['vm', 'host', 'custom']),
|
||||
serial_policy_value=dict(default=None),
|
||||
scheduling_policy=dict(default=None),
|
||||
data_center=dict(default=None),
|
||||
description=dict(default=None),
|
||||
comment=dict(default=None),
|
||||
network=dict(default=None),
|
||||
cpu_arch=dict(default=None, choices=['ppc64', 'undefined', 'x86_64']),
|
||||
cpu_type=dict(default=None),
|
||||
switch_type=dict(default=None, choices=['legacy', 'ovs']),
|
||||
compatibility_version=dict(default=None),
|
||||
mac_pool=dict(default=None),
|
||||
external_network_providers=dict(default=None, type='list'),
|
||||
scheduling_policy_properties=dict(type='list'),
|
||||
firewall_type=dict(choices=['iptables', 'firewalld'], default=None),
|
||||
gluster_tuned_profile=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
clusters_service = connection.system_service().clusters_service()
|
||||
clusters_module = ClustersModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=clusters_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = clusters_module.create()
|
||||
elif state == 'absent':
|
||||
ret = clusters_module.remove()
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,120 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_cluster_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV clusters
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV clusters."
|
||||
- This module was called C(ovirt_cluster_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_cluster_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_clusters), which
|
||||
contains a list of clusters. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search cluster X from datacenter Y use following pattern:
|
||||
name=X and datacenter=Y"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all clusters which names start with C<production>:
|
||||
- ovirt_cluster_info:
|
||||
pattern:
|
||||
name: 'production*'
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_clusters }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_clusters:
|
||||
description: "List of dictionaries describing the clusters. Cluster attributes are mapped to dictionary keys,
|
||||
all clusters attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/cluster."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_cluster_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_cluster_facts' module has been renamed to 'ovirt_cluster_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
clusters_service = connection.system_service().clusters_service()
|
||||
clusters = clusters_service.list(search=module.params['pattern'])
|
||||
result = dict(
|
||||
ovirt_clusters=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in clusters
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,233 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_datacenter
|
||||
short_description: Module to manage data centers in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage data centers in oVirt/RHV"
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the datacenter to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- "Name of the data center to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the data center be present or absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
description:
|
||||
description:
|
||||
- "Description of the data center."
|
||||
comment:
|
||||
description:
|
||||
- "Comment of the data center."
|
||||
local:
|
||||
description:
|
||||
- "I(True) if the data center should be local, I(False) if should be shared."
|
||||
- "Default value is set by engine."
|
||||
type: bool
|
||||
compatibility_version:
|
||||
description:
|
||||
- "Compatibility version of the data center."
|
||||
quota_mode:
|
||||
description:
|
||||
- "Quota mode of the data center. One of I(disabled), I(audit) or I(enabled)"
|
||||
choices: ['disabled', 'audit', 'enabled']
|
||||
mac_pool:
|
||||
description:
|
||||
- "MAC pool to be used by this datacenter."
|
||||
- "IMPORTANT: This option is deprecated in oVirt/RHV 4.1. You should
|
||||
use C(mac_pool) in C(ovirt_clusters) module, as MAC pools are
|
||||
set per cluster since 4.1."
|
||||
force:
|
||||
description:
|
||||
- "This parameter can be used only when removing a data center.
|
||||
If I(True) data center will be forcibly removed, even though it
|
||||
contains some clusters. Default value is I(False), which means
|
||||
that only empty data center can be removed."
|
||||
version_added: "2.5"
|
||||
default: False
|
||||
type: bool
|
||||
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create datacenter
|
||||
- ovirt_datacenter:
|
||||
name: mydatacenter
|
||||
local: True
|
||||
compatibility_version: 4.0
|
||||
quota_mode: enabled
|
||||
|
||||
# Remove datacenter
|
||||
- ovirt_datacenter:
|
||||
state: absent
|
||||
name: mydatacenter
|
||||
|
||||
# Change Datacenter Name
|
||||
- ovirt_datacenter:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new_datacenter_name"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: "ID of the managed datacenter"
|
||||
returned: "On success if datacenter is found."
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
data_center:
|
||||
description: "Dictionary of all the datacenter attributes. Datacenter attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/datacenter."
|
||||
returned: "On success if datacenter is found."
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
check_params,
|
||||
create_connection,
|
||||
equal,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
class DatacentersModule(BaseModule):
|
||||
|
||||
def __get_major(self, full_version):
|
||||
if full_version is None:
|
||||
return None
|
||||
if isinstance(full_version, otypes.Version):
|
||||
return full_version.major
|
||||
return int(full_version.split('.')[0])
|
||||
|
||||
def __get_minor(self, full_version):
|
||||
if full_version is None:
|
||||
return None
|
||||
if isinstance(full_version, otypes.Version):
|
||||
return full_version.minor
|
||||
return int(full_version.split('.')[1])
|
||||
|
||||
def _get_mac_pool(self):
|
||||
mac_pool = None
|
||||
if self._module.params.get('mac_pool'):
|
||||
mac_pool = search_by_name(
|
||||
self._connection.system_service().mac_pools_service(),
|
||||
self._module.params.get('mac_pool'),
|
||||
)
|
||||
|
||||
return mac_pool
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.DataCenter(
|
||||
name=self._module.params['name'],
|
||||
id=self._module.params['id'],
|
||||
comment=self._module.params['comment'],
|
||||
description=self._module.params['description'],
|
||||
mac_pool=otypes.MacPool(
|
||||
id=getattr(self._get_mac_pool(), 'id', None),
|
||||
) if self._module.params.get('mac_pool') else None,
|
||||
quota_mode=otypes.QuotaModeType(
|
||||
self._module.params['quota_mode']
|
||||
) if self._module.params['quota_mode'] else None,
|
||||
local=self._module.params['local'],
|
||||
version=otypes.Version(
|
||||
major=self.__get_major(self._module.params['compatibility_version']),
|
||||
minor=self.__get_minor(self._module.params['compatibility_version']),
|
||||
) if self._module.params['compatibility_version'] else None,
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
minor = self.__get_minor(self._module.params.get('compatibility_version'))
|
||||
major = self.__get_major(self._module.params.get('compatibility_version'))
|
||||
return (
|
||||
equal(getattr(self._get_mac_pool(), 'id', None), getattr(entity.mac_pool, 'id', None)) and
|
||||
equal(self._module.params.get('comment'), entity.comment) and
|
||||
equal(self._module.params.get('description'), entity.description) and
|
||||
equal(self._module.params.get('name'), entity.name) and
|
||||
equal(self._module.params.get('quota_mode'), str(entity.quota_mode)) and
|
||||
equal(self._module.params.get('local'), entity.local) and
|
||||
equal(minor, self.__get_minor(entity.version)) and
|
||||
equal(major, self.__get_major(entity.version))
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(default=None, required=True),
|
||||
description=dict(default=None),
|
||||
local=dict(type='bool'),
|
||||
id=dict(default=None),
|
||||
compatibility_version=dict(default=None),
|
||||
quota_mode=dict(choices=['disabled', 'audit', 'enabled']),
|
||||
comment=dict(default=None),
|
||||
mac_pool=dict(default=None),
|
||||
force=dict(default=None, type='bool'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
data_centers_service = connection.system_service().data_centers_service()
|
||||
data_centers_module = DatacentersModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=data_centers_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = data_centers_module.create()
|
||||
elif state == 'absent':
|
||||
ret = data_centers_module.remove(force=module.params['force'])
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,103 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_datacenter_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV datacenters
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV datacenters."
|
||||
- This module was called C(ovirt_datacenter_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_datacenter_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_datacenters), which
|
||||
contains a list of datacenters. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search datacenter I(X) use following pattern: I(name=X)"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all data centers which names start with C(production):
|
||||
- ovirt_datacenter_info:
|
||||
pattern: name=production*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_datacenters }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_datacenters:
|
||||
description: "List of dictionaries describing the datacenters. Datacenter attributes are mapped to dictionary keys,
|
||||
all datacenters attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/data_center."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_datacenter_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_datacenter_facts' module has been renamed to 'ovirt_datacenter_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
datacenters_service = connection.system_service().data_centers_service()
|
||||
datacenters = datacenters_service.list(search=module.params['pattern'])
|
||||
result = dict(
|
||||
ovirt_datacenters=[
|
||||
get_dict_of_struct(
|
||||
struct=d,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for d in datacenters
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,838 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_disk
|
||||
short_description: "Module to manage Virtual Machine and floating disks in oVirt/RHV"
|
||||
version_added: "2.2"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage Virtual Machine and floating disks in oVirt/RHV."
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the disk to manage. Either C(id) or C(name) is required."
|
||||
name:
|
||||
description:
|
||||
- "Name of the disk to manage. Either C(id) or C(name)/C(alias) is required."
|
||||
aliases: ['alias']
|
||||
description:
|
||||
description:
|
||||
- "Description of the disk image to manage."
|
||||
version_added: "2.5"
|
||||
vm_name:
|
||||
description:
|
||||
- "Name of the Virtual Machine to manage. Either C(vm_id) or C(vm_name) is required if C(state) is I(attached) or I(detached)."
|
||||
vm_id:
|
||||
description:
|
||||
- "ID of the Virtual Machine to manage. Either C(vm_id) or C(vm_name) is required if C(state) is I(attached) or I(detached)."
|
||||
state:
|
||||
description:
|
||||
- "Should the Virtual Machine disk be present/absent/attached/detached/exported/imported."
|
||||
choices: ['present', 'absent', 'attached', 'detached', 'exported', 'imported']
|
||||
default: 'present'
|
||||
download_image_path:
|
||||
description:
|
||||
- "Path on a file system where disk should be downloaded."
|
||||
- "Note that you must have an valid oVirt/RHV engine CA in your system trust store
|
||||
or you must provide it in C(ca_file) parameter."
|
||||
- "Note that the disk is not downloaded when the file already exists,
|
||||
but you can forcibly download the disk when using C(force) I (true)."
|
||||
version_added: "2.3"
|
||||
upload_image_path:
|
||||
description:
|
||||
- "Path to disk image, which should be uploaded."
|
||||
- "Note that currently we support only compatibility version 0.10 of the qcow disk."
|
||||
- "Note that you must have an valid oVirt/RHV engine CA in your system trust store
|
||||
or you must provide it in C(ca_file) parameter."
|
||||
- "Note that there is no reliable way to achieve idempotency, so
|
||||
if you want to upload the disk even if the disk with C(id) or C(name) exists,
|
||||
then please use C(force) I(true). If you will use C(force) I(false), which
|
||||
is default, then the disk image won't be uploaded."
|
||||
version_added: "2.3"
|
||||
size:
|
||||
description:
|
||||
- "Size of the disk. Size should be specified using IEC standard units.
|
||||
For example 10GiB, 1024MiB, etc."
|
||||
- "Size can be only increased, not decreased."
|
||||
interface:
|
||||
description:
|
||||
- "Driver of the storage interface."
|
||||
- "It's required parameter when creating the new disk."
|
||||
choices: ['virtio', 'ide', 'virtio_scsi']
|
||||
format:
|
||||
description:
|
||||
- Specify format of the disk.
|
||||
- Note that this option isn't idempotent as it's not currently possible to change format of the disk via API.
|
||||
choices: ['raw', 'cow']
|
||||
content_type:
|
||||
description:
|
||||
- Specify if the disk is a data disk or ISO image or a one of a the Hosted Engine disk types
|
||||
- The Hosted Engine disk content types are available with Engine 4.3+ and Ansible 2.8
|
||||
choices: ['data', 'iso', 'hosted_engine', 'hosted_engine_sanlock', 'hosted_engine_metadata', 'hosted_engine_configuration']
|
||||
default: 'data'
|
||||
version_added: "2.8"
|
||||
sparse:
|
||||
required: False
|
||||
type: bool
|
||||
version_added: "2.5"
|
||||
description:
|
||||
- "I(True) if the disk should be sparse (also known as I(thin provision)).
|
||||
If the parameter is omitted, cow disks will be created as sparse and raw disks as I(preallocated)"
|
||||
- Note that this option isn't idempotent as it's not currently possible to change sparseness of the disk via API.
|
||||
storage_domain:
|
||||
description:
|
||||
- "Storage domain name where disk should be created."
|
||||
storage_domains:
|
||||
description:
|
||||
- "Storage domain names where disk should be copied."
|
||||
- "C(**IMPORTANT**)"
|
||||
- "There is no reliable way to achieve idempotency, so every time
|
||||
you specify this parameter the disks are copied, so please handle
|
||||
your playbook accordingly to not copy the disks all the time. This
|
||||
is valid only for VM and floating disks, template disks works
|
||||
as expected."
|
||||
version_added: "2.3"
|
||||
force:
|
||||
description:
|
||||
- "Please take a look at C(image_path) documentation to see the correct
|
||||
usage of this parameter."
|
||||
version_added: "2.3"
|
||||
type: bool
|
||||
profile:
|
||||
description:
|
||||
- "Disk profile name to be attached to disk. By default profile is chosen by oVirt/RHV engine."
|
||||
quota_id:
|
||||
description:
|
||||
- "Disk quota ID to be used for disk. By default quota is chosen by oVirt/RHV engine."
|
||||
version_added: "2.5"
|
||||
bootable:
|
||||
description:
|
||||
- "I(True) if the disk should be bootable. By default when disk is created it isn't bootable."
|
||||
type: bool
|
||||
default: 'no'
|
||||
shareable:
|
||||
description:
|
||||
- "I(True) if the disk should be shareable. By default when disk is created it isn't shareable."
|
||||
type: bool
|
||||
logical_unit:
|
||||
description:
|
||||
- "Dictionary which describes LUN to be directly attached to VM:"
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- Address of the storage server. Used by iSCSI.
|
||||
port:
|
||||
description:
|
||||
- Port of the storage server. Used by iSCSI.
|
||||
target:
|
||||
description:
|
||||
- iSCSI target.
|
||||
lun_id:
|
||||
description:
|
||||
- LUN id.
|
||||
username:
|
||||
description:
|
||||
- CHAP Username to be used to access storage server. Used by iSCSI.
|
||||
password:
|
||||
description:
|
||||
- CHAP Password of the user to be used to access storage server. Used by iSCSI.
|
||||
storage_type:
|
||||
description:
|
||||
- Storage type either I(fcp) or I(iscsi).
|
||||
sparsify:
|
||||
description:
|
||||
- "I(True) if the disk should be sparsified."
|
||||
- "Sparsification frees space in the disk image that is not used by
|
||||
its filesystem. As a result, the image will occupy less space on
|
||||
the storage."
|
||||
- "Note that this parameter isn't idempotent, as it's not possible
|
||||
to check if the disk should be or should not be sparsified."
|
||||
version_added: "2.4"
|
||||
type: bool
|
||||
openstack_volume_type:
|
||||
description:
|
||||
- "Name of the openstack volume type. This is valid when working
|
||||
with cinder."
|
||||
version_added: "2.4"
|
||||
image_provider:
|
||||
description:
|
||||
- "When C(state) is I(exported) disk is exported to given Glance image provider."
|
||||
- "When C(state) is I(imported) disk is imported from given Glance image provider."
|
||||
- "C(**IMPORTANT**)"
|
||||
- "There is no reliable way to achieve idempotency, so every time
|
||||
you specify this parameter the disk is exported, so please handle
|
||||
your playbook accordingly to not export the disk all the time.
|
||||
This option is valid only for template disks."
|
||||
version_added: "2.4"
|
||||
host:
|
||||
description:
|
||||
- "When the hypervisor name is specified the newly created disk or
|
||||
an existing disk will refresh its information about the
|
||||
underlying storage( Disk size, Serial, Product ID, Vendor ID ...)
|
||||
The specified host will be used for gathering the storage
|
||||
related information. This option is only valid for passthrough
|
||||
disks. This option requires at least the logical_unit.id to be
|
||||
specified"
|
||||
version_added: "2.8"
|
||||
wipe_after_delete:
|
||||
description:
|
||||
- "If the disk's Wipe After Delete is enabled, then the disk is first wiped."
|
||||
type: bool
|
||||
activate:
|
||||
description:
|
||||
- I(True) if the disk should be activated.
|
||||
- When creating disk of virtual machine it is set to I(True).
|
||||
version_added: "2.8"
|
||||
type: bool
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create and attach new disk to VM
|
||||
- ovirt_disk:
|
||||
name: myvm_disk
|
||||
vm_name: rhel7
|
||||
size: 10GiB
|
||||
format: cow
|
||||
interface: virtio
|
||||
storage_domain: data
|
||||
|
||||
# Attach logical unit to VM rhel7
|
||||
- ovirt_disk:
|
||||
vm_name: rhel7
|
||||
logical_unit:
|
||||
target: iqn.2016-08-09.brq.str-01:omachace
|
||||
id: 1IET_000d0001
|
||||
address: 10.34.63.204
|
||||
interface: virtio
|
||||
|
||||
# Detach disk from VM
|
||||
- ovirt_disk:
|
||||
state: detached
|
||||
name: myvm_disk
|
||||
vm_name: rhel7
|
||||
size: 10GiB
|
||||
format: cow
|
||||
interface: virtio
|
||||
|
||||
# Change Disk Name
|
||||
- ovirt_disk:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
storage_domain: data
|
||||
name: "new_disk_name"
|
||||
vm_name: rhel7
|
||||
|
||||
# Upload local image to disk and attach it to vm:
|
||||
# Since Ansible 2.3
|
||||
- ovirt_disk:
|
||||
name: mydisk
|
||||
vm_name: myvm
|
||||
interface: virtio
|
||||
size: 10GiB
|
||||
format: cow
|
||||
image_path: /path/to/mydisk.qcow2
|
||||
storage_domain: data
|
||||
|
||||
# Download disk to local file system:
|
||||
# Since Ansible 2.3
|
||||
- ovirt_disk:
|
||||
id: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
download_image_path: /home/user/mydisk.qcow2
|
||||
|
||||
# Export disk as image to Glance domain
|
||||
# Since Ansible 2.4
|
||||
- ovirt_disk:
|
||||
id: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
image_provider: myglance
|
||||
state: exported
|
||||
|
||||
# Defining a specific quota while creating a disk image:
|
||||
# Since Ansible 2.5
|
||||
- ovirt_quotas_facts:
|
||||
data_center: Default
|
||||
name: myquota
|
||||
- ovirt_disk:
|
||||
name: mydisk
|
||||
size: 10GiB
|
||||
storage_domain: data
|
||||
description: somedescriptionhere
|
||||
quota_id: "{{ ovirt_quotas[0]['id'] }}"
|
||||
|
||||
# Upload an ISO image
|
||||
# Since Ansible 2.8
|
||||
- ovirt_disk:
|
||||
name: myiso
|
||||
upload_image_path: /path/to/iso/image
|
||||
storage_domain: data
|
||||
size: 4 GiB
|
||||
wait: true
|
||||
bootable: true
|
||||
format: raw
|
||||
content_type: iso
|
||||
|
||||
# Add fiber chanel disk
|
||||
- name: Create disk
|
||||
ovirt_disk:
|
||||
name: fcp_disk
|
||||
host: my_host
|
||||
logical_unit:
|
||||
id: 3600a09803830447a4f244c4657597777
|
||||
storage_type: fcp
|
||||
'''
|
||||
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: "ID of the managed disk"
|
||||
returned: "On success if disk is found."
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
disk:
|
||||
description: "Dictionary of all the disk attributes. Disk attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/disk."
|
||||
returned: "On success if disk is found and C(vm_id) or C(vm_name) wasn't passed."
|
||||
type: dict
|
||||
|
||||
disk_attachment:
|
||||
description: "Dictionary of all the disk attachment attributes. Disk attachment attributes can be found
|
||||
on your oVirt/RHV instance at following url:
|
||||
http://ovirt.github.io/ovirt-engine-api-model/master/#types/disk_attachment."
|
||||
returned: "On success if disk is found and C(vm_id) or C(vm_name) was passed and VM was found."
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
import ssl
|
||||
|
||||
from ansible.module_utils.six.moves.http_client import HTTPSConnection, IncompleteRead
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
check_params,
|
||||
create_connection,
|
||||
convert_to_bytes,
|
||||
equal,
|
||||
follow_link,
|
||||
get_id_by_name,
|
||||
ovirt_full_argument_spec,
|
||||
get_dict_of_struct,
|
||||
search_by_name,
|
||||
wait,
|
||||
)
|
||||
|
||||
|
||||
def _search_by_lun(disks_service, lun_id):
|
||||
"""
|
||||
Find disk by LUN ID.
|
||||
"""
|
||||
res = [
|
||||
disk for disk in disks_service.list(search='disk_type=lun') if (
|
||||
disk.lun_storage.id == lun_id
|
||||
)
|
||||
]
|
||||
return res[0] if res else None
|
||||
|
||||
|
||||
def transfer(connection, module, direction, transfer_func):
|
||||
transfers_service = connection.system_service().image_transfers_service()
|
||||
transfer = transfers_service.add(
|
||||
otypes.ImageTransfer(
|
||||
image=otypes.Image(
|
||||
id=module.params['id'],
|
||||
),
|
||||
direction=direction,
|
||||
)
|
||||
)
|
||||
transfer_service = transfers_service.image_transfer_service(transfer.id)
|
||||
|
||||
try:
|
||||
# After adding a new transfer for the disk, the transfer's status will be INITIALIZING.
|
||||
# Wait until the init phase is over. The actual transfer can start when its status is "Transferring".
|
||||
while transfer.phase == otypes.ImageTransferPhase.INITIALIZING:
|
||||
time.sleep(module.params['poll_interval'])
|
||||
transfer = transfer_service.get()
|
||||
|
||||
proxy_url = urlparse(transfer.proxy_url)
|
||||
context = ssl.create_default_context()
|
||||
auth = module.params['auth']
|
||||
if auth.get('insecure'):
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
elif auth.get('ca_file'):
|
||||
context.load_verify_locations(cafile=auth.get('ca_file'))
|
||||
|
||||
proxy_connection = HTTPSConnection(
|
||||
proxy_url.hostname,
|
||||
proxy_url.port,
|
||||
context=context,
|
||||
)
|
||||
|
||||
transfer_func(
|
||||
transfer_service,
|
||||
proxy_connection,
|
||||
proxy_url,
|
||||
transfer.signed_ticket
|
||||
)
|
||||
return True
|
||||
finally:
|
||||
transfer_service.finalize()
|
||||
while transfer.phase in [
|
||||
otypes.ImageTransferPhase.TRANSFERRING,
|
||||
otypes.ImageTransferPhase.FINALIZING_SUCCESS,
|
||||
]:
|
||||
time.sleep(module.params['poll_interval'])
|
||||
transfer = transfer_service.get()
|
||||
if transfer.phase in [
|
||||
otypes.ImageTransferPhase.UNKNOWN,
|
||||
otypes.ImageTransferPhase.FINISHED_FAILURE,
|
||||
otypes.ImageTransferPhase.FINALIZING_FAILURE,
|
||||
otypes.ImageTransferPhase.CANCELLED,
|
||||
]:
|
||||
raise Exception(
|
||||
"Error occurred while uploading image. The transfer is in %s" % transfer.phase
|
||||
)
|
||||
if not module.params.get('logical_unit'):
|
||||
disks_service = connection.system_service().disks_service()
|
||||
wait(
|
||||
service=disks_service.service(module.params['id']),
|
||||
condition=lambda d: d.status == otypes.DiskStatus.OK,
|
||||
wait=module.params['wait'],
|
||||
timeout=module.params['timeout'],
|
||||
)
|
||||
|
||||
|
||||
def download_disk_image(connection, module):
|
||||
def _transfer(transfer_service, proxy_connection, proxy_url, transfer_ticket):
|
||||
BUF_SIZE = 128 * 1024
|
||||
transfer_headers = {
|
||||
'Authorization': transfer_ticket,
|
||||
}
|
||||
proxy_connection.request(
|
||||
'GET',
|
||||
proxy_url.path,
|
||||
headers=transfer_headers,
|
||||
)
|
||||
r = proxy_connection.getresponse()
|
||||
path = module.params["download_image_path"]
|
||||
image_size = int(r.getheader('Content-Length'))
|
||||
with open(path, "wb") as mydisk:
|
||||
pos = 0
|
||||
while pos < image_size:
|
||||
to_read = min(image_size - pos, BUF_SIZE)
|
||||
chunk = r.read(to_read)
|
||||
if not chunk:
|
||||
raise RuntimeError("Socket disconnected")
|
||||
mydisk.write(chunk)
|
||||
pos += len(chunk)
|
||||
|
||||
return transfer(
|
||||
connection,
|
||||
module,
|
||||
otypes.ImageTransferDirection.DOWNLOAD,
|
||||
transfer_func=_transfer,
|
||||
)
|
||||
|
||||
|
||||
def upload_disk_image(connection, module):
|
||||
def _transfer(transfer_service, proxy_connection, proxy_url, transfer_ticket):
|
||||
BUF_SIZE = 128 * 1024
|
||||
path = module.params['upload_image_path']
|
||||
|
||||
image_size = os.path.getsize(path)
|
||||
proxy_connection.putrequest("PUT", proxy_url.path)
|
||||
proxy_connection.putheader('Content-Length', "%d" % (image_size,))
|
||||
proxy_connection.endheaders()
|
||||
with open(path, "rb") as disk:
|
||||
pos = 0
|
||||
while pos < image_size:
|
||||
to_read = min(image_size - pos, BUF_SIZE)
|
||||
chunk = disk.read(to_read)
|
||||
if not chunk:
|
||||
transfer_service.pause()
|
||||
raise RuntimeError("Unexpected end of file at pos=%d" % pos)
|
||||
proxy_connection.send(chunk)
|
||||
pos += len(chunk)
|
||||
|
||||
return transfer(
|
||||
connection,
|
||||
module,
|
||||
otypes.ImageTransferDirection.UPLOAD,
|
||||
transfer_func=_transfer,
|
||||
)
|
||||
|
||||
|
||||
class DisksModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
hosts_service = self._connection.system_service().hosts_service()
|
||||
logical_unit = self._module.params.get('logical_unit')
|
||||
disk = otypes.Disk(
|
||||
id=self._module.params.get('id'),
|
||||
name=self._module.params.get('name'),
|
||||
description=self._module.params.get('description'),
|
||||
format=otypes.DiskFormat(
|
||||
self._module.params.get('format')
|
||||
) if self._module.params.get('format') else None,
|
||||
content_type=otypes.DiskContentType(
|
||||
self._module.params.get('content_type')
|
||||
) if self._module.params.get('content_type') else None,
|
||||
sparse=self._module.params.get(
|
||||
'sparse'
|
||||
) if self._module.params.get(
|
||||
'sparse'
|
||||
) is not None else self._module.params.get('format') != 'raw',
|
||||
openstack_volume_type=otypes.OpenStackVolumeType(
|
||||
name=self.param('openstack_volume_type')
|
||||
) if self.param('openstack_volume_type') else None,
|
||||
provisioned_size=convert_to_bytes(
|
||||
self._module.params.get('size')
|
||||
),
|
||||
storage_domains=[
|
||||
otypes.StorageDomain(
|
||||
name=self._module.params.get('storage_domain'),
|
||||
),
|
||||
],
|
||||
quota=otypes.Quota(id=self._module.params.get('quota_id')) if self.param('quota_id') else None,
|
||||
shareable=self._module.params.get('shareable'),
|
||||
wipe_after_delete=self.param('wipe_after_delete'),
|
||||
lun_storage=otypes.HostStorage(
|
||||
host=otypes.Host(
|
||||
id=get_id_by_name(hosts_service, self._module.params.get('host'))
|
||||
) if self.param('host') else None,
|
||||
type=otypes.StorageType(
|
||||
logical_unit.get('storage_type', 'iscsi')
|
||||
),
|
||||
logical_units=[
|
||||
otypes.LogicalUnit(
|
||||
address=logical_unit.get('address'),
|
||||
port=logical_unit.get('port', 3260),
|
||||
target=logical_unit.get('target'),
|
||||
id=logical_unit.get('id'),
|
||||
username=logical_unit.get('username'),
|
||||
password=logical_unit.get('password'),
|
||||
)
|
||||
],
|
||||
) if logical_unit else None,
|
||||
)
|
||||
if hasattr(disk, 'initial_size') and self._module.params['upload_image_path']:
|
||||
disk.initial_size = convert_to_bytes(
|
||||
self._module.params.get('size')
|
||||
)
|
||||
|
||||
return disk
|
||||
|
||||
def update_storage_domains(self, disk_id):
|
||||
changed = False
|
||||
disk_service = self._service.service(disk_id)
|
||||
disk = disk_service.get()
|
||||
sds_service = self._connection.system_service().storage_domains_service()
|
||||
|
||||
# We don't support move© for non file based storages:
|
||||
if disk.storage_type != otypes.DiskStorageType.IMAGE:
|
||||
return changed
|
||||
|
||||
# Initiate move:
|
||||
if self._module.params['storage_domain']:
|
||||
new_disk_storage_id = get_id_by_name(sds_service, self._module.params['storage_domain'])
|
||||
changed = self.action(
|
||||
action='move',
|
||||
entity=disk,
|
||||
action_condition=lambda d: new_disk_storage_id != d.storage_domains[0].id,
|
||||
wait_condition=lambda d: d.status == otypes.DiskStatus.OK,
|
||||
storage_domain=otypes.StorageDomain(
|
||||
id=new_disk_storage_id,
|
||||
),
|
||||
post_action=lambda _: time.sleep(self._module.params['poll_interval']),
|
||||
)['changed']
|
||||
|
||||
if self._module.params['storage_domains']:
|
||||
for sd in self._module.params['storage_domains']:
|
||||
new_disk_storage = search_by_name(sds_service, sd)
|
||||
changed = changed or self.action(
|
||||
action='copy',
|
||||
entity=disk,
|
||||
action_condition=(
|
||||
lambda disk: new_disk_storage.id not in [sd.id for sd in disk.storage_domains]
|
||||
),
|
||||
wait_condition=lambda disk: disk.status == otypes.DiskStatus.OK,
|
||||
storage_domain=otypes.StorageDomain(
|
||||
id=new_disk_storage.id,
|
||||
),
|
||||
)['changed']
|
||||
|
||||
return changed
|
||||
|
||||
def _update_check(self, entity):
|
||||
return (
|
||||
equal(self._module.params.get('name'), entity.name) and
|
||||
equal(self._module.params.get('description'), entity.description) and
|
||||
equal(self.param('quota_id'), getattr(entity.quota, 'id', None)) and
|
||||
equal(convert_to_bytes(self._module.params.get('size')), entity.provisioned_size) and
|
||||
equal(self._module.params.get('shareable'), entity.shareable) and
|
||||
equal(self.param('wipe_after_delete'), entity.wipe_after_delete)
|
||||
)
|
||||
|
||||
|
||||
class DiskAttachmentsModule(DisksModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.DiskAttachment(
|
||||
disk=super(DiskAttachmentsModule, self).build_entity(),
|
||||
interface=otypes.DiskInterface(
|
||||
self._module.params.get('interface')
|
||||
) if self._module.params.get('interface') else None,
|
||||
bootable=self._module.params.get('bootable'),
|
||||
active=self.param('activate'),
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
return (
|
||||
super(DiskAttachmentsModule, self)._update_check(follow_link(self._connection, entity.disk)) and
|
||||
equal(self._module.params.get('interface'), str(entity.interface)) and
|
||||
equal(self._module.params.get('bootable'), entity.bootable) and
|
||||
equal(self.param('activate'), entity.active)
|
||||
)
|
||||
|
||||
|
||||
def searchable_attributes(module):
|
||||
"""
|
||||
Return all searchable disk attributes passed to module.
|
||||
"""
|
||||
attributes = {
|
||||
'name': module.params.get('name'),
|
||||
'Storage.name': module.params.get('storage_domain'),
|
||||
'vm_names': module.params.get('vm_name'),
|
||||
}
|
||||
return dict((k, v) for k, v in attributes.items() if v is not None)
|
||||
|
||||
|
||||
def get_vm_service(connection, module):
|
||||
if module.params.get('vm_id') is not None or module.params.get('vm_name') is not None and module.params['state'] != 'absent':
|
||||
vms_service = connection.system_service().vms_service()
|
||||
|
||||
# If `vm_id` isn't specified, find VM by name:
|
||||
vm_id = module.params['vm_id']
|
||||
if vm_id is None:
|
||||
vm_id = get_id_by_name(vms_service, module.params['vm_name'])
|
||||
|
||||
if vm_id is None:
|
||||
module.fail_json(
|
||||
msg="VM don't exists, please create it first."
|
||||
)
|
||||
|
||||
return vms_service.vm_service(vm_id)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent', 'attached', 'detached', 'exported', 'imported'],
|
||||
default='present'
|
||||
),
|
||||
id=dict(default=None),
|
||||
name=dict(default=None, aliases=['alias']),
|
||||
description=dict(default=None),
|
||||
vm_name=dict(default=None),
|
||||
vm_id=dict(default=None),
|
||||
size=dict(default=None),
|
||||
interface=dict(default=None,),
|
||||
storage_domain=dict(default=None),
|
||||
storage_domains=dict(default=None, type='list'),
|
||||
profile=dict(default=None),
|
||||
quota_id=dict(default=None),
|
||||
format=dict(default='cow', choices=['raw', 'cow']),
|
||||
content_type=dict(
|
||||
default='data',
|
||||
choices=['data', 'iso', 'hosted_engine', 'hosted_engine_sanlock', 'hosted_engine_metadata', 'hosted_engine_configuration']
|
||||
),
|
||||
sparse=dict(default=None, type='bool'),
|
||||
bootable=dict(default=None, type='bool'),
|
||||
shareable=dict(default=None, type='bool'),
|
||||
logical_unit=dict(default=None, type='dict'),
|
||||
download_image_path=dict(default=None),
|
||||
upload_image_path=dict(default=None, aliases=['image_path']),
|
||||
force=dict(default=False, type='bool'),
|
||||
sparsify=dict(default=None, type='bool'),
|
||||
openstack_volume_type=dict(default=None),
|
||||
image_provider=dict(default=None),
|
||||
host=dict(default=None),
|
||||
wipe_after_delete=dict(type='bool', default=None),
|
||||
activate=dict(default=None, type='bool'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
lun = module.params.get('logical_unit')
|
||||
host = module.params['host']
|
||||
# Fail when host is specified with the LUN id. Lun id is needed to identify
|
||||
# an existing disk if already available inthe environment.
|
||||
if (host and lun is None) or (host and lun.get("id") is None):
|
||||
module.fail_json(
|
||||
msg="Can not use parameter host ({0!s}) without "
|
||||
"specifying the logical_unit id".format(host)
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
|
||||
try:
|
||||
disk = None
|
||||
state = module.params['state']
|
||||
auth = module.params.get('auth')
|
||||
connection = create_connection(auth)
|
||||
disks_service = connection.system_service().disks_service()
|
||||
disks_module = DisksModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=disks_service,
|
||||
)
|
||||
|
||||
force_create = False
|
||||
vm_service = get_vm_service(connection, module)
|
||||
if lun:
|
||||
disk = _search_by_lun(disks_service, lun.get('id'))
|
||||
else:
|
||||
disk = disks_module.search_entity(search_params=searchable_attributes(module))
|
||||
if vm_service and disk:
|
||||
# If the VM don't exist in VMs disks, but still it's found it means it was found
|
||||
# for template with same name as VM, so we should force create the VM disk.
|
||||
force_create = disk.id not in [a.disk.id for a in vm_service.disk_attachments_service().list() if a.disk]
|
||||
|
||||
ret = None
|
||||
# First take care of creating the VM, if needed:
|
||||
if state in ('present', 'detached', 'attached'):
|
||||
# Always activate disk when its being created
|
||||
if vm_service is not None and disk is None:
|
||||
module.params['activate'] = True
|
||||
ret = disks_module.create(
|
||||
entity=disk if not force_create else None,
|
||||
result_state=otypes.DiskStatus.OK if lun is None else None,
|
||||
fail_condition=lambda d: d.status == otypes.DiskStatus.ILLEGAL if lun is None else False,
|
||||
force_create=force_create,
|
||||
)
|
||||
is_new_disk = ret['changed']
|
||||
ret['changed'] = ret['changed'] or disks_module.update_storage_domains(ret['id'])
|
||||
# We need to pass ID to the module, so in case we want detach/attach disk
|
||||
# we have this ID specified to attach/detach method:
|
||||
module.params['id'] = ret['id']
|
||||
|
||||
# Upload disk image in case it's new disk or force parameter is passed:
|
||||
if module.params['upload_image_path'] and (is_new_disk or module.params['force']):
|
||||
uploaded = upload_disk_image(connection, module)
|
||||
ret['changed'] = ret['changed'] or uploaded
|
||||
# Download disk image in case it's file don't exist or force parameter is passed:
|
||||
if (
|
||||
module.params['download_image_path'] and (not os.path.isfile(module.params['download_image_path']) or module.params['force'])
|
||||
):
|
||||
downloaded = download_disk_image(connection, module)
|
||||
ret['changed'] = ret['changed'] or downloaded
|
||||
|
||||
# Disk sparsify, only if disk is of image type:
|
||||
if not module.check_mode:
|
||||
disk = disks_service.disk_service(module.params['id']).get()
|
||||
if disk.storage_type == otypes.DiskStorageType.IMAGE:
|
||||
ret = disks_module.action(
|
||||
action='sparsify',
|
||||
action_condition=lambda d: module.params['sparsify'],
|
||||
wait_condition=lambda d: d.status == otypes.DiskStatus.OK,
|
||||
)
|
||||
|
||||
# Export disk as image to glance domain
|
||||
elif state == 'exported':
|
||||
disk = disks_module.search_entity()
|
||||
if disk is None:
|
||||
module.fail_json(
|
||||
msg="Can not export given disk '%s', it doesn't exist" %
|
||||
module.params.get('name') or module.params.get('id')
|
||||
)
|
||||
if disk.storage_type == otypes.DiskStorageType.IMAGE:
|
||||
ret = disks_module.action(
|
||||
action='export',
|
||||
action_condition=lambda d: module.params['image_provider'],
|
||||
wait_condition=lambda d: d.status == otypes.DiskStatus.OK,
|
||||
storage_domain=otypes.StorageDomain(name=module.params['image_provider']),
|
||||
)
|
||||
elif state == 'imported':
|
||||
glance_service = connection.system_service().openstack_image_providers_service()
|
||||
image_provider = search_by_name(glance_service, module.params['image_provider'])
|
||||
images_service = glance_service.service(image_provider.id).images_service()
|
||||
entity_id = get_id_by_name(images_service, module.params['name'])
|
||||
images_service.service(entity_id).import_(
|
||||
storage_domain=otypes.StorageDomain(
|
||||
name=module.params['storage_domain']
|
||||
) if module.params['storage_domain'] else None,
|
||||
disk=otypes.Disk(
|
||||
name=module.params['name']
|
||||
),
|
||||
import_as_template=False,
|
||||
)
|
||||
# Wait for disk to appear in system:
|
||||
disk = disks_module.wait_for_import(
|
||||
condition=lambda t: t.status == otypes.DiskStatus.OK
|
||||
)
|
||||
ret = disks_module.create(result_state=otypes.DiskStatus.OK)
|
||||
elif state == 'absent':
|
||||
ret = disks_module.remove()
|
||||
|
||||
# If VM was passed attach/detach disks to/from the VM:
|
||||
if vm_service:
|
||||
disk_attachments_service = vm_service.disk_attachments_service()
|
||||
disk_attachments_module = DiskAttachmentsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=disk_attachments_service,
|
||||
changed=ret['changed'] if ret else False,
|
||||
)
|
||||
|
||||
if state == 'present' or state == 'attached':
|
||||
ret = disk_attachments_module.create()
|
||||
if lun is None:
|
||||
wait(
|
||||
service=disk_attachments_service.service(ret['id']),
|
||||
condition=lambda d: follow_link(connection, d.disk).status == otypes.DiskStatus.OK,
|
||||
wait=module.params['wait'],
|
||||
timeout=module.params['timeout'],
|
||||
)
|
||||
elif state == 'detached':
|
||||
ret = disk_attachments_module.remove()
|
||||
|
||||
# When the host parameter is specified and the disk is not being
|
||||
# removed, refresh the information about the LUN.
|
||||
if state != 'absent' and host:
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
host_id = get_id_by_name(hosts_service, host)
|
||||
disks_service.disk_service(disk.id).refresh_lun(otypes.Host(id=host_id))
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,120 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2017 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_disk_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV disks
|
||||
author: "Katerina Koukiou (@KKoukiou)"
|
||||
version_added: "2.5"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV disks."
|
||||
- This module was called C(ovirt_disk_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_disk_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_disks), which
|
||||
contains a list of disks. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search Disk X from storage Y use following pattern:
|
||||
name=X and storage.name=Y"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all Disks which names start with C(centos)
|
||||
- ovirt_disk_info:
|
||||
pattern: name=centos*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_disks }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_disks:
|
||||
description: "List of dictionaries describing the Disks. Disk attributes are mapped to dictionary keys,
|
||||
all Disks attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/disk."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_disk_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_disk_facts' module has been renamed to 'ovirt_disk_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
disks_service = connection.system_service().disks_service()
|
||||
disks = disks_service.list(
|
||||
search=module.params['pattern'],
|
||||
)
|
||||
result = dict(
|
||||
ovirt_disks=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in disks
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,249 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright: (c) 2019, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_event
|
||||
short_description: Create or delete an event in oVirt/RHV
|
||||
author: "Chris Keller (@nasx)"
|
||||
version_added: "2.8"
|
||||
description:
|
||||
- "This module can be used to create or delete an event in oVirt/RHV."
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- "Should the event be present/absent."
|
||||
- "The C(wait) option must be set to false when state is absent."
|
||||
choices: ['present', 'absent']
|
||||
type: str
|
||||
default: present
|
||||
|
||||
description:
|
||||
description:
|
||||
- "Message for the event."
|
||||
- "Required when state is present."
|
||||
type: str
|
||||
|
||||
severity:
|
||||
description:
|
||||
- "Severity of the event."
|
||||
- "Required when state is present."
|
||||
choices: ['error', 'normal', 'warning']
|
||||
default: normal
|
||||
type: str
|
||||
|
||||
origin:
|
||||
description:
|
||||
- "Originator of the event."
|
||||
- "Required when state is present."
|
||||
type: str
|
||||
|
||||
custom_id:
|
||||
description:
|
||||
- "Custom ID for the event. This ID must be unique for each event."
|
||||
- "Required when state is present."
|
||||
type: int
|
||||
|
||||
id:
|
||||
description:
|
||||
- "The event ID in the oVirt/RHV audit_log table. This ID is not the same as custom_id and is only used when state is absent."
|
||||
- "Required when state is absent."
|
||||
type: str
|
||||
|
||||
cluster:
|
||||
description:
|
||||
- "The id of the cluster associated with this event."
|
||||
type: str
|
||||
|
||||
data_center:
|
||||
description:
|
||||
- "The id of the data center associated with this event."
|
||||
type: str
|
||||
|
||||
host:
|
||||
description:
|
||||
- "The id of the host associated with this event."
|
||||
type: str
|
||||
|
||||
storage_domain:
|
||||
description:
|
||||
- "The id of the storage domain associated with this event."
|
||||
type: str
|
||||
|
||||
template:
|
||||
description:
|
||||
- "The id of the template associated with this event."
|
||||
type: str
|
||||
|
||||
user:
|
||||
description:
|
||||
- "The id of the user associated with this event."
|
||||
type: str
|
||||
|
||||
vm:
|
||||
description:
|
||||
- "The id of the VM associated with this event."
|
||||
type: str
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain the auth parameter for simplicity,
|
||||
# look at the ovirt_auth module to see how to reuse authentication.
|
||||
|
||||
- name: Create an event
|
||||
ovirt_event:
|
||||
state: present
|
||||
description: "The file system /home on host xyz is almost full!"
|
||||
origin: "mymonitor"
|
||||
custom_id: 123456789
|
||||
severity: warning
|
||||
|
||||
- name: Create an event and link it to a specific object
|
||||
ovirt_event:
|
||||
state: present
|
||||
description: "The file system /home is almost full!"
|
||||
origin: "mymonitor"
|
||||
custom_id: 123456789
|
||||
severity: warning
|
||||
vm: "c79db183-46ef-44d1-95f9-1a368c516c19"
|
||||
|
||||
- name: Remove an event
|
||||
ovirt_event:
|
||||
state: absent
|
||||
id: 123456789
|
||||
wait: false
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: "ID of the event that was created."
|
||||
returned: "On success."
|
||||
type: str
|
||||
event:
|
||||
description: "Dictionary of all the Event attributes. All event attributes can be found at the following url:
|
||||
http://ovirt.github.io/ovirt-engine-api-model/master/#types/event"
|
||||
returned: "On success."
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
check_params,
|
||||
create_connection,
|
||||
equal,
|
||||
get_dict_of_struct,
|
||||
ovirt_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
class EventsModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.Event(
|
||||
description=self._module.params['description'],
|
||||
severity=otypes.LogSeverity(self._module.params['severity']),
|
||||
origin=self._module.params['origin'],
|
||||
custom_id=self._module.params['custom_id'],
|
||||
id=self._module.params['id'],
|
||||
cluster=otypes.Cluster(
|
||||
id=self._module.params['cluster']
|
||||
) if self._module.params['cluster'] is not None else None,
|
||||
data_center=otypes.DataCenter(
|
||||
id=self._module.params['data_center']
|
||||
) if self._module.params['data_center'] is not None else None,
|
||||
host=otypes.Host(
|
||||
id=self._module.params['host']
|
||||
) if self._module.params['host'] is not None else None,
|
||||
storage_domain=otypes.StorageDomain(
|
||||
id=self._module.params['storage_domain']
|
||||
) if self._module.params['storage_domain'] is not None else None,
|
||||
template=otypes.Template(
|
||||
id=self._module.params['template']
|
||||
) if self._module.params['template'] is not None else None,
|
||||
user=otypes.User(
|
||||
id=self._module.params['user']
|
||||
) if self._module.params['user'] is not None else None,
|
||||
vm=otypes.Vm(
|
||||
id=self._module.params['vm']
|
||||
) if self._module.params['vm'] is not None else None,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
description=dict(default=None),
|
||||
severity=dict(
|
||||
choices=['error', 'normal', 'warning'],
|
||||
default='normal',
|
||||
),
|
||||
origin=dict(default=None),
|
||||
custom_id=dict(default=None, type='int'),
|
||||
id=dict(default=None),
|
||||
cluster=dict(default=None),
|
||||
data_center=dict(default=None),
|
||||
host=dict(default=None),
|
||||
storage_domain=dict(default=None),
|
||||
template=dict(default=None),
|
||||
user=dict(default=None),
|
||||
vm=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
# Wait must be set to false if state == absent
|
||||
|
||||
if module.params['state'] == 'absent' and module.params['wait'] is not False:
|
||||
module.fail_json(msg='When "state" is absent, "wait" must be set to false.')
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
events_service = connection.system_service().events_service()
|
||||
events_module = EventsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=events_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = events_module.create()
|
||||
elif state == 'absent':
|
||||
ret = events_module.remove()
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,170 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright: (c) 2019, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_event_info
|
||||
short_description: This module can be used to retrieve information about one or more oVirt/RHV events
|
||||
author: "Chris Keller (@nasx)"
|
||||
version_added: "2.8"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV events."
|
||||
- This module was called C(ovirt_event_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_event_info) module no longer returns C(ansible_facts)!
|
||||
options:
|
||||
case_sensitive:
|
||||
description:
|
||||
- "Indicates if the search performed using the search parameter should be performed taking case
|
||||
into account. The default value is true, which means that case is taken into account. If you
|
||||
want to search ignoring case set it to false."
|
||||
required: false
|
||||
default: true
|
||||
type: bool
|
||||
|
||||
from_:
|
||||
description:
|
||||
- "Indicates the event index after which events should be returned. The indexes of events are
|
||||
strictly increasing, so when this parameter is used only the events with greater indexes
|
||||
will be returned."
|
||||
required: false
|
||||
type: int
|
||||
|
||||
max:
|
||||
description:
|
||||
- "Sets the maximum number of events to return. If not specified all the events are returned."
|
||||
required: false
|
||||
type: int
|
||||
|
||||
search:
|
||||
description:
|
||||
- "Search term which is accepted by the oVirt/RHV API."
|
||||
- "For example to search for events of severity alert use the following pattern: severity=alert"
|
||||
required: false
|
||||
type: str
|
||||
|
||||
headers:
|
||||
description:
|
||||
- "Additional HTTP headers."
|
||||
required: false
|
||||
type: str
|
||||
|
||||
query:
|
||||
description:
|
||||
- "Additional URL query parameters."
|
||||
required: false
|
||||
type: str
|
||||
|
||||
wait:
|
||||
description:
|
||||
- "If True wait for the response."
|
||||
required: false
|
||||
default: true
|
||||
type: bool
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain the auth parameter for simplicity,
|
||||
# look at the ovirt_auth module to see how to reuse authentication.
|
||||
|
||||
- name: Return all events
|
||||
ovirt_event_info:
|
||||
register: result
|
||||
|
||||
- name: Return the last 10 events
|
||||
ovirt_event_info:
|
||||
max: 10
|
||||
register: result
|
||||
|
||||
- name: Return all events of type alert
|
||||
ovirt_event_info:
|
||||
search: "severity=alert"
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_events }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_events:
|
||||
description: "List of dictionaries describing the events. Event attributes are mapped to dictionary keys.
|
||||
All event attributes can be found at the following url:
|
||||
http://ovirt.github.io/ovirt-engine-api-model/master/#types/event"
|
||||
returned: On success."
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
case_sensitive=dict(default=True, type='bool', required=False),
|
||||
from_=dict(default=None, type='int', required=False),
|
||||
max=dict(default=None, type='int', required=False),
|
||||
search=dict(default='', required=False),
|
||||
headers=dict(default='', required=False),
|
||||
query=dict(default='', required=False),
|
||||
wait=dict(default=True, type='bool', required=False)
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_event_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_event_facts' module has been renamed to 'ovirt_event_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
events_service = connection.system_service().events_service()
|
||||
events = events_service.list(
|
||||
case_sensitive=module.params['case_sensitive'],
|
||||
from_=module.params['from_'],
|
||||
max=module.params['max'],
|
||||
search=module.params['search'],
|
||||
headers=module.params['headers'],
|
||||
query=module.params['query'],
|
||||
wait=module.params['wait']
|
||||
)
|
||||
|
||||
result = dict(
|
||||
ovirt_events=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in events
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,406 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_external_provider
|
||||
short_description: Module to manage external providers in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage external providers in oVirt/RHV"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the external provider to manage."
|
||||
state:
|
||||
description:
|
||||
- "Should the external be present or absent"
|
||||
- "When you are using absent for I(os_volume), you need to make
|
||||
sure that SD is not attached to the data center!"
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
description:
|
||||
description:
|
||||
- "Description of the external provider."
|
||||
type:
|
||||
description:
|
||||
- "Type of the external provider."
|
||||
choices: ['os_image', 'network', 'os_volume', 'foreman']
|
||||
url:
|
||||
description:
|
||||
- "URL where external provider is hosted."
|
||||
- "Applicable for those types: I(os_image), I(os_volume), I(network) and I(foreman)."
|
||||
username:
|
||||
description:
|
||||
- "Username to be used for login to external provider."
|
||||
- "Applicable for all types."
|
||||
password:
|
||||
description:
|
||||
- "Password of the user specified in C(username) parameter."
|
||||
- "Applicable for all types."
|
||||
tenant_name:
|
||||
description:
|
||||
- "Name of the tenant."
|
||||
- "Applicable for those types: I(os_image), I(os_volume) and I(network)."
|
||||
aliases: ['tenant']
|
||||
authentication_url:
|
||||
description:
|
||||
- "Keystone authentication URL of the openstack provider."
|
||||
- "Applicable for those types: I(os_image), I(os_volume) and I(network)."
|
||||
aliases: ['auth_url']
|
||||
data_center:
|
||||
description:
|
||||
- "Name of the data center where provider should be attached."
|
||||
- "Applicable for those type: I(os_volume)."
|
||||
read_only:
|
||||
description:
|
||||
- "Specify if the network should be read only."
|
||||
- "Applicable if C(type) is I(network)."
|
||||
type: bool
|
||||
network_type:
|
||||
description:
|
||||
- "Type of the external network provider either external (for example OVN) or neutron."
|
||||
- "Applicable if C(type) is I(network)."
|
||||
choices: ['external', 'neutron']
|
||||
default: ['external']
|
||||
authentication_keys:
|
||||
description:
|
||||
- "List of authentication keys. Each key is represented by dict
|
||||
like {'uuid': 'our-uuid', 'value': 'YourSecretValue=='}"
|
||||
- "When you will not pass these keys and there are already some
|
||||
of them defined in the system they will be removed."
|
||||
- "Applicable for I(os_volume)."
|
||||
default: []
|
||||
version_added: "2.6"
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Add image external provider:
|
||||
- ovirt_external_provider:
|
||||
name: image_provider
|
||||
type: os_image
|
||||
url: http://1.2.3.4:9292
|
||||
username: admin
|
||||
password: 123456
|
||||
tenant: admin
|
||||
auth_url: http://1.2.3.4:35357/v2.0
|
||||
|
||||
# Add volume external provider:
|
||||
- ovirt_external_provider:
|
||||
name: image_provider
|
||||
type: os_volume
|
||||
url: http://1.2.3.4:9292
|
||||
username: admin
|
||||
password: 123456
|
||||
tenant: admin
|
||||
auth_url: http://1.2.3.4:5000/v2.0
|
||||
authentication_keys:
|
||||
-
|
||||
uuid: "1234567-a1234-12a3-a234-123abc45678"
|
||||
value: "ABCD00000000111111222333445w=="
|
||||
|
||||
# Add foreman provider:
|
||||
- ovirt_external_provider:
|
||||
name: foreman_provider
|
||||
type: foreman
|
||||
url: https://foreman.example.com
|
||||
username: admin
|
||||
password: 123456
|
||||
|
||||
# Add external network provider for OVN:
|
||||
- ovirt_external_provider:
|
||||
name: ovn_provider
|
||||
type: network
|
||||
network_type: external
|
||||
url: http://1.2.3.4:9696
|
||||
|
||||
# Remove image external provider:
|
||||
- ovirt_external_provider:
|
||||
state: absent
|
||||
name: image_provider
|
||||
type: os_image
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the external provider which is managed
|
||||
returned: On success if external provider is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
external_host_provider:
|
||||
description: "Dictionary of all the external_host_provider attributes. External provider attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/external_host_provider."
|
||||
returned: "On success and if parameter 'type: foreman' is used."
|
||||
type: dict
|
||||
openstack_image_provider:
|
||||
description: "Dictionary of all the openstack_image_provider attributes. External provider attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_image_provider."
|
||||
returned: "On success and if parameter 'type: os_image' is used."
|
||||
type: dict
|
||||
openstack_volume_provider:
|
||||
description: "Dictionary of all the openstack_volume_provider attributes. External provider attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_volume_provider."
|
||||
returned: "On success and if parameter 'type: os_volume' is used."
|
||||
type: dict
|
||||
openstack_network_provider:
|
||||
description: "Dictionary of all the openstack_network_provider attributes. External provider attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_network_provider."
|
||||
returned: "On success and if parameter 'type: network' is used."
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_params,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
ovirt_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
OS_VOLUME = 'os_volume'
|
||||
OS_IMAGE = 'os_image'
|
||||
NETWORK = 'network'
|
||||
FOREMAN = 'foreman'
|
||||
|
||||
|
||||
class ExternalProviderModule(BaseModule):
|
||||
|
||||
non_provider_params = ['type', 'authentication_keys', 'data_center']
|
||||
|
||||
def provider_type(self, provider_type):
|
||||
self._provider_type = provider_type
|
||||
|
||||
def provider_module_params(self):
|
||||
provider_params = [
|
||||
(key, value) for key, value in self._module.params.items() if key
|
||||
not in self.non_provider_params
|
||||
]
|
||||
provider_params.append(('data_center', self.get_data_center()))
|
||||
return provider_params
|
||||
|
||||
def get_data_center(self):
|
||||
dc_name = self._module.params.get("data_center", None)
|
||||
if dc_name:
|
||||
system_service = self._connection.system_service()
|
||||
data_centers_service = system_service.data_centers_service()
|
||||
return data_centers_service.list(
|
||||
search='name=%s' % dc_name,
|
||||
)[0]
|
||||
return dc_name
|
||||
|
||||
def build_entity(self):
|
||||
provider_type = self._provider_type(
|
||||
requires_authentication=self._module.params.get('username') is not None,
|
||||
)
|
||||
if self._module.params.pop('type') == NETWORK:
|
||||
setattr(
|
||||
provider_type,
|
||||
'type',
|
||||
otypes.OpenStackNetworkProviderType(self._module.params.pop('network_type'))
|
||||
)
|
||||
|
||||
for key, value in self.provider_module_params():
|
||||
if hasattr(provider_type, key):
|
||||
setattr(provider_type, key, value)
|
||||
|
||||
return provider_type
|
||||
|
||||
def update_check(self, entity):
|
||||
return (
|
||||
equal(self._module.params.get('description'), entity.description) and
|
||||
equal(self._module.params.get('url'), entity.url) and
|
||||
equal(self._module.params.get('authentication_url'), entity.authentication_url) and
|
||||
equal(self._module.params.get('tenant_name'), getattr(entity, 'tenant_name', None)) and
|
||||
equal(self._module.params.get('username'), entity.username)
|
||||
)
|
||||
|
||||
def update_volume_provider_auth_keys(
|
||||
self, provider, providers_service, keys
|
||||
):
|
||||
"""
|
||||
Update auth keys for volume provider, if not exist add them or remove
|
||||
if they are not specified and there are already defined in the external
|
||||
volume provider.
|
||||
|
||||
Args:
|
||||
provider (dict): Volume provider details.
|
||||
providers_service (openstack_volume_providers_service): Provider
|
||||
service.
|
||||
keys (list): Keys to be updated/added to volume provider, each key
|
||||
is represented as dict with keys: uuid, value.
|
||||
"""
|
||||
|
||||
provider_service = providers_service.provider_service(provider['id'])
|
||||
auth_keys_service = provider_service.authentication_keys_service()
|
||||
provider_keys = auth_keys_service.list()
|
||||
# removing keys which are not defined
|
||||
for key in [
|
||||
k.id for k in provider_keys if k.uuid not in [
|
||||
defined_key['uuid'] for defined_key in keys
|
||||
]
|
||||
]:
|
||||
self.changed = True
|
||||
if not self._module.check_mode:
|
||||
auth_keys_service.key_service(key).remove()
|
||||
if not (provider_keys or keys):
|
||||
# Nothing need to do when both are empty.
|
||||
return
|
||||
for key in keys:
|
||||
key_id_for_update = None
|
||||
for existing_key in provider_keys:
|
||||
if key['uuid'] == existing_key.uuid:
|
||||
key_id_for_update = existing_key.id
|
||||
|
||||
auth_key_usage_type = (
|
||||
otypes.OpenstackVolumeAuthenticationKeyUsageType("ceph")
|
||||
)
|
||||
auth_key = otypes.OpenstackVolumeAuthenticationKey(
|
||||
usage_type=auth_key_usage_type,
|
||||
uuid=key['uuid'],
|
||||
value=key['value'],
|
||||
)
|
||||
|
||||
if not key_id_for_update:
|
||||
self.changed = True
|
||||
if not self._module.check_mode:
|
||||
auth_keys_service.add(auth_key)
|
||||
else:
|
||||
# We cannot really distinguish here if it was really updated cause
|
||||
# we cannot take key value to check if it was changed or not. So
|
||||
# for sure we update here always.
|
||||
self.changed = True
|
||||
if not self._module.check_mode:
|
||||
auth_key_service = (
|
||||
auth_keys_service.key_service(key_id_for_update)
|
||||
)
|
||||
auth_key_service.update(auth_key)
|
||||
|
||||
|
||||
def _external_provider_service(provider_type, system_service):
|
||||
if provider_type == OS_IMAGE:
|
||||
return otypes.OpenStackImageProvider, system_service.openstack_image_providers_service()
|
||||
elif provider_type == NETWORK:
|
||||
return otypes.OpenStackNetworkProvider, system_service.openstack_network_providers_service()
|
||||
elif provider_type == OS_VOLUME:
|
||||
return otypes.OpenStackVolumeProvider, system_service.openstack_volume_providers_service()
|
||||
elif provider_type == FOREMAN:
|
||||
return otypes.ExternalHostProvider, system_service.external_host_providers_service()
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(default=None),
|
||||
description=dict(default=None),
|
||||
type=dict(
|
||||
default=None,
|
||||
required=True,
|
||||
choices=[
|
||||
OS_IMAGE, NETWORK, OS_VOLUME, FOREMAN,
|
||||
],
|
||||
aliases=['provider'],
|
||||
),
|
||||
url=dict(default=None),
|
||||
username=dict(default=None),
|
||||
password=dict(default=None, no_log=True),
|
||||
tenant_name=dict(default=None, aliases=['tenant']),
|
||||
authentication_url=dict(default=None, aliases=['auth_url']),
|
||||
data_center=dict(default=None),
|
||||
read_only=dict(default=None, type='bool'),
|
||||
network_type=dict(
|
||||
default='external',
|
||||
choices=['external', 'neutron'],
|
||||
),
|
||||
authentication_keys=dict(
|
||||
default=[], aliases=['auth_keys'], type='list', no_log=True,
|
||||
),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
provider_type_param = module.params.get('type')
|
||||
provider_type, external_providers_service = _external_provider_service(
|
||||
provider_type=provider_type_param,
|
||||
system_service=connection.system_service(),
|
||||
)
|
||||
external_providers_module = ExternalProviderModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=external_providers_service,
|
||||
)
|
||||
external_providers_module.provider_type(provider_type)
|
||||
|
||||
state = module.params.pop('state')
|
||||
if state == 'absent':
|
||||
ret = external_providers_module.remove()
|
||||
elif state == 'present':
|
||||
ret = external_providers_module.create()
|
||||
openstack_volume_provider_id = ret.get('id')
|
||||
if (
|
||||
provider_type_param == OS_VOLUME and
|
||||
openstack_volume_provider_id
|
||||
):
|
||||
external_providers_module.update_volume_provider_auth_keys(
|
||||
ret, external_providers_service,
|
||||
module.params.get('authentication_keys'),
|
||||
)
|
||||
|
||||
module.exit_json(**ret)
|
||||
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,161 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_external_provider_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV external providers
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV external providers."
|
||||
- This module was called C(ovirt_external_provider_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_external_provider_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_external_providers), which
|
||||
contains a list of external_providers. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
type:
|
||||
description:
|
||||
- "Type of the external provider."
|
||||
choices: ['os_image', 'os_network', 'os_volume', 'foreman']
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- "Name of the external provider, can be used as glob expression."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all image external providers named C<glance>:
|
||||
- ovirt_external_provider_info:
|
||||
type: os_image
|
||||
name: glance
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_external_providers }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_external_providers:
|
||||
description:
|
||||
- "List of dictionaries. Content depends on I(type)."
|
||||
- "For type C(foreman), attributes appearing in the dictionary can be found on your oVirt/RHV instance
|
||||
at the following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/external_host_provider."
|
||||
- "For type C(os_image), attributes appearing in the dictionary can be found on your oVirt/RHV instance
|
||||
at the following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_image_provider."
|
||||
- "For type C(os_volume), attributes appearing in the dictionary can be found on your oVirt/RHV instance
|
||||
at the following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_volume_provider."
|
||||
- "For type C(os_network), attributes appearing in the dictionary can be found on your oVirt/RHV instance
|
||||
at the following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_network_provider."
|
||||
returned: On success
|
||||
type: list
|
||||
'''
|
||||
|
||||
import fnmatch
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def _external_provider_service(provider_type, system_service):
|
||||
if provider_type == 'os_image':
|
||||
return system_service.openstack_image_providers_service()
|
||||
elif provider_type == 'os_network':
|
||||
return system_service.openstack_network_providers_service()
|
||||
elif provider_type == 'os_volume':
|
||||
return system_service.openstack_volume_providers_service()
|
||||
elif provider_type == 'foreman':
|
||||
return system_service.external_host_providers_service()
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
name=dict(default=None, required=False),
|
||||
type=dict(
|
||||
default=None,
|
||||
required=True,
|
||||
choices=[
|
||||
'os_image', 'os_network', 'os_volume', 'foreman',
|
||||
],
|
||||
aliases=['provider'],
|
||||
),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_external_provider_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_external_provider_facts' module has been renamed to 'ovirt_external_provider_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
external_providers_service = _external_provider_service(
|
||||
provider_type=module.params.pop('type'),
|
||||
system_service=connection.system_service(),
|
||||
)
|
||||
if module.params['name']:
|
||||
external_providers = [
|
||||
e for e in external_providers_service.list()
|
||||
if fnmatch.fnmatch(e.name, module.params['name'])
|
||||
]
|
||||
else:
|
||||
external_providers = external_providers_service.list()
|
||||
|
||||
result = dict(
|
||||
ovirt_external_providers=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in external_providers
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,185 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_group
|
||||
short_description: Module to manage groups in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage groups in oVirt/RHV"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the group to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the group be present/absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
authz_name:
|
||||
description:
|
||||
- "Authorization provider of the group. In previous versions of oVirt/RHV known as domain."
|
||||
required: true
|
||||
aliases: ['domain']
|
||||
namespace:
|
||||
description:
|
||||
- "Namespace of the authorization provider, where group resides."
|
||||
required: false
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Add group group1 from authorization provider example.com-authz
|
||||
- ovirt_group:
|
||||
name: group1
|
||||
domain: example.com-authz
|
||||
|
||||
# Add group group1 from authorization provider example.com-authz
|
||||
# In case of multi-domain Active Directory setup, you should pass
|
||||
# also namespace, so it adds correct group:
|
||||
- ovirt_group:
|
||||
name: group1
|
||||
namespace: dc=ad2,dc=example,dc=com
|
||||
domain: example.com-authz
|
||||
|
||||
# Remove group group1 with authorization provider example.com-authz
|
||||
- ovirt_group:
|
||||
state: absent
|
||||
name: group1
|
||||
domain: example.com-authz
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the group which is managed
|
||||
returned: On success if group is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
group:
|
||||
description: "Dictionary of all the group attributes. Group attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/group."
|
||||
returned: On success if group is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
check_params,
|
||||
create_connection,
|
||||
equal,
|
||||
ovirt_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def _group(connection, module):
|
||||
groups = connection.system_service().groups_service().list(
|
||||
search="name={name}".format(
|
||||
name=module.params['name'],
|
||||
)
|
||||
)
|
||||
|
||||
# If found more groups, filter them by namespace and authz name:
|
||||
# (filtering here, as oVirt/RHV backend doesn't support it)
|
||||
if len(groups) > 1:
|
||||
groups = [
|
||||
g for g in groups if (
|
||||
equal(module.params['namespace'], g.namespace) and
|
||||
equal(module.params['authz_name'], g.domain.name)
|
||||
)
|
||||
]
|
||||
return groups[0] if groups else None
|
||||
|
||||
|
||||
class GroupsModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.Group(
|
||||
domain=otypes.Domain(
|
||||
name=self._module.params['authz_name']
|
||||
),
|
||||
name=self._module.params['name'],
|
||||
namespace=self._module.params['namespace'],
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(required=True),
|
||||
authz_name=dict(required=True, aliases=['domain']),
|
||||
namespace=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
groups_service = connection.system_service().groups_service()
|
||||
groups_module = GroupsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=groups_service,
|
||||
)
|
||||
group = _group(connection, module)
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = groups_module.create(entity=group)
|
||||
elif state == 'absent':
|
||||
ret = groups_module.remove(entity=group)
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,118 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_group_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV groups
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV groups."
|
||||
- This module was called C(ovirt_group_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_group_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_groups), which
|
||||
contains a list of groups. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search group X use following pattern: name=X"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all groups which names start with C(admin):
|
||||
- ovirt_group_info:
|
||||
pattern: name=admin*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_groups }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_groups:
|
||||
description: "List of dictionaries describing the groups. Group attributes are mapped to dictionary keys,
|
||||
all groups attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/group."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_group_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_group_facts' module has been renamed to 'ovirt_group_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
groups_service = connection.system_service().groups_service()
|
||||
groups = groups_service.list(search=module.params['pattern'])
|
||||
result = dict(
|
||||
ovirt_groups=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in groups
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,701 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_host
|
||||
short_description: Module to manage hosts in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage hosts in oVirt/RHV"
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the host to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- "Name of the host to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "State which should a host to be in after successful completion."
|
||||
- "I(iscsilogin) and I(iscsidiscover) are supported since version 2.4."
|
||||
choices: [
|
||||
'present', 'absent', 'maintenance', 'upgraded', 'started',
|
||||
'restarted', 'stopped', 'reinstalled', 'iscsidiscover', 'iscsilogin'
|
||||
]
|
||||
default: present
|
||||
comment:
|
||||
description:
|
||||
- "Description of the host."
|
||||
timeout:
|
||||
description:
|
||||
- "The amount of time in seconds the module should wait for the host to
|
||||
get into desired state."
|
||||
default: 600
|
||||
cluster:
|
||||
description:
|
||||
- "Name of the cluster, where host should be created."
|
||||
address:
|
||||
description:
|
||||
- "Host address. It can be either FQDN (preferred) or IP address."
|
||||
password:
|
||||
description:
|
||||
- "Password of the root. It's required in case C(public_key) is set to I(False)."
|
||||
public_key:
|
||||
description:
|
||||
- "I(True) if the public key should be used to authenticate to host."
|
||||
- "It's required in case C(password) is not set."
|
||||
default: False
|
||||
type: bool
|
||||
aliases: ['ssh_public_key']
|
||||
kdump_integration:
|
||||
description:
|
||||
- "Specify if host will have enabled Kdump integration."
|
||||
choices: ['enabled', 'disabled']
|
||||
spm_priority:
|
||||
description:
|
||||
- "SPM priority of the host. Integer value from 1 to 10, where higher number means higher priority."
|
||||
override_iptables:
|
||||
description:
|
||||
- "If True host iptables will be overridden by host deploy script."
|
||||
- "Note that C(override_iptables) is I(false) by default in oVirt/RHV."
|
||||
type: bool
|
||||
force:
|
||||
description:
|
||||
- "Indicates that the host should be removed even if it is non-responsive,
|
||||
or if it is part of a Gluster Storage cluster and has volume bricks on it."
|
||||
- "WARNING: It doesn't forcibly remove the host if another host related operation is being executed on the host at the same time."
|
||||
default: False
|
||||
type: bool
|
||||
override_display:
|
||||
description:
|
||||
- "Override the display address of all VMs on this host with specified address."
|
||||
type: bool
|
||||
kernel_params:
|
||||
description:
|
||||
- "List of kernel boot parameters."
|
||||
- "Following are most common kernel parameters used for host:"
|
||||
- "Hostdev Passthrough & SR-IOV: intel_iommu=on"
|
||||
- "Nested Virtualization: kvm-intel.nested=1"
|
||||
- "Unsafe Interrupts: vfio_iommu_type1.allow_unsafe_interrupts=1"
|
||||
- "PCI Reallocation: pci=realloc"
|
||||
- "C(Note:)"
|
||||
- "Modifying kernel boot parameters settings can lead to a host boot failure.
|
||||
Please consult the product documentation before doing any changes."
|
||||
- "Kernel boot parameters changes require host deploy and restart. The host needs
|
||||
to be I(reinstalled) successfully and then to be I(rebooted) for kernel boot parameters
|
||||
to be applied."
|
||||
hosted_engine:
|
||||
description:
|
||||
- "If I(deploy) it means this host should deploy also hosted engine
|
||||
components."
|
||||
- "If I(undeploy) it means this host should un-deploy hosted engine
|
||||
components and this host will not function as part of the High
|
||||
Availability cluster."
|
||||
choices:
|
||||
- 'deploy'
|
||||
- 'undeploy'
|
||||
power_management_enabled:
|
||||
description:
|
||||
- "Enable or disable power management of the host."
|
||||
- "For more comprehensive setup of PM use C(ovirt_host_pm) module."
|
||||
version_added: 2.4
|
||||
type: bool
|
||||
activate:
|
||||
description:
|
||||
- "If C(state) is I(present) activate the host."
|
||||
- "This parameter is good to disable, when you don't want to change
|
||||
the state of host when using I(present) C(state)."
|
||||
default: True
|
||||
type: bool
|
||||
version_added: 2.4
|
||||
iscsi:
|
||||
description:
|
||||
- "If C(state) is I(iscsidiscover) it means that the iscsi attribute is being
|
||||
used to discover targets"
|
||||
- "If C(state) is I(iscsilogin) it means that the iscsi attribute is being
|
||||
used to login to the specified targets passed as part of the iscsi attribute"
|
||||
suboptions:
|
||||
username:
|
||||
description:
|
||||
- "A CHAP user name for logging into a target."
|
||||
password:
|
||||
description:
|
||||
- "A CHAP password for logging into a target."
|
||||
address:
|
||||
description:
|
||||
- "Address of the iSCSI storage server."
|
||||
target:
|
||||
description:
|
||||
- "The target IQN for the storage device."
|
||||
port:
|
||||
description:
|
||||
- "The port being used to connect with iscsi."
|
||||
portal:
|
||||
description:
|
||||
- "The portal being used to connect with iscsi."
|
||||
version_added: 2.10
|
||||
version_added: 2.4
|
||||
check_upgrade:
|
||||
description:
|
||||
- "If I(true) and C(state) is I(upgraded) run check for upgrade
|
||||
action before executing upgrade action."
|
||||
default: True
|
||||
type: bool
|
||||
version_added: 2.4
|
||||
reboot_after_upgrade:
|
||||
description:
|
||||
- "If I(true) and C(state) is I(upgraded) reboot host after successful upgrade."
|
||||
default: True
|
||||
type: bool
|
||||
version_added: 2.6
|
||||
vgpu_placement:
|
||||
description:
|
||||
- If I(consolidated), each vGPU is placed on the first physical card with
|
||||
available space. This is the default placement, utilizing all available
|
||||
space on the physical cards.
|
||||
- If I(separated), each vGPU is placed on a separate physical card, if
|
||||
possible. This can be useful for improving vGPU performance.
|
||||
choices: ['consolidated', 'separated']
|
||||
version_added: 2.8
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Add host with username/password supporting SR-IOV.
|
||||
# Note that override_iptables is false by default in oVirt/RHV:
|
||||
- ovirt_host:
|
||||
cluster: Default
|
||||
name: myhost
|
||||
address: 10.34.61.145
|
||||
password: secret
|
||||
override_iptables: true
|
||||
kernel_params:
|
||||
- intel_iommu=on
|
||||
|
||||
# Add host using public key
|
||||
- ovirt_host:
|
||||
public_key: true
|
||||
cluster: Default
|
||||
name: myhost2
|
||||
address: 10.34.61.145
|
||||
override_iptables: true
|
||||
|
||||
# Deploy hosted engine host
|
||||
- ovirt_host:
|
||||
cluster: Default
|
||||
name: myhost2
|
||||
password: secret
|
||||
address: 10.34.61.145
|
||||
override_iptables: true
|
||||
hosted_engine: deploy
|
||||
|
||||
# Maintenance
|
||||
- ovirt_host:
|
||||
state: maintenance
|
||||
name: myhost
|
||||
|
||||
# Restart host using power management:
|
||||
- ovirt_host:
|
||||
state: restarted
|
||||
name: myhost
|
||||
|
||||
# Upgrade host
|
||||
- ovirt_host:
|
||||
state: upgraded
|
||||
name: myhost
|
||||
|
||||
# discover iscsi targets
|
||||
- ovirt_host:
|
||||
state: iscsidiscover
|
||||
name: myhost
|
||||
iscsi:
|
||||
username: iscsi_user
|
||||
password: secret
|
||||
address: 10.34.61.145
|
||||
port: 3260
|
||||
|
||||
|
||||
# login to iscsi targets
|
||||
- ovirt_host:
|
||||
state: iscsilogin
|
||||
name: myhost
|
||||
iscsi:
|
||||
username: iscsi_user
|
||||
password: secret
|
||||
address: 10.34.61.145
|
||||
target: "iqn.2015-07.com.mlipchuk2.redhat:444"
|
||||
port: 3260
|
||||
|
||||
|
||||
# Reinstall host using public key
|
||||
- ovirt_host:
|
||||
state: reinstalled
|
||||
name: myhost
|
||||
public_key: true
|
||||
|
||||
# Remove host
|
||||
- ovirt_host:
|
||||
state: absent
|
||||
name: myhost
|
||||
force: True
|
||||
|
||||
# Retry removing host when failed (https://bugzilla.redhat.com/show_bug.cgi?id=1719271)
|
||||
- ovirt_host:
|
||||
state: absent
|
||||
name: myhost
|
||||
register: result
|
||||
until: not result.failed
|
||||
retries: 6
|
||||
delay: 20
|
||||
|
||||
# Change host Name
|
||||
- ovirt_host:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new host name"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the host which is managed
|
||||
returned: On success if host is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
host:
|
||||
description: "Dictionary of all the host attributes. Host attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/host."
|
||||
returned: On success if host is found.
|
||||
type: dict
|
||||
iscsi_targets:
|
||||
description: "List of host iscsi targets"
|
||||
returned: On success if host is found and state is iscsidiscover.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import time
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
|
||||
from ovirtsdk4.types import HostStatus as hoststate
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_id_by_name,
|
||||
ovirt_full_argument_spec,
|
||||
wait,
|
||||
)
|
||||
|
||||
|
||||
class HostsModule(BaseModule):
|
||||
def __init__(self, start_event=None, *args, **kwargs):
|
||||
super(HostsModule, self).__init__(*args, **kwargs)
|
||||
self.start_event = start_event
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.Host(
|
||||
id=self._module.params.get('id'),
|
||||
name=self.param('name'),
|
||||
cluster=otypes.Cluster(
|
||||
name=self.param('cluster')
|
||||
) if self.param('cluster') else None,
|
||||
comment=self.param('comment'),
|
||||
address=self.param('address'),
|
||||
root_password=self.param('password'),
|
||||
ssh=otypes.Ssh(
|
||||
authentication_method=otypes.SshAuthenticationMethod.PUBLICKEY,
|
||||
) if self.param('public_key') else None,
|
||||
spm=otypes.Spm(
|
||||
priority=self.param('spm_priority'),
|
||||
) if self.param('spm_priority') else None,
|
||||
override_iptables=self.param('override_iptables'),
|
||||
display=otypes.Display(
|
||||
address=self.param('override_display'),
|
||||
) if self.param('override_display') else None,
|
||||
os=otypes.OperatingSystem(
|
||||
custom_kernel_cmdline=' '.join(self.param('kernel_params')),
|
||||
) if self.param('kernel_params') else None,
|
||||
power_management=otypes.PowerManagement(
|
||||
enabled=self.param('power_management_enabled'),
|
||||
kdump_detection=self.param('kdump_integration') == 'enabled',
|
||||
) if self.param('power_management_enabled') is not None or self.param('kdump_integration') else None,
|
||||
vgpu_placement=otypes.VgpuPlacement(
|
||||
self.param('vgpu_placement')
|
||||
) if self.param('vgpu_placement') is not None else None,
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
kernel_params = self.param('kernel_params')
|
||||
return (
|
||||
equal(self.param('comment'), entity.comment) and
|
||||
equal(self.param('kdump_integration'), 'enabled' if entity.power_management.kdump_detection else 'disabled') and
|
||||
equal(self.param('spm_priority'), entity.spm.priority) and
|
||||
equal(self.param('name'), entity.name) and
|
||||
equal(self.param('power_management_enabled'), entity.power_management.enabled) and
|
||||
equal(self.param('override_display'), getattr(entity.display, 'address', None)) and
|
||||
equal(self.param('vgpu_placement'), str(entity.vgpu_placement)) and
|
||||
equal(
|
||||
sorted(kernel_params) if kernel_params else None,
|
||||
sorted(entity.os.custom_kernel_cmdline.split(' '))
|
||||
)
|
||||
)
|
||||
|
||||
def pre_remove(self, entity):
|
||||
self.action(
|
||||
entity=entity,
|
||||
action='deactivate',
|
||||
action_condition=lambda h: h.status != hoststate.MAINTENANCE,
|
||||
wait_condition=lambda h: h.status == hoststate.MAINTENANCE,
|
||||
)
|
||||
|
||||
def post_reinstall(self, host):
|
||||
wait(
|
||||
service=self._service.service(host.id),
|
||||
condition=lambda h: h.status != hoststate.MAINTENANCE,
|
||||
fail_condition=failed_state,
|
||||
wait=self.param('wait'),
|
||||
timeout=self.param('timeout'),
|
||||
)
|
||||
|
||||
def raise_host_exception(self):
|
||||
events = self._connection.system_service().events_service().list(from_=int(self.start_event.index))
|
||||
error_events = [
|
||||
event.description for event in events
|
||||
if event.host is not None and (event.host.id == self.param('id') or event.host.name == self.param('name')) and
|
||||
event.severity in [otypes.LogSeverity.WARNING, otypes.LogSeverity.ERROR]
|
||||
]
|
||||
if error_events:
|
||||
raise Exception("Error message: %s" % error_events)
|
||||
return True
|
||||
|
||||
def failed_state_after_reinstall(self, host, count=0):
|
||||
if host.status in [
|
||||
hoststate.ERROR,
|
||||
hoststate.INSTALL_FAILED,
|
||||
hoststate.NON_OPERATIONAL,
|
||||
]:
|
||||
return self.raise_host_exception()
|
||||
|
||||
# If host is in non-responsive state after upgrade/install
|
||||
# let's wait for few seconds and re-check again the state:
|
||||
if host.status == hoststate.NON_RESPONSIVE:
|
||||
if count <= 3:
|
||||
time.sleep(20)
|
||||
return self.failed_state_after_reinstall(
|
||||
self._service.service(host.id).get(),
|
||||
count + 1,
|
||||
)
|
||||
else:
|
||||
return self.raise_host_exception()
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def failed_state(host):
|
||||
return host.status in [
|
||||
hoststate.ERROR,
|
||||
hoststate.INSTALL_FAILED,
|
||||
hoststate.NON_RESPONSIVE,
|
||||
hoststate.NON_OPERATIONAL,
|
||||
]
|
||||
|
||||
|
||||
def control_state(host_module):
|
||||
host = host_module.search_entity()
|
||||
if host is None:
|
||||
return
|
||||
|
||||
state = host_module._module.params['state']
|
||||
host_service = host_module._service.service(host.id)
|
||||
if failed_state(host):
|
||||
# In case host is in INSTALL_FAILED status, we can reinstall it:
|
||||
if hoststate.INSTALL_FAILED == host.status and state != 'reinstalled':
|
||||
raise Exception(
|
||||
"Not possible to manage host '%s' in state '%s'." % (
|
||||
host.name,
|
||||
host.status
|
||||
)
|
||||
)
|
||||
elif host.status in [
|
||||
hoststate.REBOOT,
|
||||
hoststate.CONNECTING,
|
||||
hoststate.INITIALIZING,
|
||||
hoststate.INSTALLING,
|
||||
hoststate.INSTALLING_OS,
|
||||
]:
|
||||
wait(
|
||||
service=host_service,
|
||||
condition=lambda host: host.status == hoststate.UP,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
elif host.status == hoststate.PREPARING_FOR_MAINTENANCE:
|
||||
wait(
|
||||
service=host_service,
|
||||
condition=lambda host: host.status == hoststate.MAINTENANCE,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
|
||||
return host
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=[
|
||||
'present', 'absent', 'maintenance', 'upgraded', 'started',
|
||||
'restarted', 'stopped', 'reinstalled', 'iscsidiscover', 'iscsilogin'
|
||||
],
|
||||
default='present',
|
||||
),
|
||||
name=dict(required=True),
|
||||
id=dict(default=None),
|
||||
comment=dict(default=None),
|
||||
cluster=dict(default=None),
|
||||
address=dict(default=None),
|
||||
password=dict(default=None, no_log=True),
|
||||
public_key=dict(default=False, type='bool', aliases=['ssh_public_key']),
|
||||
kdump_integration=dict(default=None, choices=['enabled', 'disabled']),
|
||||
spm_priority=dict(default=None, type='int'),
|
||||
override_iptables=dict(default=None, type='bool'),
|
||||
force=dict(default=False, type='bool'),
|
||||
timeout=dict(default=600, type='int'),
|
||||
override_display=dict(default=None),
|
||||
kernel_params=dict(default=None, type='list'),
|
||||
hosted_engine=dict(default=None, choices=['deploy', 'undeploy']),
|
||||
power_management_enabled=dict(default=None, type='bool'),
|
||||
activate=dict(default=True, type='bool'),
|
||||
iscsi=dict(default=None, type='dict'),
|
||||
check_upgrade=dict(default=True, type='bool'),
|
||||
reboot_after_upgrade=dict(default=True, type='bool'),
|
||||
vgpu_placement=dict(default=None, choices=['consolidated', 'separated']),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
['state', 'iscsidiscover', ['iscsi']],
|
||||
['state', 'iscsilogin', ['iscsi']]
|
||||
]
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
start_event = connection.system_service().events_service().list(max=1)[0]
|
||||
hosts_module = HostsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=hosts_service,
|
||||
start_event=start_event,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
host = control_state(hosts_module)
|
||||
if state == 'present':
|
||||
ret = hosts_module.create(
|
||||
deploy_hosted_engine=(
|
||||
module.params.get('hosted_engine') == 'deploy'
|
||||
) if module.params.get('hosted_engine') is not None else None,
|
||||
activate=module.params['activate'],
|
||||
result_state=(hoststate.MAINTENANCE if module.params['activate'] is False else hoststate.UP) if host is None else None,
|
||||
fail_condition=hosts_module.failed_state_after_reinstall if host is None else lambda h: False,
|
||||
)
|
||||
if module.params['activate'] and host is not None:
|
||||
ret = hosts_module.action(
|
||||
action='activate',
|
||||
action_condition=lambda h: h.status != hoststate.UP,
|
||||
wait_condition=lambda h: h.status == hoststate.UP,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
elif state == 'absent':
|
||||
ret = hosts_module.remove()
|
||||
elif state == 'maintenance':
|
||||
hosts_module.action(
|
||||
action='deactivate',
|
||||
action_condition=lambda h: h.status != hoststate.MAINTENANCE,
|
||||
wait_condition=lambda h: h.status == hoststate.MAINTENANCE,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
ret = hosts_module.create()
|
||||
elif state == 'upgraded':
|
||||
result_state = hoststate.MAINTENANCE if host.status == hoststate.MAINTENANCE else hoststate.UP
|
||||
events_service = connection.system_service().events_service()
|
||||
last_event = events_service.list(max=1)[0]
|
||||
|
||||
if module.params['check_upgrade']:
|
||||
hosts_module.action(
|
||||
action='upgrade_check',
|
||||
action_condition=lambda host: not host.update_available,
|
||||
wait_condition=lambda host: host.update_available or (
|
||||
len([
|
||||
event
|
||||
for event in events_service.list(
|
||||
from_=int(last_event.id),
|
||||
search='type=885',
|
||||
# Uncomment when 4.1 is EOL, and remove the cond:
|
||||
# if host.name in event.description
|
||||
# search='type=885 and host.name=%s' % host.name,
|
||||
) if host.name in event.description
|
||||
]) > 0
|
||||
),
|
||||
fail_condition=lambda host: len([
|
||||
event
|
||||
for event in events_service.list(
|
||||
from_=int(last_event.id),
|
||||
search='type=839 or type=887 and host.name=%s' % host.name,
|
||||
)
|
||||
]) > 0,
|
||||
)
|
||||
# Set to False, because upgrade_check isn't 'changing' action:
|
||||
hosts_module._changed = False
|
||||
ret = hosts_module.action(
|
||||
action='upgrade',
|
||||
action_condition=lambda h: h.update_available,
|
||||
wait_condition=lambda h: h.status == result_state,
|
||||
post_action=lambda h: time.sleep(module.params['poll_interval']),
|
||||
fail_condition=lambda h: hosts_module.failed_state_after_reinstall(h) or (
|
||||
len([
|
||||
event
|
||||
for event in events_service.list(
|
||||
from_=int(last_event.id),
|
||||
# Fail upgrade if migration fails:
|
||||
# 17: Failed to switch Host to Maintenance mode
|
||||
# 65, 140: Migration failed
|
||||
# 166: No available host was found to migrate VM
|
||||
search='type=65 or type=140 or type=166 or type=17',
|
||||
) if host.name in event.description
|
||||
]) > 0
|
||||
),
|
||||
reboot=module.params['reboot_after_upgrade'],
|
||||
)
|
||||
elif state == 'iscsidiscover':
|
||||
host_id = get_id_by_name(hosts_service, module.params['name'])
|
||||
iscsi_param = module.params['iscsi']
|
||||
iscsi_targets = hosts_service.service(host_id).iscsi_discover(
|
||||
iscsi=otypes.IscsiDetails(
|
||||
port=int(iscsi_param.get('port', 3260)),
|
||||
username=iscsi_param.get('username'),
|
||||
password=iscsi_param.get('password'),
|
||||
address=iscsi_param.get('address'),
|
||||
portal=iscsi_param.get('portal'),
|
||||
),
|
||||
)
|
||||
ret = {
|
||||
'changed': False,
|
||||
'id': host_id,
|
||||
'iscsi_targets': iscsi_targets,
|
||||
}
|
||||
elif state == 'iscsilogin':
|
||||
host_id = get_id_by_name(hosts_service, module.params['name'])
|
||||
iscsi_param = module.params['iscsi']
|
||||
ret = hosts_module.action(
|
||||
action='iscsi_login',
|
||||
iscsi=otypes.IscsiDetails(
|
||||
port=int(iscsi_param.get('port', 3260)),
|
||||
username=iscsi_param.get('username'),
|
||||
password=iscsi_param.get('password'),
|
||||
address=iscsi_param.get('address'),
|
||||
target=iscsi_param.get('target'),
|
||||
portal=iscsi_param.get('portal'),
|
||||
),
|
||||
)
|
||||
elif state == 'started':
|
||||
ret = hosts_module.action(
|
||||
action='fence',
|
||||
action_condition=lambda h: h.status == hoststate.DOWN,
|
||||
wait_condition=lambda h: h.status in [hoststate.UP, hoststate.MAINTENANCE],
|
||||
fail_condition=hosts_module.failed_state_after_reinstall,
|
||||
fence_type='start',
|
||||
)
|
||||
elif state == 'stopped':
|
||||
hosts_module.action(
|
||||
action='deactivate',
|
||||
action_condition=lambda h: h.status not in [hoststate.MAINTENANCE, hoststate.DOWN],
|
||||
wait_condition=lambda h: h.status in [hoststate.MAINTENANCE, hoststate.DOWN],
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
ret = hosts_module.action(
|
||||
action='fence',
|
||||
action_condition=lambda h: h.status != hoststate.DOWN,
|
||||
wait_condition=lambda h: h.status == hoststate.DOWN if module.params['wait'] else True,
|
||||
fail_condition=failed_state,
|
||||
fence_type='stop',
|
||||
)
|
||||
elif state == 'restarted':
|
||||
ret = hosts_module.action(
|
||||
action='fence',
|
||||
wait_condition=lambda h: h.status == hoststate.UP,
|
||||
fail_condition=hosts_module.failed_state_after_reinstall,
|
||||
fence_type='restart',
|
||||
)
|
||||
elif state == 'reinstalled':
|
||||
# Deactivate host if not in maintanence:
|
||||
hosts_module.action(
|
||||
action='deactivate',
|
||||
action_condition=lambda h: h.status not in [hoststate.MAINTENANCE, hoststate.DOWN],
|
||||
wait_condition=lambda h: h.status in [hoststate.MAINTENANCE, hoststate.DOWN],
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
|
||||
# Reinstall host:
|
||||
hosts_module.action(
|
||||
action='install',
|
||||
action_condition=lambda h: h.status == hoststate.MAINTENANCE,
|
||||
post_action=hosts_module.post_reinstall,
|
||||
wait_condition=lambda h: h.status == hoststate.MAINTENANCE,
|
||||
fail_condition=hosts_module.failed_state_after_reinstall,
|
||||
host=otypes.Host(
|
||||
override_iptables=module.params['override_iptables'],
|
||||
) if module.params['override_iptables'] else None,
|
||||
root_password=module.params['password'],
|
||||
ssh=otypes.Ssh(
|
||||
authentication_method=otypes.SshAuthenticationMethod.PUBLICKEY,
|
||||
) if module.params['public_key'] else None,
|
||||
deploy_hosted_engine=(
|
||||
module.params.get('hosted_engine') == 'deploy'
|
||||
) if module.params.get('hosted_engine') is not None else None,
|
||||
undeploy_hosted_engine=(
|
||||
module.params.get('hosted_engine') == 'undeploy'
|
||||
) if module.params.get('hosted_engine') is not None else None,
|
||||
)
|
||||
|
||||
# Activate host after reinstall:
|
||||
ret = hosts_module.action(
|
||||
action='activate',
|
||||
action_condition=lambda h: h.status == hoststate.MAINTENANCE,
|
||||
wait_condition=lambda h: h.status == hoststate.UP,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,144 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_host_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV hosts
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV hosts."
|
||||
- This module was called C(ovirt_host_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_host_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_hosts), which
|
||||
contains a list of hosts. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search host X from datacenter Y use following pattern:
|
||||
name=X and datacenter=Y"
|
||||
all_content:
|
||||
description:
|
||||
- "If I(true) all the attributes of the hosts should be
|
||||
included in the response."
|
||||
default: False
|
||||
version_added: "2.7"
|
||||
type: bool
|
||||
cluster_version:
|
||||
description:
|
||||
- "Filter the hosts based on the cluster version."
|
||||
type: str
|
||||
version_added: "2.8"
|
||||
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all hosts which names start with C(host) and
|
||||
# belong to data center C(west):
|
||||
- ovirt_host_info:
|
||||
pattern: name=host* and datacenter=west
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_hosts }}"
|
||||
# All hosts with cluster version 4.2:
|
||||
- ovirt_host_info:
|
||||
pattern: name=host*
|
||||
cluster_version: "4.2"
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_hosts }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_hosts:
|
||||
description: "List of dictionaries describing the hosts. Host attributes are mapped to dictionary keys,
|
||||
all hosts attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/host."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def get_filtered_hosts(cluster_version, hosts, connection):
|
||||
# Filtering by cluster version returns only those which have same cluster version as input
|
||||
filtered_hosts = []
|
||||
for host in hosts:
|
||||
cluster = connection.follow_link(host.cluster)
|
||||
cluster_version_host = str(cluster.version.major) + '.' + str(cluster.version.minor)
|
||||
if cluster_version_host == cluster_version:
|
||||
filtered_hosts.append(host)
|
||||
return filtered_hosts
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
all_content=dict(default=False, type='bool'),
|
||||
cluster_version=dict(default=None, type='str'),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_host_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_host_facts' module has been renamed to 'ovirt_host_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
hosts = hosts_service.list(
|
||||
search=module.params['pattern'],
|
||||
all_content=module.params['all_content']
|
||||
)
|
||||
cluster_version = module.params.get('cluster_version')
|
||||
if cluster_version is not None:
|
||||
hosts = get_filtered_hosts(cluster_version, hosts, connection)
|
||||
result = dict(
|
||||
ovirt_hosts=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in hosts
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,601 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016, 2018 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_host_network
|
||||
short_description: Module to manage host networks in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage host networks in oVirt/RHV."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the host to manage networks for."
|
||||
required: true
|
||||
aliases:
|
||||
- 'host'
|
||||
state:
|
||||
description:
|
||||
- "Should the host be present/absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
bond:
|
||||
description:
|
||||
- "Dictionary describing network bond:"
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Bond name.
|
||||
mode:
|
||||
description:
|
||||
- Bonding mode.
|
||||
options:
|
||||
description:
|
||||
- Bonding options.
|
||||
interfaces:
|
||||
description:
|
||||
- List of interfaces to create a bond.
|
||||
interface:
|
||||
description:
|
||||
- "Name of the network interface where logical network should be attached."
|
||||
networks:
|
||||
description:
|
||||
- "List of dictionary describing networks to be attached to interface or bond:"
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Name of the logical network to be assigned to bond or interface.
|
||||
boot_protocol:
|
||||
description:
|
||||
- Boot protocol.
|
||||
choices: ['none', 'static', 'dhcp']
|
||||
address:
|
||||
description:
|
||||
- IP address in case of I(static) boot protocol is used.
|
||||
netmask:
|
||||
description:
|
||||
- Subnet mask in case of I(static) boot protocol is used.
|
||||
gateway:
|
||||
description:
|
||||
- Gateway in case of I(static) boot protocol is used.
|
||||
version:
|
||||
description:
|
||||
- IP version. Either v4 or v6. Default is v4.
|
||||
custom_properties:
|
||||
description:
|
||||
- "Custom properties applied to the host network."
|
||||
- "Custom properties is a list of dictionary which can have following values."
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Name of custom property.
|
||||
value:
|
||||
description:
|
||||
- Value of custom property.
|
||||
version_added: 2.10
|
||||
labels:
|
||||
description:
|
||||
- "List of names of the network label to be assigned to bond or interface."
|
||||
check:
|
||||
description:
|
||||
- "If I(true) verify connectivity between host and engine."
|
||||
- "Network configuration changes will be rolled back if connectivity between
|
||||
engine and the host is lost after changing network configuration."
|
||||
type: bool
|
||||
save:
|
||||
description:
|
||||
- "If I(true) network configuration will be persistent, otherwise it is temporary. Default I(true) since Ansible 2.8."
|
||||
type: bool
|
||||
default: True
|
||||
sync_networks:
|
||||
description:
|
||||
- "If I(true) all networks will be synchronized before modification"
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 2.8
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# In all examples the durability of the configuration created is dependent on the 'save' option value:
|
||||
|
||||
# Create bond on eth0 and eth1 interface, and put 'myvlan' network on top of it and persist the new configuration:
|
||||
- name: Bonds
|
||||
ovirt_host_network:
|
||||
name: myhost
|
||||
save: yes
|
||||
bond:
|
||||
name: bond0
|
||||
mode: 2
|
||||
interfaces:
|
||||
- eth1
|
||||
- eth2
|
||||
networks:
|
||||
- name: myvlan
|
||||
boot_protocol: static
|
||||
address: 1.2.3.4
|
||||
netmask: 255.255.255.0
|
||||
gateway: 1.2.3.4
|
||||
version: v4
|
||||
|
||||
# Create bond on eth1 and eth2 interface, specifying both mode and miimon:
|
||||
- name: Bonds
|
||||
ovirt_host_network:
|
||||
name: myhost
|
||||
bond:
|
||||
name: bond0
|
||||
mode: 1
|
||||
options:
|
||||
miimon: 200
|
||||
interfaces:
|
||||
- eth1
|
||||
- eth2
|
||||
|
||||
# Remove bond0 bond from host interfaces:
|
||||
- ovirt_host_network:
|
||||
state: absent
|
||||
name: myhost
|
||||
bond:
|
||||
name: bond0
|
||||
|
||||
# Assign myvlan1 and myvlan2 vlans to host eth0 interface:
|
||||
- ovirt_host_network:
|
||||
name: myhost
|
||||
interface: eth0
|
||||
networks:
|
||||
- name: myvlan1
|
||||
- name: myvlan2
|
||||
|
||||
# Remove myvlan2 vlan from host eth0 interface:
|
||||
- ovirt_host_network:
|
||||
state: absent
|
||||
name: myhost
|
||||
interface: eth0
|
||||
networks:
|
||||
- name: myvlan2
|
||||
|
||||
# Remove all networks/vlans from host eth0 interface:
|
||||
- ovirt_host_network:
|
||||
state: absent
|
||||
name: myhost
|
||||
interface: eth0
|
||||
|
||||
# Add custom_properties to network:
|
||||
- ovirt_host_network:
|
||||
name: myhost
|
||||
interface: eth0
|
||||
networks:
|
||||
- name: myvlan1
|
||||
custom_properties:
|
||||
- name: bridge_opts
|
||||
value: gc_timer=10
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the host NIC which is managed
|
||||
returned: On success if host NIC is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
host_nic:
|
||||
description: "Dictionary of all the host NIC attributes. Host NIC attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/host_nic."
|
||||
returned: On success if host NIC is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils import six
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_dict_of_struct,
|
||||
get_entity,
|
||||
get_link_name,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
engine_supported
|
||||
)
|
||||
|
||||
|
||||
def get_bond_options(mode, usr_opts):
|
||||
MIIMON_100 = dict(miimon='100')
|
||||
DEFAULT_MODE_OPTS = {
|
||||
'1': MIIMON_100,
|
||||
'2': MIIMON_100,
|
||||
'3': MIIMON_100,
|
||||
'4': dict(xmit_hash_policy='2', **MIIMON_100)
|
||||
}
|
||||
|
||||
options = []
|
||||
if mode is None:
|
||||
return options
|
||||
|
||||
def get_type_name(mode_number):
|
||||
"""
|
||||
We need to maintain this type strings, for the __compare_options method,
|
||||
for easier comparision.
|
||||
"""
|
||||
modes = [
|
||||
'Active-Backup',
|
||||
'Load balance (balance-xor)',
|
||||
None,
|
||||
'Dynamic link aggregation (802.3ad)',
|
||||
]
|
||||
if (not 0 < mode_number <= len(modes)):
|
||||
return None
|
||||
return modes[mode_number - 1]
|
||||
|
||||
try:
|
||||
mode_number = int(mode)
|
||||
except ValueError:
|
||||
raise Exception('Bond mode must be a number.')
|
||||
|
||||
options.append(
|
||||
otypes.Option(
|
||||
name='mode',
|
||||
type=get_type_name(mode_number),
|
||||
value=str(mode_number)
|
||||
)
|
||||
)
|
||||
|
||||
opts_dict = DEFAULT_MODE_OPTS.get(str(mode), {})
|
||||
if usr_opts is not None:
|
||||
opts_dict.update(**usr_opts)
|
||||
|
||||
options.extend(
|
||||
[otypes.Option(name=opt, value=str(value))
|
||||
for opt, value in six.iteritems(opts_dict)]
|
||||
)
|
||||
return options
|
||||
|
||||
|
||||
class HostNetworksModule(BaseModule):
|
||||
|
||||
def __compare_options(self, new_options, old_options):
|
||||
return sorted((get_dict_of_struct(opt) for opt in new_options),
|
||||
key=lambda x: x["name"]) != sorted((get_dict_of_struct(opt) for opt in old_options),
|
||||
key=lambda x: x["name"])
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.Host()
|
||||
|
||||
def update_custom_properties(self, attachments_service, attachment, network):
|
||||
if network.get('custom_properties'):
|
||||
current = []
|
||||
if attachment.properties:
|
||||
current = [(cp.name, str(cp.value)) for cp in attachment.properties]
|
||||
passed = [(cp.get('name'), str(cp.get('value'))) for cp in network.get('custom_properties') if cp]
|
||||
if sorted(current) != sorted(passed):
|
||||
attachment.properties = [
|
||||
otypes.Property(
|
||||
name=prop.get('name'),
|
||||
value=prop.get('value')
|
||||
) for prop in network.get('custom_properties')
|
||||
]
|
||||
if not self._module.check_mode:
|
||||
attachments_service.service(attachment.id).update(attachment)
|
||||
self.changed = True
|
||||
|
||||
def update_address(self, attachments_service, attachment, network):
|
||||
# Check if there is any change in address assignments and
|
||||
# update it if needed:
|
||||
for ip in attachment.ip_address_assignments:
|
||||
if str(ip.ip.version) == network.get('version', 'v4'):
|
||||
changed = False
|
||||
if not equal(network.get('boot_protocol'), str(ip.assignment_method)):
|
||||
ip.assignment_method = otypes.BootProtocol(network.get('boot_protocol'))
|
||||
changed = True
|
||||
if not equal(network.get('address'), ip.ip.address):
|
||||
ip.ip.address = network.get('address')
|
||||
changed = True
|
||||
if not equal(network.get('gateway'), ip.ip.gateway):
|
||||
ip.ip.gateway = network.get('gateway')
|
||||
changed = True
|
||||
if not equal(network.get('netmask'), ip.ip.netmask):
|
||||
ip.ip.netmask = network.get('netmask')
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
if not self._module.check_mode:
|
||||
attachments_service.service(attachment.id).update(attachment)
|
||||
self.changed = True
|
||||
break
|
||||
|
||||
def has_update(self, nic_service):
|
||||
update = False
|
||||
bond = self._module.params['bond']
|
||||
networks = self._module.params['networks']
|
||||
labels = self._module.params['labels']
|
||||
nic = get_entity(nic_service)
|
||||
|
||||
if nic is None:
|
||||
return update
|
||||
|
||||
# Check if bond configuration should be updated:
|
||||
if bond:
|
||||
update = self.__compare_options(get_bond_options(bond.get('mode'), bond.get('options')), getattr(nic.bonding, 'options', []))
|
||||
update = update or not equal(
|
||||
sorted(bond.get('interfaces')) if bond.get('interfaces') else None,
|
||||
sorted(get_link_name(self._connection, s) for s in nic.bonding.slaves)
|
||||
)
|
||||
|
||||
# Check if labels need to be updated on interface/bond:
|
||||
if labels:
|
||||
net_labels = nic_service.network_labels_service().list()
|
||||
# If any labels which user passed aren't assigned, relabel the interface:
|
||||
if sorted(labels) != sorted([lbl.id for lbl in net_labels]):
|
||||
return True
|
||||
|
||||
if not networks:
|
||||
return update
|
||||
|
||||
# Check if networks attachments configuration should be updated:
|
||||
attachments_service = nic_service.network_attachments_service()
|
||||
network_names = [network.get('name') for network in networks]
|
||||
|
||||
attachments = {}
|
||||
for attachment in attachments_service.list():
|
||||
name = get_link_name(self._connection, attachment.network)
|
||||
if name in network_names:
|
||||
attachments[name] = attachment
|
||||
|
||||
for network in networks:
|
||||
attachment = attachments.get(network.get('name'))
|
||||
# If attachment don't exists, we need to create it:
|
||||
if attachment is None:
|
||||
return True
|
||||
self.update_custom_properties(attachments_service, attachment, network)
|
||||
self.update_address(attachments_service, attachment, network)
|
||||
|
||||
return update
|
||||
|
||||
def _action_save_configuration(self, entity):
|
||||
if not self._module.check_mode:
|
||||
self._service.service(entity.id).commit_net_config()
|
||||
self.changed = True
|
||||
|
||||
|
||||
def needs_sync(nics_service):
|
||||
nics = nics_service.list()
|
||||
for nic in nics:
|
||||
nic_service = nics_service.nic_service(nic.id)
|
||||
for network_attachment_service in nic_service.network_attachments_service().list():
|
||||
if not network_attachment_service.in_sync:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(aliases=['host'], required=True),
|
||||
bond=dict(default=None, type='dict'),
|
||||
interface=dict(default=None),
|
||||
networks=dict(default=None, type='list'),
|
||||
labels=dict(default=None, type='list'),
|
||||
check=dict(default=None, type='bool'),
|
||||
save=dict(default=True, type='bool'),
|
||||
sync_networks=dict(default=False, type='bool'),
|
||||
)
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
host_networks_module = HostNetworksModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=hosts_service,
|
||||
)
|
||||
|
||||
host = host_networks_module.search_entity()
|
||||
if host is None:
|
||||
raise Exception("Host '%s' was not found." % module.params['name'])
|
||||
|
||||
bond = module.params['bond']
|
||||
interface = module.params['interface']
|
||||
networks = module.params['networks']
|
||||
labels = module.params['labels']
|
||||
nic_name = bond.get('name') if bond else module.params['interface']
|
||||
|
||||
host_service = hosts_service.host_service(host.id)
|
||||
nics_service = host_service.nics_service()
|
||||
nic = search_by_name(nics_service, nic_name)
|
||||
|
||||
if module.params["sync_networks"]:
|
||||
if needs_sync(nics_service):
|
||||
if not module.check_mode:
|
||||
host_service.sync_all_networks()
|
||||
host_networks_module.changed = True
|
||||
|
||||
network_names = [network['name'] for network in networks or []]
|
||||
state = module.params['state']
|
||||
|
||||
if (
|
||||
state == 'present' and
|
||||
(nic is None or host_networks_module.has_update(nics_service.service(nic.id)))
|
||||
):
|
||||
# Remove networks which are attached to different interface then user want:
|
||||
attachments_service = host_service.network_attachments_service()
|
||||
|
||||
# Append attachment ID to network if needs update:
|
||||
for a in attachments_service.list():
|
||||
current_network_name = get_link_name(connection, a.network)
|
||||
if current_network_name in network_names:
|
||||
for n in networks:
|
||||
if n['name'] == current_network_name:
|
||||
n['id'] = a.id
|
||||
|
||||
# Check if we have to break some bonds:
|
||||
removed_bonds = []
|
||||
if nic is not None:
|
||||
for host_nic in nics_service.list():
|
||||
if host_nic.bonding and nic.id in [slave.id for slave in host_nic.bonding.slaves]:
|
||||
removed_bonds.append(otypes.HostNic(id=host_nic.id))
|
||||
|
||||
# Assign the networks:
|
||||
setup_params = dict(
|
||||
entity=host,
|
||||
action='setup_networks',
|
||||
check_connectivity=module.params['check'],
|
||||
removed_bonds=removed_bonds if removed_bonds else None,
|
||||
modified_bonds=[
|
||||
otypes.HostNic(
|
||||
name=bond.get('name'),
|
||||
bonding=otypes.Bonding(
|
||||
options=get_bond_options(bond.get('mode'), bond.get('options')),
|
||||
slaves=[
|
||||
otypes.HostNic(name=i) for i in bond.get('interfaces', [])
|
||||
],
|
||||
),
|
||||
),
|
||||
] if bond else None,
|
||||
modified_labels=[
|
||||
otypes.NetworkLabel(
|
||||
id=str(name),
|
||||
host_nic=otypes.HostNic(
|
||||
name=bond.get('name') if bond else interface
|
||||
),
|
||||
) for name in labels
|
||||
] if labels else None,
|
||||
modified_network_attachments=[
|
||||
otypes.NetworkAttachment(
|
||||
id=network.get('id'),
|
||||
network=otypes.Network(
|
||||
name=network['name']
|
||||
) if network['name'] else None,
|
||||
host_nic=otypes.HostNic(
|
||||
name=bond.get('name') if bond else interface
|
||||
),
|
||||
ip_address_assignments=[
|
||||
otypes.IpAddressAssignment(
|
||||
assignment_method=otypes.BootProtocol(
|
||||
network.get('boot_protocol', 'none')
|
||||
),
|
||||
ip=otypes.Ip(
|
||||
address=network.get('address'),
|
||||
gateway=network.get('gateway'),
|
||||
netmask=network.get('netmask'),
|
||||
version=otypes.IpVersion(
|
||||
network.get('version')
|
||||
) if network.get('version') else None,
|
||||
),
|
||||
),
|
||||
],
|
||||
properties=[
|
||||
otypes.Property(
|
||||
name=prop.get('name'),
|
||||
value=prop.get('value')
|
||||
) for prop in network.get('custom_properties')
|
||||
]
|
||||
) for network in networks
|
||||
] if networks else None,
|
||||
)
|
||||
if engine_supported(connection, '4.3'):
|
||||
setup_params['commit_on_success'] = module.params['save']
|
||||
elif module.params['save']:
|
||||
setup_params['post_action'] = host_networks_module._action_save_configuration
|
||||
host_networks_module.action(**setup_params)
|
||||
elif state == 'absent' and nic:
|
||||
attachments = []
|
||||
nic_service = nics_service.nic_service(nic.id)
|
||||
|
||||
attached_labels = set([str(lbl.id) for lbl in nic_service.network_labels_service().list()])
|
||||
if networks:
|
||||
attachments_service = nic_service.network_attachments_service()
|
||||
attachments = attachments_service.list()
|
||||
attachments = [
|
||||
attachment for attachment in attachments
|
||||
if get_link_name(connection, attachment.network) in network_names
|
||||
]
|
||||
|
||||
# Remove unmanaged networks:
|
||||
unmanaged_networks_service = host_service.unmanaged_networks_service()
|
||||
unmanaged_networks = [(u.id, u.name) for u in unmanaged_networks_service.list()]
|
||||
for net_id, net_name in unmanaged_networks:
|
||||
if net_name in network_names:
|
||||
if not module.check_mode:
|
||||
unmanaged_networks_service.unmanaged_network_service(net_id).remove()
|
||||
host_networks_module.changed = True
|
||||
|
||||
# Need to check if there are any labels to be removed, as backend fail
|
||||
# if we try to send remove non existing label, for bond and attachments it's OK:
|
||||
if (labels and set(labels).intersection(attached_labels)) or bond or attachments:
|
||||
setup_params = dict(
|
||||
entity=host,
|
||||
action='setup_networks',
|
||||
check_connectivity=module.params['check'],
|
||||
removed_bonds=[
|
||||
otypes.HostNic(
|
||||
name=bond.get('name'),
|
||||
),
|
||||
] if bond else None,
|
||||
removed_labels=[
|
||||
otypes.NetworkLabel(id=str(name)) for name in labels
|
||||
] if labels else None,
|
||||
removed_network_attachments=attachments if attachments else None,
|
||||
)
|
||||
if engine_supported(connection, '4.3'):
|
||||
setup_params['commit_on_success'] = module.params['save']
|
||||
elif module.params['save']:
|
||||
setup_params['post_action'] = host_networks_module._action_save_configuration
|
||||
host_networks_module.action(**setup_params)
|
||||
|
||||
nic = search_by_name(nics_service, nic_name)
|
||||
module.exit_json(**{
|
||||
'changed': host_networks_module.changed,
|
||||
'id': nic.id if nic else None,
|
||||
'host_nic': get_dict_of_struct(nic),
|
||||
})
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,261 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_host_pm
|
||||
short_description: Module to manage power management of hosts in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage power management of hosts in oVirt/RHV."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the host to manage."
|
||||
required: true
|
||||
aliases: ['host']
|
||||
state:
|
||||
description:
|
||||
- "Should the host be present/absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
address:
|
||||
description:
|
||||
- "Address of the power management interface."
|
||||
username:
|
||||
description:
|
||||
- "Username to be used to connect to power management interface."
|
||||
password:
|
||||
description:
|
||||
- "Password of the user specified in C(username) parameter."
|
||||
type:
|
||||
description:
|
||||
- "Type of the power management. oVirt/RHV predefined values are I(drac5), I(ipmilan), I(rsa),
|
||||
I(bladecenter), I(alom), I(apc), I(apc_snmp), I(eps), I(wti), I(rsb), I(cisco_ucs),
|
||||
I(drac7), I(hpblade), I(ilo), I(ilo2), I(ilo3), I(ilo4), I(ilo_ssh),
|
||||
but user can have defined custom type."
|
||||
port:
|
||||
description:
|
||||
- "Power management interface port."
|
||||
options:
|
||||
description:
|
||||
- "Dictionary of additional fence agent options (including Power Management slot)."
|
||||
- "Additional information about options can be found at U(https://github.com/ClusterLabs/fence-agents/blob/master/doc/FenceAgentAPI.md)."
|
||||
encrypt_options:
|
||||
description:
|
||||
- "If I(true) options will be encrypted when send to agent."
|
||||
aliases: ['encrypt']
|
||||
type: bool
|
||||
order:
|
||||
description:
|
||||
- "Integer value specifying, by default it's added at the end."
|
||||
version_added: "2.5"
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Add fence agent to host 'myhost'
|
||||
- ovirt_host_pm:
|
||||
name: myhost
|
||||
address: 1.2.3.4
|
||||
options:
|
||||
myoption1: x
|
||||
myoption2: y
|
||||
username: admin
|
||||
password: admin
|
||||
port: 3333
|
||||
type: ipmilan
|
||||
|
||||
# Add fence agent to host 'myhost' using 'slot' option
|
||||
- ovirt_host_pm:
|
||||
name: myhost
|
||||
address: 1.2.3.4
|
||||
options:
|
||||
myoption1: x
|
||||
myoption2: y
|
||||
slot: myslot
|
||||
username: admin
|
||||
password: admin
|
||||
port: 3333
|
||||
type: ipmilan
|
||||
|
||||
|
||||
# Remove ipmilan fence agent with address 1.2.3.4 on host 'myhost'
|
||||
- ovirt_host_pm:
|
||||
state: absent
|
||||
name: myhost
|
||||
address: 1.2.3.4
|
||||
type: ipmilan
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the agent which is managed
|
||||
returned: On success if agent is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
agent:
|
||||
description: "Dictionary of all the agent attributes. Agent attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/agent."
|
||||
returned: On success if agent is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
class HostModule(BaseModule):
|
||||
def build_entity(self):
|
||||
return otypes.Host(
|
||||
power_management=otypes.PowerManagement(
|
||||
enabled=True,
|
||||
),
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
return equal(True, entity.power_management.enabled)
|
||||
|
||||
|
||||
class HostPmModule(BaseModule):
|
||||
|
||||
def pre_create(self, entity):
|
||||
# Save the entity, so we know if Agent already existed
|
||||
self.entity = entity
|
||||
|
||||
def build_entity(self):
|
||||
last = next((s for s in sorted([a.order for a in self._service.list()])), 0)
|
||||
order = self.param('order') if self.param('order') is not None else self.entity.order if self.entity else last + 1
|
||||
return otypes.Agent(
|
||||
address=self._module.params['address'],
|
||||
encrypt_options=self._module.params['encrypt_options'],
|
||||
options=[
|
||||
otypes.Option(
|
||||
name=name,
|
||||
value=value,
|
||||
) for name, value in self._module.params['options'].items()
|
||||
] if self._module.params['options'] else None,
|
||||
password=self._module.params['password'],
|
||||
port=self._module.params['port'],
|
||||
type=self._module.params['type'],
|
||||
username=self._module.params['username'],
|
||||
order=order,
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
def check_options():
|
||||
if self.param('options'):
|
||||
current = []
|
||||
if entity.options:
|
||||
current = [(opt.name, str(opt.value)) for opt in entity.options]
|
||||
passed = [(k, str(v)) for k, v in self.param('options').items()]
|
||||
return sorted(current) == sorted(passed)
|
||||
return True
|
||||
|
||||
return (
|
||||
check_options() and
|
||||
equal(self._module.params.get('address'), entity.address) and
|
||||
equal(self._module.params.get('encrypt_options'), entity.encrypt_options) and
|
||||
equal(self._module.params.get('username'), entity.username) and
|
||||
equal(self._module.params.get('port'), entity.port) and
|
||||
equal(self._module.params.get('type'), entity.type) and
|
||||
equal(self._module.params.get('order'), entity.order)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(default=None, required=True, aliases=['host']),
|
||||
address=dict(default=None),
|
||||
username=dict(default=None),
|
||||
password=dict(default=None, no_log=True),
|
||||
type=dict(default=None),
|
||||
port=dict(default=None, type='int'),
|
||||
order=dict(default=None, type='int'),
|
||||
options=dict(default=None, type='dict'),
|
||||
encrypt_options=dict(default=None, type='bool', aliases=['encrypt']),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
host = search_by_name(hosts_service, module.params['name'])
|
||||
fence_agents_service = hosts_service.host_service(host.id).fence_agents_service()
|
||||
|
||||
host_pm_module = HostPmModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=fence_agents_service,
|
||||
)
|
||||
host_module = HostModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=hosts_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
agent = host_pm_module.search_entity(
|
||||
search_params={
|
||||
'address': module.params['address'],
|
||||
'type': module.params['type'],
|
||||
}
|
||||
)
|
||||
ret = host_pm_module.create(entity=agent)
|
||||
|
||||
# Enable Power Management, if it's not enabled:
|
||||
host_module.create(entity=host)
|
||||
elif state == 'absent':
|
||||
agent = host_pm_module.search_entity(
|
||||
search_params={
|
||||
'address': module.params['address'],
|
||||
'type': module.params['type'],
|
||||
}
|
||||
)
|
||||
ret = host_pm_module.remove(entity=agent)
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,182 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2017 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_host_storage_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV HostStorages (applicable only for block storage)
|
||||
author: "Daniel Erez (@derez)"
|
||||
version_added: "2.4"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV HostStorages (applicable only for block storage)."
|
||||
- This module was called C(ovirt_host_storage_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_host_storage_info) module no longer returns C(ansible_facts)!
|
||||
options:
|
||||
host:
|
||||
description:
|
||||
- "Host to get device list from."
|
||||
required: true
|
||||
iscsi:
|
||||
description:
|
||||
- "Dictionary with values for iSCSI storage type:"
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- "Address of the iSCSI storage server."
|
||||
target:
|
||||
description:
|
||||
- "The target IQN for the storage device."
|
||||
username:
|
||||
description:
|
||||
- "A CHAP user name for logging into a target."
|
||||
password:
|
||||
description:
|
||||
- "A CHAP password for logging into a target."
|
||||
portal:
|
||||
description:
|
||||
- "The portal being used to connect with iscsi."
|
||||
version_added: 2.10
|
||||
fcp:
|
||||
description:
|
||||
- "Dictionary with values for fibre channel storage type:"
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- "Address of the fibre channel storage server."
|
||||
port:
|
||||
description:
|
||||
- "Port of the fibre channel storage server."
|
||||
lun_id:
|
||||
description:
|
||||
- "LUN id."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about HostStorages with specified target and address:
|
||||
- ovirt_host_storage_info:
|
||||
host: myhost
|
||||
iscsi:
|
||||
target: iqn.2016-08-09.domain-01:nickname
|
||||
address: 10.34.63.204
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_host_storages }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_host_storages:
|
||||
description: "List of dictionaries describing the HostStorage. HostStorage attributes are mapped to dictionary keys,
|
||||
all HostStorage attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/host_storage."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
get_id_by_name,
|
||||
)
|
||||
|
||||
|
||||
def _login(host_service, iscsi):
|
||||
host_service.iscsi_login(
|
||||
iscsi=otypes.IscsiDetails(
|
||||
username=iscsi.get('username'),
|
||||
password=iscsi.get('password'),
|
||||
address=iscsi.get('address'),
|
||||
target=iscsi.get('target'),
|
||||
portal=iscsi.get('portal')
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _get_storage_type(params):
|
||||
for sd_type in ['iscsi', 'fcp']:
|
||||
if params.get(sd_type) is not None:
|
||||
return sd_type
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
host=dict(required=True),
|
||||
iscsi=dict(default=None, type='dict'),
|
||||
fcp=dict(default=None, type='dict'),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_host_storage_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_host_storage_facts' module has been renamed to 'ovirt_host_storage_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
|
||||
# Get Host
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
host_id = get_id_by_name(hosts_service, module.params['host'])
|
||||
storage_type = _get_storage_type(module.params)
|
||||
host_service = hosts_service.host_service(host_id)
|
||||
|
||||
if storage_type == 'iscsi':
|
||||
# Login
|
||||
iscsi = module.params.get('iscsi')
|
||||
_login(host_service, iscsi)
|
||||
|
||||
# Get LUNs exposed from the specified target
|
||||
host_storages = host_service.storage_service().list()
|
||||
|
||||
if storage_type == 'iscsi':
|
||||
filterred_host_storages = [host_storage for host_storage in host_storages
|
||||
if host_storage.type == otypes.StorageType.ISCSI]
|
||||
if 'target' in iscsi:
|
||||
filterred_host_storages = [host_storage for host_storage in filterred_host_storages
|
||||
if iscsi.get('target') == host_storage.logical_units[0].target]
|
||||
elif storage_type == 'fcp':
|
||||
filterred_host_storages = [host_storage for host_storage in host_storages
|
||||
if host_storage.type == otypes.StorageType.FCP]
|
||||
|
||||
result = dict(
|
||||
ovirt_host_storages=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in filterred_host_storages
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,592 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_instance_type
|
||||
short_description: Module to manage Instance Types in oVirt/RHV
|
||||
version_added: "2.8"
|
||||
author:
|
||||
- Martin Necas (@mnecas)
|
||||
- Ondra Machacek (@machacekondra)
|
||||
description:
|
||||
- This module manages whole lifecycle of the Instance Type in oVirt/RHV.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the Instance Type to manage.
|
||||
- If instance type don't exists C(name) is required. Otherwise C(id) or C(name) can be used.
|
||||
id:
|
||||
description:
|
||||
- ID of the Instance Type to manage.
|
||||
state:
|
||||
description:
|
||||
- Should the Instance Type be present/absent.
|
||||
- I(present) state will create/update instance type and don't change its state if it already exists.
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
memory:
|
||||
description:
|
||||
- Amount of memory of the Instance Type. Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB).
|
||||
- Default value is set by engine.
|
||||
memory_guaranteed:
|
||||
description:
|
||||
- Amount of minimal guaranteed memory of the Instance Type.
|
||||
Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB).
|
||||
- C(memory_guaranteed) parameter can't be lower than C(memory) parameter.
|
||||
- Default value is set by engine.
|
||||
nics:
|
||||
description:
|
||||
- List of NICs, which should be attached to Virtual Machine. NIC is described by following dictionary.
|
||||
- C(name) - Name of the NIC.
|
||||
- C(profile_name) - Profile name where NIC should be attached.
|
||||
- C(interface) - Type of the network interface. One of following I(virtio), I(e1000), I(rtl8139), default is I(virtio).
|
||||
- C(mac_address) - Custom MAC address of the network interface, by default it's obtained from MAC pool.
|
||||
- NOTE - This parameter is used only when C(state) is I(running) or I(present) and is able to only create NICs.
|
||||
To manage NICs of the instance type in more depth please use M(ovirt_nic) module instead.
|
||||
memory_max:
|
||||
description:
|
||||
- Upper bound of instance type memory up to which memory hot-plug can be performed.
|
||||
Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB).
|
||||
- Default value is set by engine.
|
||||
cpu_cores:
|
||||
description:
|
||||
- Number of virtual CPUs cores of the Instance Type.
|
||||
- Default value is set by oVirt/RHV engine.
|
||||
cpu_sockets:
|
||||
description:
|
||||
- Number of virtual CPUs sockets of the Instance Type.
|
||||
- Default value is set by oVirt/RHV engine.
|
||||
cpu_threads:
|
||||
description:
|
||||
- Number of virtual CPUs sockets of the Instance Type.
|
||||
- Default value is set by oVirt/RHV engine.
|
||||
operating_system:
|
||||
description:
|
||||
- Operating system of the Instance Type.
|
||||
- Default value is set by oVirt/RHV engine.
|
||||
- "Possible values: debian_7, freebsd, freebsdx64, other, other_linux,
|
||||
other_linux_ppc64, other_ppc64, rhel_3, rhel_4, rhel_4x64, rhel_5, rhel_5x64,
|
||||
rhel_6, rhel_6x64, rhel_6_ppc64, rhel_7x64, rhel_7_ppc64, sles_11, sles_11_ppc64,
|
||||
ubuntu_12_04, ubuntu_12_10, ubuntu_13_04, ubuntu_13_10, ubuntu_14_04, ubuntu_14_04_ppc64,
|
||||
windows_10, windows_10x64, windows_2003, windows_2003x64, windows_2008, windows_2008x64,
|
||||
windows_2008r2x64, windows_2008R2x64, windows_2012x64, windows_2012R2x64, windows_7,
|
||||
windows_7x64, windows_8, windows_8x64, windows_xp"
|
||||
boot_devices:
|
||||
description:
|
||||
- List of boot devices which should be used to boot. For example C([ cdrom, hd ]).
|
||||
- Default value is set by oVirt/RHV engine.
|
||||
choices: [ cdrom, hd, network ]
|
||||
serial_console:
|
||||
description:
|
||||
- "I(True) enable VirtIO serial console, I(False) to disable it. By default is chosen by oVirt/RHV engine."
|
||||
type: bool
|
||||
usb_support:
|
||||
description:
|
||||
- "I(True) enable USB support, I(False) to disable it. By default is chosen by oVirt/RHV engine."
|
||||
type: bool
|
||||
high_availability:
|
||||
description:
|
||||
- If I(yes) Instance Type will be set as highly available.
|
||||
- If I(no) Instance Type won't be set as highly available.
|
||||
- If no value is passed, default value is set by oVirt/RHV engine.
|
||||
type: bool
|
||||
high_availability_priority:
|
||||
description:
|
||||
- Indicates the priority of the instance type inside the run and migration queues.
|
||||
Instance Type with higher priorities will be started and migrated before instance types with lower
|
||||
priorities. The value is an integer between 0 and 100. The higher the value, the higher the priority.
|
||||
- If no value is passed, default value is set by oVirt/RHV engine.
|
||||
watchdog:
|
||||
description:
|
||||
- "Assign watchdog device for the instance type."
|
||||
- "Watchdogs is a dictionary which can have following values:"
|
||||
- "C(model) - Model of the watchdog device. For example: I(i6300esb), I(diag288) or I(null)."
|
||||
- "C(action) - Watchdog action to be performed when watchdog is triggered. For example: I(none), I(reset), I(poweroff), I(pause) or I(dump)."
|
||||
host:
|
||||
description:
|
||||
- Specify host where Instance Type should be running. By default the host is chosen by engine scheduler.
|
||||
- This parameter is used only when C(state) is I(running) or I(present).
|
||||
graphical_console:
|
||||
description:
|
||||
- "Assign graphical console to the instance type."
|
||||
- "Graphical console is a dictionary which can have following values:"
|
||||
- "C(headless_mode) - If I(true) disable the graphics console for this instance type."
|
||||
- "C(protocol) - Graphical protocol, a list of I(spice), I(vnc), or both."
|
||||
description:
|
||||
description:
|
||||
- "Description of the instance type."
|
||||
cpu_mode:
|
||||
description:
|
||||
- "CPU mode of the instance type. It can be some of the following: I(host_passthrough), I(host_model) or I(custom)."
|
||||
- "For I(host_passthrough) CPU type you need to set C(placement_policy) to I(pinned)."
|
||||
- "If no value is passed, default value is set by oVirt/RHV engine."
|
||||
rng_device:
|
||||
description:
|
||||
- "Random number generator (RNG). You can choose of one the following devices I(urandom), I(random) or I(hwrng)."
|
||||
- "In order to select I(hwrng), you must have it enabled on cluster first."
|
||||
- "/dev/urandom is used for cluster version >= 4.1, and /dev/random for cluster version <= 4.0"
|
||||
rng_bytes:
|
||||
description:
|
||||
- "Number of bytes allowed to consume per period."
|
||||
rng_period:
|
||||
description:
|
||||
- "Duration of one period in milliseconds."
|
||||
placement_policy:
|
||||
description:
|
||||
- "The configuration of the instance type's placement policy."
|
||||
- "Placement policy can be one of the following values:"
|
||||
- "C(migratable) - Allow manual and automatic migration."
|
||||
- "C(pinned) - Do not allow migration."
|
||||
- "C(user_migratable) - Allow manual migration only."
|
||||
- "If no value is passed, default value is set by oVirt/RHV engine."
|
||||
cpu_pinning:
|
||||
description:
|
||||
- "CPU Pinning topology to map instance type CPU to host CPU."
|
||||
- "CPU Pinning topology is a list of dictionary which can have following values:"
|
||||
- "C(cpu) - Number of the host CPU."
|
||||
- "C(vcpu) - Number of the instance type CPU."
|
||||
soundcard_enabled:
|
||||
description:
|
||||
- "If I(true), the sound card is added to the instance type."
|
||||
type: bool
|
||||
smartcard_enabled:
|
||||
description:
|
||||
- "If I(true), use smart card authentication."
|
||||
type: bool
|
||||
virtio_scsi:
|
||||
description:
|
||||
- "If I(true), virtio scsi will be enabled."
|
||||
type: bool
|
||||
io_threads:
|
||||
description:
|
||||
- "Number of IO threads used by instance type. I(0) means IO threading disabled."
|
||||
ballooning_enabled:
|
||||
description:
|
||||
- "If I(true), use memory ballooning."
|
||||
- "Memory balloon is a guest device, which may be used to re-distribute / reclaim the host memory
|
||||
based on instance type needs in a dynamic way. In this way it's possible to create memory over commitment states."
|
||||
type: bool
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create instance type
|
||||
- name: Create instance type
|
||||
ovirt_instance_type:
|
||||
state: present
|
||||
name: myit
|
||||
rng_device: hwrng
|
||||
rng_bytes: 200
|
||||
rng_period: 200
|
||||
soundcard_enabled: true
|
||||
virtio_scsi: true
|
||||
boot_devices:
|
||||
- network
|
||||
|
||||
# Remove instance type
|
||||
- ovirt_instance_type:
|
||||
state: absent
|
||||
name: myit
|
||||
|
||||
|
||||
# Create instance type with predefined memory and cpu limits.
|
||||
- ovirt_instance_type:
|
||||
state: present
|
||||
name: myit
|
||||
memory: 2GiB
|
||||
cpu_cores: 2
|
||||
cpu_sockets: 2
|
||||
nics:
|
||||
- name: nic1
|
||||
|
||||
# Enable usb support and serial console
|
||||
- ovirt_instance_type:
|
||||
name: myit
|
||||
usb_support: True
|
||||
serial_console: True
|
||||
|
||||
# Use graphical console with spice and vnc
|
||||
- name: Create a instance type that has the console configured for both Spice and VNC
|
||||
ovirt_instance_type:
|
||||
name: myit
|
||||
graphical_console:
|
||||
protocol:
|
||||
- spice
|
||||
- vnc
|
||||
'''
|
||||
|
||||
|
||||
RETURN = '''
|
||||
|
||||
id:
|
||||
description: ID of the instance type which is managed
|
||||
returned: On success if instance type is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
instancetype:
|
||||
description: "Dictionary of all the instance type attributes. instance type attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/instance_type."
|
||||
returned: On success if instance type is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_params,
|
||||
check_sdk,
|
||||
convert_to_bytes,
|
||||
create_connection,
|
||||
equal,
|
||||
get_dict_of_struct,
|
||||
get_entity,
|
||||
get_link_name,
|
||||
get_id_by_name,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_attributes,
|
||||
search_by_name,
|
||||
wait,
|
||||
)
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class InstanceTypeModule(BaseModule):
|
||||
def build_entity(self):
|
||||
return otypes.InstanceType(
|
||||
id=self.param('id'),
|
||||
name=self.param('name'),
|
||||
console=(
|
||||
otypes.Console(enabled=self.param('serial_console'))
|
||||
) if self.param('serial_console') is not None else None,
|
||||
usb=(
|
||||
otypes.Usb(enabled=self.param('usb_support'))
|
||||
) if self.param('usb_support') is not None else None,
|
||||
high_availability=otypes.HighAvailability(
|
||||
enabled=self.param('high_availability'),
|
||||
priority=self.param('high_availability_priority'),
|
||||
) if self.param('high_availability') is not None or self.param('high_availability_priority') else None,
|
||||
cpu=otypes.Cpu(
|
||||
topology=otypes.CpuTopology(
|
||||
cores=self.param('cpu_cores'),
|
||||
sockets=self.param('cpu_sockets'),
|
||||
threads=self.param('cpu_threads'),
|
||||
) if any((
|
||||
self.param('cpu_cores'),
|
||||
self.param('cpu_sockets'),
|
||||
self.param('cpu_threads')
|
||||
)) else None,
|
||||
cpu_tune=otypes.CpuTune(
|
||||
vcpu_pins=[
|
||||
otypes.VcpuPin(vcpu=int(pin['vcpu']), cpu_set=str(pin['cpu'])) for pin in self.param('cpu_pinning')
|
||||
],
|
||||
) if self.param('cpu_pinning') else None,
|
||||
mode=otypes.CpuMode(self.param('cpu_mode')) if self.param(
|
||||
'cpu_mode') else None,
|
||||
) if any((
|
||||
self.param('cpu_cores'),
|
||||
self.param('cpu_sockets'),
|
||||
self.param('cpu_threads'),
|
||||
self.param('cpu_mode'),
|
||||
self.param('cpu_pinning')
|
||||
)) else None,
|
||||
os=otypes.OperatingSystem(
|
||||
type=self.param('operating_system'),
|
||||
boot=otypes.Boot(
|
||||
devices=[
|
||||
otypes.BootDevice(dev) for dev in self.param('boot_devices')
|
||||
],
|
||||
) if self.param('boot_devices') else None
|
||||
),
|
||||
rng_device=otypes.RngDevice(
|
||||
source=otypes.RngSource(self.param('rng_device')),
|
||||
rate=otypes.Rate(
|
||||
bytes=self.param('rng_bytes'),
|
||||
period=self.param('rng_period')
|
||||
)
|
||||
) if self.param('rng_device') else None,
|
||||
memory=convert_to_bytes(
|
||||
self.param('memory')
|
||||
) if self.param('memory') else None,
|
||||
virtio_scsi=otypes.VirtioScsi(
|
||||
enabled=self.param('virtio_scsi')
|
||||
) if self.param('virtio_scsi') else None,
|
||||
memory_policy=otypes.MemoryPolicy(
|
||||
guaranteed=convert_to_bytes(self.param('memory_guaranteed')),
|
||||
ballooning=self.param('ballooning_enabled'),
|
||||
max=convert_to_bytes(self.param('memory_max')),
|
||||
) if any((
|
||||
self.param('memory_guaranteed'),
|
||||
self.param('ballooning_enabled') is not None,
|
||||
self.param('memory_max')
|
||||
)) else None,
|
||||
description=self.param('description'),
|
||||
placement_policy=otypes.VmPlacementPolicy(
|
||||
affinity=otypes.VmAffinity(self.param('placement_policy')),
|
||||
hosts=[
|
||||
otypes.Host(name=self.param('host')),
|
||||
] if self.param('host') else None,
|
||||
) if self.param('placement_policy') else None,
|
||||
soundcard_enabled=self.param('soundcard_enabled'),
|
||||
display=otypes.Display(
|
||||
smartcard_enabled=self.param('smartcard_enabled')
|
||||
) if self.param('smartcard_enabled') is not None else None,
|
||||
io=otypes.Io(
|
||||
threads=self.param('io_threads'),
|
||||
) if self.param('io_threads') is not None else None,
|
||||
)
|
||||
|
||||
def __attach_watchdog(self, entity):
|
||||
watchdogs_service = self._service.service(entity.id).watchdogs_service()
|
||||
watchdog = self.param('watchdog')
|
||||
if watchdog is not None:
|
||||
current_watchdog = next(iter(watchdogs_service.list()), None)
|
||||
if watchdog.get('model') is None and current_watchdog:
|
||||
watchdogs_service.watchdog_service(current_watchdog.id).remove()
|
||||
return True
|
||||
elif watchdog.get('model') is not None and current_watchdog is None:
|
||||
watchdogs_service.add(
|
||||
otypes.Watchdog(
|
||||
model=otypes.WatchdogModel(watchdog.get('model').lower()),
|
||||
action=otypes.WatchdogAction(watchdog.get('action')),
|
||||
)
|
||||
)
|
||||
return True
|
||||
elif current_watchdog is not None:
|
||||
if (
|
||||
str(current_watchdog.model).lower() != watchdog.get('model').lower() or
|
||||
str(current_watchdog.action).lower() != watchdog.get('action').lower()
|
||||
):
|
||||
watchdogs_service.watchdog_service(current_watchdog.id).update(
|
||||
otypes.Watchdog(
|
||||
model=otypes.WatchdogModel(watchdog.get('model')),
|
||||
action=otypes.WatchdogAction(watchdog.get('action')),
|
||||
)
|
||||
)
|
||||
return True
|
||||
return False
|
||||
|
||||
def __get_vnic_profile_id(self, nic):
|
||||
"""
|
||||
Return VNIC profile ID looked up by it's name, because there can be
|
||||
more VNIC profiles with same name, other criteria of filter is cluster.
|
||||
"""
|
||||
vnics_service = self._connection.system_service().vnic_profiles_service()
|
||||
clusters_service = self._connection.system_service().clusters_service()
|
||||
cluster = search_by_name(clusters_service, self.param('cluster'))
|
||||
profiles = [
|
||||
profile for profile in vnics_service.list()
|
||||
if profile.name == nic.get('profile_name')
|
||||
]
|
||||
cluster_networks = [
|
||||
net.id for net in self._connection.follow_link(cluster.networks)
|
||||
]
|
||||
try:
|
||||
return next(
|
||||
profile.id for profile in profiles
|
||||
if profile.network.id in cluster_networks
|
||||
)
|
||||
except StopIteration:
|
||||
raise Exception(
|
||||
"Profile '%s' was not found in cluster '%s'" % (
|
||||
nic.get('profile_name'),
|
||||
self.param('cluster')
|
||||
)
|
||||
)
|
||||
|
||||
def __attach_nics(self, entity):
|
||||
# Attach NICs to instance type, if specified:
|
||||
nics_service = self._service.service(entity.id).nics_service()
|
||||
for nic in self.param('nics'):
|
||||
if search_by_name(nics_service, nic.get('name')) is None:
|
||||
if not self._module.check_mode:
|
||||
nics_service.add(
|
||||
otypes.Nic(
|
||||
name=nic.get('name'),
|
||||
interface=otypes.NicInterface(
|
||||
nic.get('interface', 'virtio')
|
||||
),
|
||||
vnic_profile=otypes.VnicProfile(
|
||||
id=self.__get_vnic_profile_id(nic),
|
||||
) if nic.get('profile_name') else None,
|
||||
mac=otypes.Mac(
|
||||
address=nic.get('mac_address')
|
||||
) if nic.get('mac_address') else None,
|
||||
)
|
||||
)
|
||||
self.changed = True
|
||||
|
||||
def __attach_graphical_console(self, entity):
|
||||
graphical_console = self.param('graphical_console')
|
||||
if not graphical_console:
|
||||
return False
|
||||
|
||||
it_service = self._service.instance_type_service(entity.id)
|
||||
gcs_service = it_service.graphics_consoles_service()
|
||||
graphical_consoles = gcs_service.list()
|
||||
# Remove all graphical consoles if there are any:
|
||||
if bool(graphical_console.get('headless_mode')):
|
||||
if not self._module.check_mode:
|
||||
for gc in graphical_consoles:
|
||||
gcs_service.console_service(gc.id).remove()
|
||||
return len(graphical_consoles) > 0
|
||||
|
||||
# If there are not gc add any gc to be added:
|
||||
protocol = graphical_console.get('protocol')
|
||||
if isinstance(protocol, str):
|
||||
protocol = [protocol]
|
||||
|
||||
current_protocols = [str(gc.protocol) for gc in graphical_consoles]
|
||||
if not current_protocols:
|
||||
if not self._module.check_mode:
|
||||
for p in protocol:
|
||||
gcs_service.add(
|
||||
otypes.GraphicsConsole(
|
||||
protocol=otypes.GraphicsType(p),
|
||||
)
|
||||
)
|
||||
return True
|
||||
|
||||
# Update consoles:
|
||||
if sorted(protocol) != sorted(current_protocols):
|
||||
if not self._module.check_mode:
|
||||
for gc in graphical_consoles:
|
||||
gcs_service.console_service(gc.id).remove()
|
||||
for p in protocol:
|
||||
gcs_service.add(
|
||||
otypes.GraphicsConsole(
|
||||
protocol=otypes.GraphicsType(p),
|
||||
)
|
||||
)
|
||||
return True
|
||||
|
||||
def post_update(self, entity):
|
||||
self.post_present(entity.id)
|
||||
|
||||
def post_present(self, entity_id):
|
||||
entity = self._service.service(entity_id).get()
|
||||
self.changed = self.__attach_nics(entity)
|
||||
self.changed = self.__attach_watchdog(entity)
|
||||
self.changed = self.__attach_graphical_console(entity)
|
||||
|
||||
def update_check(self, entity):
|
||||
cpu_mode = getattr(entity.cpu, 'mode')
|
||||
it_display = entity.display
|
||||
return (
|
||||
not self.param('kernel_params_persist') and
|
||||
equal(convert_to_bytes(self.param('memory_guaranteed')), entity.memory_policy.guaranteed) and
|
||||
equal(convert_to_bytes(self.param('memory_max')), entity.memory_policy.max) and
|
||||
equal(self.param('cpu_cores'), entity.cpu.topology.cores) and
|
||||
equal(self.param('cpu_sockets'), entity.cpu.topology.sockets) and
|
||||
equal(self.param('cpu_threads'), entity.cpu.topology.threads) and
|
||||
equal(self.param('cpu_mode'), str(cpu_mode) if cpu_mode else None) and
|
||||
equal(self.param('type'), str(entity.type)) and
|
||||
equal(self.param('name'), str(entity.name)) and
|
||||
equal(self.param('operating_system'), str(entity.os.type)) and
|
||||
equal(self.param('soundcard_enabled'), entity.soundcard_enabled) and
|
||||
equal(self.param('smartcard_enabled'), getattr(it_display, 'smartcard_enabled', False)) and
|
||||
equal(self.param('io_threads'), entity.io.threads) and
|
||||
equal(self.param('ballooning_enabled'), entity.memory_policy.ballooning) and
|
||||
equal(self.param('serial_console'), getattr(entity.console, 'enabled', None)) and
|
||||
equal(self.param('usb_support'), entity.usb.enabled) and
|
||||
equal(self.param('virtio_scsi'), getattr(entity, 'smartcard_enabled', False)) and
|
||||
equal(self.param('high_availability'), entity.high_availability.enabled) and
|
||||
equal(self.param('high_availability_priority'), entity.high_availability.priority) and
|
||||
equal(self.param('boot_devices'), [str(dev) for dev in getattr(entity.os.boot, 'devices', [])]) and
|
||||
equal(self.param('description'), entity.description) and
|
||||
equal(self.param('rng_device'), str(entity.rng_device.source) if entity.rng_device else None) and
|
||||
equal(self.param('rng_bytes'), entity.rng_device.rate.bytes if entity.rng_device else None) and
|
||||
equal(self.param('rng_period'), entity.rng_device.rate.period if entity.rng_device else None) and
|
||||
equal(self.param('placement_policy'), str(entity.placement_policy.affinity) if entity.placement_policy else None)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(type='str', default='present',
|
||||
choices=['absent', 'present']),
|
||||
name=dict(type='str'),
|
||||
id=dict(type='str'),
|
||||
memory=dict(type='str'),
|
||||
memory_guaranteed=dict(type='str'),
|
||||
memory_max=dict(type='str'),
|
||||
cpu_sockets=dict(type='int'),
|
||||
cpu_cores=dict(type='int'),
|
||||
cpu_threads=dict(type='int'),
|
||||
operating_system=dict(type='str'),
|
||||
boot_devices=dict(type='list', choices=['cdrom', 'hd', 'network']),
|
||||
serial_console=dict(type='bool'),
|
||||
usb_support=dict(type='bool'),
|
||||
high_availability=dict(type='bool'),
|
||||
high_availability_priority=dict(type='int'),
|
||||
watchdog=dict(type='dict'),
|
||||
host=dict(type='str'),
|
||||
graphical_console=dict(type='dict'),
|
||||
description=dict(type='str'),
|
||||
cpu_mode=dict(type='str'),
|
||||
rng_device=dict(type='str'),
|
||||
rng_bytes=dict(type='int', default=None),
|
||||
rng_period=dict(type='int', default=None),
|
||||
placement_policy=dict(type='str'),
|
||||
cpu_pinning=dict(type='list'),
|
||||
soundcard_enabled=dict(type='bool', default=None),
|
||||
virtio_scsi=dict(type='bool', default=None),
|
||||
smartcard_enabled=dict(type='bool', default=None),
|
||||
io_threads=dict(type='int', default=None),
|
||||
nics=dict(type='list', default=[]),
|
||||
ballooning_enabled=dict(type='bool', default=None),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=[['id', 'name']],
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
|
||||
try:
|
||||
state = module.params['state']
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
its_service = connection.system_service().instance_types_service()
|
||||
its_module = InstanceTypeModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=its_service,
|
||||
)
|
||||
it = its_module.search_entity()
|
||||
|
||||
if state == 'present':
|
||||
ret = its_module.create(
|
||||
entity=it
|
||||
)
|
||||
its_module.post_present(ret['id'])
|
||||
ret['changed'] = its_module.changed
|
||||
elif state == 'absent':
|
||||
ret = its_module.remove()
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,236 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_job
|
||||
short_description: Module to manage jobs in oVirt/RHV
|
||||
version_added: "2.9"
|
||||
author: "Martin Necas (@mnecas)"
|
||||
description:
|
||||
- "This module manage jobs in oVirt/RHV. It can also manage steps of the job."
|
||||
options:
|
||||
description:
|
||||
description:
|
||||
- "Description of the job."
|
||||
- "When task with same description has already finished and you rerun taks it will create new job."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the job be C(present)/C(absent)/C(failed)."
|
||||
- "C(started) is alias for C(present). C(finished) is alias for C(absent). Same in the steps."
|
||||
- "Note when C(finished)/C(failed) it will finish/fail all steps."
|
||||
choices: ['present', 'absent', 'started', 'finished', 'failed']
|
||||
default: present
|
||||
steps:
|
||||
description:
|
||||
- "The steps of the job."
|
||||
suboptions:
|
||||
description:
|
||||
description:
|
||||
- "Description of the step."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the step be present/absent/failed."
|
||||
- "Note when one step fail whole job will fail"
|
||||
- "Note when all steps are finished it will finish job."
|
||||
choices: ['present', 'absent', 'started', 'finished', 'failed']
|
||||
default: present
|
||||
type: list
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
- name: Create job with two steps
|
||||
ovirt_job:
|
||||
description: job_name
|
||||
steps:
|
||||
- description: step_name_A
|
||||
- description: step_name_B
|
||||
|
||||
- name: Finish one step
|
||||
ovirt_job:
|
||||
description: job_name
|
||||
steps:
|
||||
- description: step_name_A
|
||||
state: finished
|
||||
|
||||
- name: When you fail one step whole job will stop
|
||||
ovirt_job:
|
||||
description: job_name
|
||||
steps:
|
||||
- description: step_name_B
|
||||
state: failed
|
||||
|
||||
- name: Finish all steps
|
||||
ovirt_job:
|
||||
description: job_name
|
||||
state: finished
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the job which is managed
|
||||
returned: On success if job is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
job:
|
||||
description: "Dictionary of all the job attributes. Job attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/job."
|
||||
returned: On success if job is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_id_by_name,
|
||||
ovirt_full_argument_spec,
|
||||
get_dict_of_struct,
|
||||
)
|
||||
|
||||
|
||||
def build_job(description):
|
||||
return otypes.Job(
|
||||
description=description,
|
||||
status=otypes.JobStatus.STARTED,
|
||||
external=True,
|
||||
auto_cleared=True
|
||||
)
|
||||
|
||||
|
||||
def build_step(description, job_id):
|
||||
return otypes.Step(
|
||||
description=description,
|
||||
type=otypes.StepEnum.UNKNOWN,
|
||||
job=otypes.Job(
|
||||
id=job_id
|
||||
),
|
||||
status=otypes.StepStatus.STARTED,
|
||||
external=True,
|
||||
)
|
||||
|
||||
|
||||
def attach_steps(module, job_id, jobs_service):
|
||||
changed = False
|
||||
steps_service = jobs_service.job_service(job_id).steps_service()
|
||||
if module.params.get('steps'):
|
||||
for step in module.params.get('steps'):
|
||||
step_entity = get_entity(steps_service, step.get('description'))
|
||||
step_state = step.get('state', 'present')
|
||||
if step_state in ['present', 'started']:
|
||||
if step_entity is None:
|
||||
steps_service.add(build_step(step.get('description'), job_id))
|
||||
changed = True
|
||||
if step_entity is not None and step_entity.status not in [otypes.StepStatus.FINISHED, otypes.StepStatus.FAILED]:
|
||||
if step_state in ['absent', 'finished']:
|
||||
steps_service.step_service(step_entity.id).end(succeeded=True)
|
||||
changed = True
|
||||
elif step_state == 'failed':
|
||||
steps_service.step_service(step_entity.id).end(succeeded=False)
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
|
||||
def get_entity(service, description):
|
||||
all_entities = service.list()
|
||||
for entity in all_entities:
|
||||
if entity.description == description and entity.status not in [otypes.StepStatus.FINISHED, otypes.JobStatus.FINISHED]:
|
||||
return entity
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent', 'started', 'finished', 'failed'],
|
||||
default='present',
|
||||
),
|
||||
description=dict(default=None),
|
||||
steps=dict(default=None, type='list'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=False,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
jobs_service = connection.system_service().jobs_service()
|
||||
|
||||
state = module.params['state']
|
||||
job = get_entity(jobs_service, module.params['description'])
|
||||
changed = False
|
||||
if state in ['present', 'started']:
|
||||
if job is None or job.status in [otypes.JobStatus.FINISHED, otypes.JobStatus.FAILED]:
|
||||
job = jobs_service.add(build_job(module.params['description']))
|
||||
changed = True
|
||||
changed = attach_steps(module, job.id, jobs_service) or changed
|
||||
|
||||
if job is not None and job.status not in [otypes.JobStatus.FINISHED, otypes.JobStatus.FAILED]:
|
||||
if state in ['absent', 'finished']:
|
||||
jobs_service.job_service(job.id).end(succeeded=True)
|
||||
changed = True
|
||||
|
||||
elif state == 'failed':
|
||||
jobs_service.job_service(job.id).end(succeeded=False)
|
||||
changed = True
|
||||
|
||||
ret = {
|
||||
'changed': changed,
|
||||
'id': getattr(job, 'id', None),
|
||||
'job': get_dict_of_struct(
|
||||
struct=job,
|
||||
connection=connection,
|
||||
fetch_nested=True,
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
),
|
||||
}
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,181 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_mac_pool
|
||||
short_description: Module to manage MAC pools in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "This module manage MAC pools in oVirt/RHV."
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the mac pool to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- "Name of the MAC pool to manage."
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- "Description of the MAC pool."
|
||||
state:
|
||||
description:
|
||||
- "Should the mac pool be present or absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
allow_duplicates:
|
||||
description:
|
||||
- "If I(true) allow a MAC address to be used multiple times in a pool."
|
||||
- "Default value is set by oVirt/RHV engine to I(false)."
|
||||
type: bool
|
||||
ranges:
|
||||
description:
|
||||
- "List of MAC ranges. The from and to should be split by comma."
|
||||
- "For example: 00:1a:4a:16:01:51,00:1a:4a:16:01:61"
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create MAC pool:
|
||||
- ovirt_mac_pool:
|
||||
name: mymacpool
|
||||
allow_duplicates: false
|
||||
ranges:
|
||||
- 00:1a:4a:16:01:51,00:1a:4a:16:01:61
|
||||
- 00:1a:4a:16:02:51,00:1a:4a:16:02:61
|
||||
|
||||
# Remove MAC pool:
|
||||
- ovirt_mac_pool:
|
||||
state: absent
|
||||
name: mymacpool
|
||||
|
||||
# Change MAC pool Name
|
||||
- ovirt_nic:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new_mac_pool_name"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the MAC pool which is managed
|
||||
returned: On success if MAC pool is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
template:
|
||||
description: "Dictionary of all the MAC pool attributes. MAC pool attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/mac_pool."
|
||||
returned: On success if MAC pool is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
equal,
|
||||
create_connection,
|
||||
ovirt_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
class MACPoolModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.MacPool(
|
||||
name=self._module.params['name'],
|
||||
id=self._module.params['id'],
|
||||
allow_duplicates=self._module.params['allow_duplicates'],
|
||||
description=self._module.params['description'],
|
||||
ranges=[
|
||||
otypes.Range(
|
||||
from_=mac_range.split(',')[0],
|
||||
to=mac_range.split(',')[1],
|
||||
)
|
||||
for mac_range in self._module.params['ranges']
|
||||
] if self._module.params['ranges'] else None,
|
||||
)
|
||||
|
||||
def _compare_ranges(self, entity):
|
||||
if self._module.params['ranges'] is not None:
|
||||
ranges = sorted([
|
||||
'%s,%s' % (mac_range.from_, mac_range.to)
|
||||
for mac_range in entity.ranges
|
||||
])
|
||||
return equal(sorted(self._module.params['ranges']), ranges)
|
||||
|
||||
return True
|
||||
|
||||
def update_check(self, entity):
|
||||
return (
|
||||
self._compare_ranges(entity) and
|
||||
equal(self._module.params['allow_duplicates'], entity.allow_duplicates) and
|
||||
equal(self._module.params['description'], entity.description) and
|
||||
equal(self._module.params['name'], entity.name)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(required=True),
|
||||
id=dict(default=None),
|
||||
allow_duplicates=dict(default=None, type='bool'),
|
||||
description=dict(default=None),
|
||||
ranges=dict(default=None, type='list'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
mac_pools_service = connection.system_service().mac_pools_service()
|
||||
mac_pools_module = MACPoolModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=mac_pools_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = mac_pools_module.create()
|
||||
elif state == 'absent':
|
||||
ret = mac_pools_module.remove()
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,360 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_network
|
||||
short_description: Module to manage logical networks in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage logical networks in oVirt/RHV"
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the network to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- "Name of the network to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the network be present or absent"
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
data_center:
|
||||
description:
|
||||
- "Datacenter name where network reside."
|
||||
description:
|
||||
description:
|
||||
- "Description of the network."
|
||||
comment:
|
||||
description:
|
||||
- "Comment of the network."
|
||||
vlan_tag:
|
||||
description:
|
||||
- "Specify VLAN tag."
|
||||
external_provider:
|
||||
description:
|
||||
- "Name of external network provider."
|
||||
- "At first it tries to import the network when not found it will create network in external provider."
|
||||
version_added: 2.8
|
||||
vm_network:
|
||||
description:
|
||||
- "If I(True) network will be marked as network for VM."
|
||||
- "VM network carries traffic relevant to the virtual machine."
|
||||
type: bool
|
||||
mtu:
|
||||
description:
|
||||
- "Maximum transmission unit (MTU) of the network."
|
||||
clusters:
|
||||
description:
|
||||
- "List of dictionaries describing how the network is managed in specific cluster."
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Cluster name.
|
||||
assigned:
|
||||
description:
|
||||
- I(true) if the network should be assigned to cluster. Default is I(true).
|
||||
type: bool
|
||||
required:
|
||||
description:
|
||||
- I(true) if the network must remain operational for all hosts associated with this network.
|
||||
type: bool
|
||||
display:
|
||||
description:
|
||||
- I(true) if the network should marked as display network.
|
||||
type: bool
|
||||
migration:
|
||||
description:
|
||||
- I(true) if the network should marked as migration network.
|
||||
type: bool
|
||||
gluster:
|
||||
description:
|
||||
- I(true) if the network should marked as gluster network.
|
||||
type: bool
|
||||
label:
|
||||
description:
|
||||
- "Name of the label to assign to the network."
|
||||
version_added: "2.5"
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create network
|
||||
- ovirt_network:
|
||||
data_center: mydatacenter
|
||||
name: mynetwork
|
||||
vlan_tag: 1
|
||||
vm_network: true
|
||||
|
||||
# Remove network
|
||||
- ovirt_network:
|
||||
state: absent
|
||||
name: mynetwork
|
||||
|
||||
# Change Network Name
|
||||
- ovirt_network:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new_network_name"
|
||||
data_center: mydatacenter
|
||||
|
||||
# Add network from external provider
|
||||
- ovirt_network:
|
||||
data_center: mydatacenter
|
||||
name: mynetwork
|
||||
external_provider: ovirt-provider-ovn
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: "ID of the managed network"
|
||||
returned: "On success if network is found."
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
network:
|
||||
description: "Dictionary of all the network attributes. Network attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/network."
|
||||
returned: "On success if network is found."
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
check_params,
|
||||
create_connection,
|
||||
equal,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
get_id_by_name,
|
||||
get_dict_of_struct,
|
||||
get_entity
|
||||
)
|
||||
|
||||
|
||||
class NetworksModule(BaseModule):
|
||||
def build_entity(self):
|
||||
if self.param('external_provider'):
|
||||
ons_service = self._connection.system_service().openstack_network_providers_service()
|
||||
on_service = ons_service.provider_service(get_id_by_name(ons_service, self.param('external_provider')))
|
||||
return otypes.Network(
|
||||
name=self._module.params['name'],
|
||||
comment=self._module.params['comment'],
|
||||
description=self._module.params['description'],
|
||||
id=self._module.params['id'],
|
||||
data_center=otypes.DataCenter(
|
||||
name=self._module.params['data_center'],
|
||||
) if self._module.params['data_center'] else None,
|
||||
vlan=otypes.Vlan(
|
||||
self._module.params['vlan_tag'],
|
||||
) if self._module.params['vlan_tag'] else None,
|
||||
usages=[
|
||||
otypes.NetworkUsage.VM if self._module.params['vm_network'] else None
|
||||
] if self._module.params['vm_network'] is not None else None,
|
||||
mtu=self._module.params['mtu'],
|
||||
external_provider=otypes.OpenStackNetworkProvider(id=on_service.get().id)
|
||||
if self.param('external_provider') else None,
|
||||
)
|
||||
|
||||
def post_create(self, entity):
|
||||
self._update_label_assignments(entity)
|
||||
|
||||
def _update_label_assignments(self, entity):
|
||||
if self.param('label') is None:
|
||||
return
|
||||
|
||||
labels_service = self._service.service(entity.id).network_labels_service()
|
||||
labels = [lbl.id for lbl in labels_service.list()]
|
||||
if not self.param('label') in labels:
|
||||
if not self._module.check_mode:
|
||||
if labels:
|
||||
labels_service.label_service(labels[0]).remove()
|
||||
labels_service.add(
|
||||
label=otypes.NetworkLabel(id=self.param('label'))
|
||||
)
|
||||
self.changed = True
|
||||
|
||||
def update_check(self, entity):
|
||||
self._update_label_assignments(entity)
|
||||
return (
|
||||
equal(self._module.params.get('comment'), entity.comment) and
|
||||
equal(self._module.params.get('name'), entity.name) and
|
||||
equal(self._module.params.get('description'), entity.description) and
|
||||
equal(self._module.params.get('vlan_tag'), getattr(entity.vlan, 'id', None)) and
|
||||
equal(self._module.params.get('vm_network'), True if entity.usages else False) and
|
||||
equal(self._module.params.get('mtu'), entity.mtu)
|
||||
)
|
||||
|
||||
|
||||
class ClusterNetworksModule(BaseModule):
|
||||
|
||||
def __init__(self, network_id, cluster_network, *args, **kwargs):
|
||||
super(ClusterNetworksModule, self).__init__(*args, **kwargs)
|
||||
self._network_id = network_id
|
||||
self._cluster_network = cluster_network
|
||||
self._old_usages = []
|
||||
self._cluster_network_entity = get_entity(self._service.network_service(network_id))
|
||||
if self._cluster_network_entity is not None:
|
||||
self._old_usages = self._cluster_network_entity.usages
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.Network(
|
||||
id=self._network_id,
|
||||
name=self._module.params['name'],
|
||||
required=self._cluster_network.get('required'),
|
||||
display=self._cluster_network.get('display'),
|
||||
usages=list(set([
|
||||
otypes.NetworkUsage(usage)
|
||||
for usage in ['display', 'gluster', 'migration']
|
||||
if self._cluster_network.get(usage, False)
|
||||
] + self._old_usages))
|
||||
if (
|
||||
self._cluster_network.get('display') is not None or
|
||||
self._cluster_network.get('gluster') is not None or
|
||||
self._cluster_network.get('migration') is not None
|
||||
) else None,
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
return (
|
||||
equal(self._cluster_network.get('required'), entity.required) and
|
||||
equal(self._cluster_network.get('display'), entity.display) and
|
||||
all(
|
||||
x in [
|
||||
str(usage)
|
||||
for usage in getattr(entity, 'usages', [])
|
||||
# VM + MANAGEMENT is part of root network
|
||||
if usage != otypes.NetworkUsage.VM and usage != otypes.NetworkUsage.MANAGEMENT
|
||||
]
|
||||
for x in [
|
||||
usage
|
||||
for usage in ['display', 'gluster', 'migration']
|
||||
if self._cluster_network.get(usage, False)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
data_center=dict(required=True),
|
||||
id=dict(default=None),
|
||||
name=dict(required=True),
|
||||
description=dict(default=None),
|
||||
comment=dict(default=None),
|
||||
external_provider=dict(default=None),
|
||||
vlan_tag=dict(default=None, type='int'),
|
||||
vm_network=dict(default=None, type='bool'),
|
||||
mtu=dict(default=None, type='int'),
|
||||
clusters=dict(default=None, type='list'),
|
||||
label=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
clusters_service = connection.system_service().clusters_service()
|
||||
networks_service = connection.system_service().networks_service()
|
||||
networks_module = NetworksModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=networks_service,
|
||||
)
|
||||
state = module.params['state']
|
||||
search_params = {
|
||||
'name': module.params['name'],
|
||||
'datacenter': module.params['data_center'],
|
||||
}
|
||||
if state == 'present':
|
||||
imported = False
|
||||
if module.params.get('external_provider') and module.params.get('name') not in [net.name for net in networks_service.list()]:
|
||||
# Try to import network
|
||||
ons_service = connection.system_service().openstack_network_providers_service()
|
||||
on_service = ons_service.provider_service(get_id_by_name(ons_service, module.params.get('external_provider')))
|
||||
on_networks_service = on_service.networks_service()
|
||||
if module.params.get('name') in [net.name for net in on_networks_service.list()]:
|
||||
network_service = on_networks_service.network_service(get_id_by_name(on_networks_service, module.params.get('name')))
|
||||
network_service.import_(data_center=otypes.DataCenter(name=module.params.get('data_center')))
|
||||
imported = True
|
||||
|
||||
ret = networks_module.create(search_params=search_params)
|
||||
ret['changed'] = ret['changed'] or imported
|
||||
# Update clusters networks:
|
||||
if module.params.get('clusters') is not None:
|
||||
for param_cluster in module.params.get('clusters'):
|
||||
cluster = search_by_name(clusters_service, param_cluster.get('name'))
|
||||
if cluster is None:
|
||||
raise Exception("Cluster '%s' was not found." % param_cluster.get('name'))
|
||||
cluster_networks_service = clusters_service.service(cluster.id).networks_service()
|
||||
cluster_networks_module = ClusterNetworksModule(
|
||||
network_id=ret['id'],
|
||||
cluster_network=param_cluster,
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=cluster_networks_service,
|
||||
)
|
||||
if param_cluster.get('assigned', True):
|
||||
ret = cluster_networks_module.create()
|
||||
else:
|
||||
ret = cluster_networks_module.remove()
|
||||
|
||||
elif state == 'absent':
|
||||
ret = networks_module.remove(search_params=search_params)
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,120 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_network_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV networks
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV networks."
|
||||
- This module was called C(ovirt_network_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_network_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_networks), which
|
||||
contains a list of networks. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search network starting with string vlan1 use: name=vlan1*"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all networks which names start with C(vlan1):
|
||||
- ovirt_network_info:
|
||||
pattern: name=vlan1*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_networks }}"
|
||||
'''
|
||||
|
||||
|
||||
RETURN = '''
|
||||
ovirt_networks:
|
||||
description: "List of dictionaries describing the networks. Network attributes are mapped to dictionary keys,
|
||||
all networks attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/network."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_network_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_network_facts' module has been renamed to 'ovirt_network_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
networks_service = connection.system_service().networks_service()
|
||||
networks = networks_service.list(search=module.params['pattern'])
|
||||
result = dict(
|
||||
ovirt_networks=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in networks
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,318 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_nic
|
||||
short_description: Module to manage network interfaces of Virtual Machines in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author:
|
||||
- Ondra Machacek (@machacekondra)
|
||||
description:
|
||||
- Module to manage network interfaces of Virtual Machines in oVirt/RHV.
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the nic to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- Name of the network interface to manage.
|
||||
required: true
|
||||
vm:
|
||||
description:
|
||||
- Name of the Virtual Machine to manage.
|
||||
- You must provide either C(vm) parameter or C(template) parameter.
|
||||
template:
|
||||
description:
|
||||
- Name of the template to manage.
|
||||
- You must provide either C(vm) parameter or C(template) parameter.
|
||||
version_added: "2.4"
|
||||
state:
|
||||
description:
|
||||
- Should the Virtual Machine NIC be present/absent/plugged/unplugged.
|
||||
choices: [ absent, plugged, present, unplugged ]
|
||||
default: present
|
||||
network:
|
||||
description:
|
||||
- Logical network to which the VM network interface should use,
|
||||
by default Empty network is used if network is not specified.
|
||||
profile:
|
||||
description:
|
||||
- Virtual network interface profile to be attached to VM network interface.
|
||||
- When not specified and network has only single profile it will be auto-selected, otherwise you must specify profile.
|
||||
interface:
|
||||
description:
|
||||
- "Type of the network interface. For example e1000, pci_passthrough, rtl8139, rtl8139_virtio, spapr_vlan or virtio."
|
||||
- "It's required parameter when creating the new NIC."
|
||||
mac_address:
|
||||
description:
|
||||
- Custom MAC address of the network interface, by default it's obtained from MAC pool.
|
||||
linked:
|
||||
description:
|
||||
- Defines if the NIC is linked to the virtual machine.
|
||||
type: bool
|
||||
version_added: "2.9"
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
- name: Add NIC to VM
|
||||
ovirt_nic:
|
||||
state: present
|
||||
vm: myvm
|
||||
name: mynic
|
||||
interface: e1000
|
||||
mac_address: 00:1a:4a:16:01:56
|
||||
profile: ovirtmgmt
|
||||
network: ovirtmgmt
|
||||
|
||||
- name: Plug NIC to VM
|
||||
ovirt_nic:
|
||||
state: plugged
|
||||
vm: myvm
|
||||
name: mynic
|
||||
|
||||
- name: Unplug NIC from VM
|
||||
ovirt_nic:
|
||||
state: unplugged
|
||||
linked: false
|
||||
vm: myvm
|
||||
name: mynic
|
||||
|
||||
- name: Add NIC to template
|
||||
ovirt_nic:
|
||||
auth: "{{ ovirt_auth }}"
|
||||
state: present
|
||||
template: my_template
|
||||
name: nic1
|
||||
interface: virtio
|
||||
profile: ovirtmgmt
|
||||
network: ovirtmgmt
|
||||
|
||||
- name: Remove NIC from VM
|
||||
ovirt_nic:
|
||||
state: absent
|
||||
vm: myvm
|
||||
name: mynic
|
||||
|
||||
# Change NIC Name
|
||||
- ovirt_nic:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new_nic_name"
|
||||
vm: myvm
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the network interface which is managed
|
||||
returned: On success if network interface is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
nic:
|
||||
description: "Dictionary of all the network interface attributes. Network interface attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/nic."
|
||||
returned: On success if network interface is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_link_name,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
class EntityNicsModule(BaseModule):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EntityNicsModule, self).__init__(*args, **kwargs)
|
||||
self.vnic_id = None
|
||||
|
||||
@property
|
||||
def vnic_id(self):
|
||||
return self._vnic_id
|
||||
|
||||
@vnic_id.setter
|
||||
def vnic_id(self, vnic_id):
|
||||
self._vnic_id = vnic_id
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.Nic(
|
||||
id=self._module.params.get('id'),
|
||||
name=self._module.params.get('name'),
|
||||
interface=otypes.NicInterface(
|
||||
self._module.params.get('interface')
|
||||
) if self._module.params.get('interface') else None,
|
||||
vnic_profile=otypes.VnicProfile(
|
||||
id=self.vnic_id,
|
||||
) if self.vnic_id else None,
|
||||
mac=otypes.Mac(
|
||||
address=self._module.params.get('mac_address')
|
||||
) if self._module.params.get('mac_address') else None,
|
||||
linked=self.param('linked') if self.param('linked') is not None else None,
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
if self._module.params.get('vm'):
|
||||
return (
|
||||
equal(self._module.params.get('interface'), str(entity.interface)) and
|
||||
equal(self._module.params.get('linked'), entity.linked) and
|
||||
equal(self._module.params.get('name'), str(entity.name)) and
|
||||
equal(self._module.params.get('profile'), get_link_name(self._connection, entity.vnic_profile)) and
|
||||
equal(self._module.params.get('mac_address'), entity.mac.address)
|
||||
)
|
||||
elif self._module.params.get('template'):
|
||||
return (
|
||||
equal(self._module.params.get('interface'), str(entity.interface)) and
|
||||
equal(self._module.params.get('linked'), entity.linked) and
|
||||
equal(self._module.params.get('name'), str(entity.name)) and
|
||||
equal(self._module.params.get('profile'), get_link_name(self._connection, entity.vnic_profile))
|
||||
)
|
||||
|
||||
|
||||
def get_vnics(networks_service, network, connection):
|
||||
resp = []
|
||||
vnic_services = connection.system_service().vnic_profiles_service()
|
||||
for vnic in vnic_services.list():
|
||||
if vnic.network.id == network.id:
|
||||
resp.append(vnic)
|
||||
return resp
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(type='str', default='present', choices=['absent', 'plugged', 'present', 'unplugged']),
|
||||
vm=dict(type='str'),
|
||||
id=dict(default=None),
|
||||
template=dict(type='str'),
|
||||
name=dict(type='str', required=True),
|
||||
interface=dict(type='str'),
|
||||
profile=dict(type='str'),
|
||||
network=dict(type='str'),
|
||||
mac_address=dict(type='str'),
|
||||
linked=dict(type='bool'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=[['vm', 'template']],
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
# Locate the service that manages the virtual machines and use it to
|
||||
# search for the NIC:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
entity_name = None
|
||||
|
||||
if module.params.get('vm'):
|
||||
# Locate the VM, where we will manage NICs:
|
||||
entity_name = module.params.get('vm')
|
||||
collection_service = connection.system_service().vms_service()
|
||||
elif module.params.get('template'):
|
||||
entity_name = module.params.get('template')
|
||||
collection_service = connection.system_service().templates_service()
|
||||
|
||||
# TODO: We have to modify the search_by_name function to accept raise_error=True/False,
|
||||
entity = search_by_name(collection_service, entity_name)
|
||||
if entity is None:
|
||||
raise Exception("Vm/Template '%s' was not found." % entity_name)
|
||||
|
||||
service = collection_service.service(entity.id)
|
||||
cluster_id = entity.cluster
|
||||
|
||||
nics_service = service.nics_service()
|
||||
entitynics_module = EntityNicsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=nics_service,
|
||||
)
|
||||
|
||||
# Find vNIC id of the network interface (if any):
|
||||
if module.params['network']:
|
||||
profile = module.params.get('profile')
|
||||
cluster_name = get_link_name(connection, cluster_id)
|
||||
dcs_service = connection.system_service().data_centers_service()
|
||||
dc = dcs_service.list(search='Clusters.name=%s' % cluster_name)[0]
|
||||
networks_service = dcs_service.service(dc.id).networks_service()
|
||||
network = next(
|
||||
(n for n in networks_service.list()
|
||||
if n.name == module.params['network']),
|
||||
None
|
||||
)
|
||||
if network is None:
|
||||
raise Exception(
|
||||
"Network '%s' was not found in datacenter '%s'." % (
|
||||
module.params['network'],
|
||||
dc.name
|
||||
)
|
||||
)
|
||||
if profile:
|
||||
for vnic in connection.system_service().vnic_profiles_service().list():
|
||||
if vnic.name == profile and vnic.network.id == network.id:
|
||||
entitynics_module.vnic_id = vnic.id
|
||||
else:
|
||||
# When not specified which vnic use ovirtmgmt/ovirtmgmt
|
||||
vnics = get_vnics(networks_service, network, connection)
|
||||
if len(vnics) == 1:
|
||||
entitynics_module.vnic_id = vnics[0].id
|
||||
else:
|
||||
raise Exception(
|
||||
"You didn't specify any vnic profile. "
|
||||
"Following vnic profiles are in system: '%s', please specify one of them" % ([vnic.name for vnic in vnics])
|
||||
)
|
||||
# Handle appropriate action:
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = entitynics_module.create()
|
||||
elif state == 'absent':
|
||||
ret = entitynics_module.remove()
|
||||
elif state == 'plugged':
|
||||
entitynics_module.create()
|
||||
ret = entitynics_module.action(
|
||||
action='activate',
|
||||
action_condition=lambda nic: not nic.plugged,
|
||||
wait_condition=lambda nic: nic.plugged,
|
||||
)
|
||||
elif state == 'unplugged':
|
||||
entitynics_module.create()
|
||||
ret = entitynics_module.action(
|
||||
action='deactivate',
|
||||
action_condition=lambda nic: nic.plugged,
|
||||
wait_condition=lambda nic: not nic.plugged,
|
||||
)
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,138 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_nic_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV virtual machine network interfaces
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV virtual machine network interfaces."
|
||||
- This module was called C(ovirt_nic_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_nic_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_nics), which
|
||||
contains a list of NICs. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
vm:
|
||||
description:
|
||||
- "Name of the VM where NIC is attached."
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- "Name of the NIC, can be used as glob expression."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all NICs which names start with C(eth) for VM named C(centos7):
|
||||
- ovirt_nic_info:
|
||||
vm: centos7
|
||||
name: eth*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_nics }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_nics:
|
||||
description: "List of dictionaries describing the network interfaces. NIC attributes are mapped to dictionary keys,
|
||||
all NICs attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/nic."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import fnmatch
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
vm=dict(required=True),
|
||||
name=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_nic_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_nic_facts' module has been renamed to 'ovirt_nic_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
vms_service = connection.system_service().vms_service()
|
||||
vm_name = module.params['vm']
|
||||
vm = search_by_name(vms_service, vm_name)
|
||||
if vm is None:
|
||||
raise Exception("VM '%s' was not found." % vm_name)
|
||||
|
||||
nics_service = vms_service.service(vm.id).nics_service()
|
||||
if module.params['name']:
|
||||
nics = [
|
||||
e for e in nics_service.list()
|
||||
if fnmatch.fnmatch(e.name, module.params['name'])
|
||||
]
|
||||
else:
|
||||
nics = nics_service.list()
|
||||
|
||||
result = dict(
|
||||
ovirt_nics=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in nics
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,320 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_permission
|
||||
short_description: Module to manage permissions of users/groups in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author:
|
||||
- Ondra Machacek (@machacekondra)
|
||||
description:
|
||||
- Module to manage permissions of users/groups in oVirt/RHV.
|
||||
options:
|
||||
role:
|
||||
description:
|
||||
- Name of the role to be assigned to user/group on specific object.
|
||||
default: UserRole
|
||||
state:
|
||||
description:
|
||||
- Should the permission be present/absent.
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
object_id:
|
||||
description:
|
||||
- ID of the object where the permissions should be managed.
|
||||
object_name:
|
||||
description:
|
||||
- Name of the object where the permissions should be managed.
|
||||
object_type:
|
||||
description:
|
||||
- The object where the permissions should be managed.
|
||||
choices:
|
||||
- cluster
|
||||
- cpu_profile
|
||||
- data_center
|
||||
- disk
|
||||
- disk_profile
|
||||
- host
|
||||
- network
|
||||
- storage_domain
|
||||
- system
|
||||
- template
|
||||
- vm
|
||||
- vm_pool
|
||||
- vnic_profile
|
||||
default: vm
|
||||
user_name:
|
||||
description:
|
||||
- Username of the user to manage. In most LDAPs it's I(uid) of the user,
|
||||
but in Active Directory you must specify I(UPN) of the user.
|
||||
- Note that if user does not exist in the system this module will fail,
|
||||
you should ensure the user exists by using M(ovirt_users) module.
|
||||
group_name:
|
||||
description:
|
||||
- Name of the group to manage.
|
||||
- Note that if group does not exist in the system this module will fail,
|
||||
you should ensure the group exists by using M(ovirt_groups) module.
|
||||
authz_name:
|
||||
description:
|
||||
- Authorization provider of the user/group.
|
||||
required: true
|
||||
aliases: [ domain ]
|
||||
namespace:
|
||||
description:
|
||||
- Namespace of the authorization provider, where user/group resides.
|
||||
quota_name:
|
||||
description:
|
||||
- Name of the quota to assign permission. Works only with C(object_type) I(data_center).
|
||||
version_added: "2.7"
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
- name: Add user user1 from authorization provider example.com-authz
|
||||
ovirt_permission:
|
||||
user_name: user1
|
||||
authz_name: example.com-authz
|
||||
object_type: vm
|
||||
object_name: myvm
|
||||
role: UserVmManager
|
||||
|
||||
- name: Remove permission from user
|
||||
ovirt_permission:
|
||||
state: absent
|
||||
user_name: user1
|
||||
authz_name: example.com-authz
|
||||
object_type: cluster
|
||||
object_name: mycluster
|
||||
role: ClusterAdmin
|
||||
|
||||
- name: Assign QuotaConsumer role to user
|
||||
ovirt_permissions:
|
||||
state: present
|
||||
user_name: user1
|
||||
authz_name: example.com-authz
|
||||
object_type: data_center
|
||||
object_name: mydatacenter
|
||||
quota_name: myquota
|
||||
role: QuotaConsumer
|
||||
|
||||
- name: Assign QuotaConsumer role to group
|
||||
ovirt_permissions:
|
||||
state: present
|
||||
group_name: group1
|
||||
authz_name: example.com-authz
|
||||
object_type: data_center
|
||||
object_name: mydatacenter
|
||||
quota_name: myquota
|
||||
role: QuotaConsumer
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the permission which is managed
|
||||
returned: On success if permission is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
permission:
|
||||
description: "Dictionary of all the permission attributes. Permission attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/permission."
|
||||
returned: On success if permission is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
follow_link,
|
||||
get_link_name,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_attributes,
|
||||
search_by_name,
|
||||
get_id_by_name
|
||||
)
|
||||
|
||||
|
||||
def _objects_service(connection, object_type):
|
||||
if object_type == 'system':
|
||||
return connection.system_service()
|
||||
|
||||
return getattr(
|
||||
connection.system_service(),
|
||||
'%ss_service' % object_type,
|
||||
None,
|
||||
)()
|
||||
|
||||
|
||||
def _object_service(connection, module):
|
||||
object_type = module.params['object_type']
|
||||
objects_service = _objects_service(connection, object_type)
|
||||
if object_type == 'system':
|
||||
return objects_service
|
||||
|
||||
object_id = module.params['object_id']
|
||||
if object_id is None:
|
||||
sdk_object = search_by_name(objects_service, module.params['object_name'])
|
||||
if sdk_object is None:
|
||||
raise Exception(
|
||||
"'%s' object '%s' was not found." % (
|
||||
module.params['object_type'],
|
||||
module.params['object_name']
|
||||
)
|
||||
)
|
||||
object_id = sdk_object.id
|
||||
|
||||
object_service = objects_service.service(object_id)
|
||||
if module.params['quota_name'] and object_type == 'data_center':
|
||||
quotas_service = object_service.quotas_service()
|
||||
return quotas_service.quota_service(get_id_by_name(quotas_service, module.params['quota_name']))
|
||||
return object_service
|
||||
|
||||
|
||||
def _permission(module, permissions_service, connection):
|
||||
for permission in permissions_service.list():
|
||||
user = follow_link(connection, permission.user)
|
||||
if (
|
||||
equal(module.params['user_name'], user.principal if user else None) and
|
||||
equal(module.params['group_name'], get_link_name(connection, permission.group)) and
|
||||
equal(module.params['role'], get_link_name(connection, permission.role))
|
||||
):
|
||||
return permission
|
||||
|
||||
|
||||
class PermissionsModule(BaseModule):
|
||||
|
||||
def _user(self):
|
||||
user = search_by_attributes(
|
||||
self._connection.system_service().users_service(),
|
||||
usrname="{name}@{authz_name}".format(
|
||||
name=self._module.params['user_name'],
|
||||
authz_name=self._module.params['authz_name'],
|
||||
),
|
||||
)
|
||||
if user is None:
|
||||
raise Exception("User '%s' was not found." % self._module.params['user_name'])
|
||||
return user
|
||||
|
||||
def _group(self):
|
||||
groups = self._connection.system_service().groups_service().list(
|
||||
search="name={name}".format(
|
||||
name=self._module.params['group_name'],
|
||||
)
|
||||
)
|
||||
|
||||
# If found more groups, filter them by namespace and authz name:
|
||||
# (filtering here, as oVirt/RHV backend doesn't support it)
|
||||
if len(groups) > 1:
|
||||
groups = [
|
||||
g for g in groups if (
|
||||
equal(self._module.params['namespace'], g.namespace) and
|
||||
equal(self._module.params['authz_name'], g.domain.name)
|
||||
)
|
||||
]
|
||||
if not groups:
|
||||
raise Exception("Group '%s' was not found." % self._module.params['group_name'])
|
||||
return groups[0]
|
||||
|
||||
def build_entity(self):
|
||||
entity = self._group() if self._module.params['group_name'] else self._user()
|
||||
|
||||
return otypes.Permission(
|
||||
user=otypes.User(
|
||||
id=entity.id
|
||||
) if self._module.params['user_name'] else None,
|
||||
group=otypes.Group(
|
||||
id=entity.id
|
||||
) if self._module.params['group_name'] else None,
|
||||
role=otypes.Role(
|
||||
name=self._module.params['role']
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
role=dict(type='str', default='UserRole'),
|
||||
object_type=dict(type='str', default='vm',
|
||||
choices=[
|
||||
'cluster',
|
||||
'cpu_profile',
|
||||
'data_center',
|
||||
'disk',
|
||||
'disk_profile',
|
||||
'host',
|
||||
'network',
|
||||
'storage_domain',
|
||||
'system',
|
||||
'template',
|
||||
'vm',
|
||||
'vm_pool',
|
||||
'vnic_profile',
|
||||
]),
|
||||
authz_name=dict(type='str', required=True, aliases=['domain']),
|
||||
object_id=dict(type='str'),
|
||||
object_name=dict(type='str'),
|
||||
user_name=dict(type='str'),
|
||||
group_name=dict(type='str'),
|
||||
namespace=dict(type='str'),
|
||||
quota_name=dict(type='str'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
if (module.params['object_name'] is None and module.params['object_id'] is None) and module.params['object_type'] != 'system':
|
||||
module.fail_json(msg='"object_name" or "object_id" is required')
|
||||
|
||||
if module.params['user_name'] is None and module.params['group_name'] is None:
|
||||
module.fail_json(msg='"user_name" or "group_name" is required')
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
permissions_service = _object_service(connection, module).permissions_service()
|
||||
permissions_module = PermissionsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=permissions_service,
|
||||
)
|
||||
|
||||
permission = _permission(module, permissions_service, connection)
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = permissions_module.create(entity=permission)
|
||||
elif state == 'absent':
|
||||
ret = permissions_module.remove(entity=permission)
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,161 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_permission_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV permissions
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV permissions."
|
||||
- This module was called C(ovirt_permission_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_permission_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_permissions), which
|
||||
contains a list of permissions. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
user_name:
|
||||
description:
|
||||
- "Username of the user to manage. In most LDAPs it's I(uid) of the user, but in Active Directory you must specify I(UPN) of the user."
|
||||
group_name:
|
||||
description:
|
||||
- "Name of the group to manage."
|
||||
authz_name:
|
||||
description:
|
||||
- "Authorization provider of the user/group. In previous versions of oVirt/RHV known as domain."
|
||||
required: true
|
||||
aliases: ['domain']
|
||||
namespace:
|
||||
description:
|
||||
- "Namespace of the authorization provider, where user/group resides."
|
||||
required: false
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all permissions of user with username C(john):
|
||||
- ovirt_permission_info:
|
||||
user_name: john
|
||||
authz_name: example.com-authz
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_permissions }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_permissions:
|
||||
description: "List of dictionaries describing the permissions. Permission attributes are mapped to dictionary keys,
|
||||
all permissions attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/permission."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4 as sdk
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_link_name,
|
||||
ovirt_info_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
def _permissions_service(connection, module):
|
||||
if module.params['user_name']:
|
||||
service = connection.system_service().users_service()
|
||||
entity = next(
|
||||
iter(
|
||||
service.list(
|
||||
search='usrname={0}'.format(
|
||||
'{0}@{1}'.format(module.params['user_name'], module.params['authz_name'])
|
||||
)
|
||||
)
|
||||
),
|
||||
None
|
||||
)
|
||||
else:
|
||||
service = connection.system_service().groups_service()
|
||||
entity = search_by_name(service, module.params['group_name'])
|
||||
|
||||
if entity is None:
|
||||
raise Exception("User/Group wasn't found.")
|
||||
|
||||
return service.service(entity.id).permissions_service()
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
authz_name=dict(required=True, aliases=['domain']),
|
||||
user_name=dict(default=None),
|
||||
group_name=dict(default=None),
|
||||
namespace=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_permission_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_permission_facts' module has been renamed to 'ovirt_permission_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
permissions_service = _permissions_service(connection, module)
|
||||
permissions = []
|
||||
for p in permissions_service.list():
|
||||
newperm = dict()
|
||||
for key, value in p.__dict__.items():
|
||||
if value and isinstance(value, sdk.Struct):
|
||||
newperm[key[1:]] = get_link_name(connection, value)
|
||||
newperm['%s_id' % key[1:]] = value.id
|
||||
permissions.append(newperm)
|
||||
|
||||
result = dict(ovirt_permissions=permissions)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,319 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_quota
|
||||
short_description: Module to manage datacenter quotas in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage datacenter quotas in oVirt/RHV"
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the quota to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- "Name of the quota to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the quota be present/absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
data_center:
|
||||
description:
|
||||
- "Name of the datacenter where quota should be managed."
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- "Description of the quota to manage."
|
||||
cluster_threshold:
|
||||
description:
|
||||
- "Cluster threshold(soft limit) defined in percentage (0-100)."
|
||||
aliases:
|
||||
- "cluster_soft_limit"
|
||||
cluster_grace:
|
||||
description:
|
||||
- "Cluster grace(hard limit) defined in percentage (1-100)."
|
||||
aliases:
|
||||
- "cluster_hard_limit"
|
||||
storage_threshold:
|
||||
description:
|
||||
- "Storage threshold(soft limit) defined in percentage (0-100)."
|
||||
aliases:
|
||||
- "storage_soft_limit"
|
||||
storage_grace:
|
||||
description:
|
||||
- "Storage grace(hard limit) defined in percentage (1-100)."
|
||||
aliases:
|
||||
- "storage_hard_limit"
|
||||
clusters:
|
||||
description:
|
||||
- "List of dictionary of cluster limits, which is valid to specific cluster."
|
||||
- "If cluster isn't specified it's valid to all clusters in system:"
|
||||
suboptions:
|
||||
cluster:
|
||||
description:
|
||||
- Name of the cluster.
|
||||
memory:
|
||||
description:
|
||||
- Memory limit (in GiB).
|
||||
cpu:
|
||||
description:
|
||||
- CPU limit.
|
||||
storages:
|
||||
description:
|
||||
- "List of dictionary of storage limits, which is valid to specific storage."
|
||||
- "If storage isn't specified it's valid to all storages in system:"
|
||||
suboptions:
|
||||
storage:
|
||||
description:
|
||||
- Name of the storage.
|
||||
size:
|
||||
description:
|
||||
- Size limit (in GiB).
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Add cluster quota to cluster cluster1 with memory limit 20GiB and CPU limit to 10:
|
||||
- ovirt_quota:
|
||||
name: quota1
|
||||
data_center: dcX
|
||||
clusters:
|
||||
- name: cluster1
|
||||
memory: 20
|
||||
cpu: 10
|
||||
|
||||
# Add cluster quota to all clusters with memory limit 30GiB and CPU limit to 15:
|
||||
- ovirt_quota:
|
||||
name: quota2
|
||||
data_center: dcX
|
||||
clusters:
|
||||
- memory: 30
|
||||
cpu: 15
|
||||
|
||||
# Add storage quota to storage data1 with size limit to 100GiB
|
||||
- ovirt_quota:
|
||||
name: quota3
|
||||
data_center: dcX
|
||||
storage_grace: 40
|
||||
storage_threshold: 60
|
||||
storages:
|
||||
- name: data1
|
||||
size: 100
|
||||
|
||||
# Remove quota quota1 (Note the quota must not be assigned to any VM/disk):
|
||||
- ovirt_quota:
|
||||
state: absent
|
||||
data_center: dcX
|
||||
name: quota1
|
||||
|
||||
# Change Quota Name
|
||||
- ovirt_quota:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new_quota_name"
|
||||
data_center: dcX
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the quota which is managed
|
||||
returned: On success if quota is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
quota:
|
||||
description: "Dictionary of all the quota attributes. Quota attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/quota."
|
||||
returned: On success if quota is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_link_name,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
class QuotasModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.Quota(
|
||||
description=self._module.params['description'],
|
||||
name=self._module.params['name'],
|
||||
id=self._module.params['id'],
|
||||
storage_hard_limit_pct=self._module.params.get('storage_grace'),
|
||||
storage_soft_limit_pct=self._module.params.get('storage_threshold'),
|
||||
cluster_hard_limit_pct=self._module.params.get('cluster_grace'),
|
||||
cluster_soft_limit_pct=self._module.params.get('cluster_threshold'),
|
||||
)
|
||||
|
||||
def update_storage_limits(self, entity):
|
||||
new_limits = {}
|
||||
for storage in self._module.params.get('storages'):
|
||||
new_limits[storage.get('name', '')] = {
|
||||
'size': storage.get('size'),
|
||||
}
|
||||
|
||||
old_limits = {}
|
||||
sd_limit_service = self._service.service(entity.id).quota_storage_limits_service()
|
||||
for limit in sd_limit_service.list():
|
||||
storage = get_link_name(self._connection, limit.storage_domain) if limit.storage_domain else ''
|
||||
old_limits[storage] = {
|
||||
'size': limit.limit,
|
||||
}
|
||||
sd_limit_service.service(limit.id).remove()
|
||||
|
||||
return new_limits == old_limits
|
||||
|
||||
def update_cluster_limits(self, entity):
|
||||
new_limits = {}
|
||||
for cluster in self._module.params.get('clusters'):
|
||||
new_limits[cluster.get('name', '')] = {
|
||||
'cpu': cluster.get('cpu'),
|
||||
'memory': float(cluster.get('memory')),
|
||||
}
|
||||
|
||||
old_limits = {}
|
||||
cl_limit_service = self._service.service(entity.id).quota_cluster_limits_service()
|
||||
for limit in cl_limit_service.list():
|
||||
cluster = get_link_name(self._connection, limit.cluster) if limit.cluster else ''
|
||||
old_limits[cluster] = {
|
||||
'cpu': limit.vcpu_limit,
|
||||
'memory': limit.memory_limit,
|
||||
}
|
||||
cl_limit_service.service(limit.id).remove()
|
||||
|
||||
return new_limits == old_limits
|
||||
|
||||
def update_check(self, entity):
|
||||
# -- FIXME --
|
||||
# Note that we here always remove all cluster/storage limits, because
|
||||
# it's not currently possible to update them and then re-create the limits
|
||||
# appropriately, this shouldn't have any side-effects, but it's not considered
|
||||
# as a correct approach.
|
||||
# This feature is tracked here: https://bugzilla.redhat.com/show_bug.cgi?id=1398576
|
||||
#
|
||||
|
||||
return (
|
||||
self.update_storage_limits(entity) and
|
||||
self.update_cluster_limits(entity) and
|
||||
equal(self._module.params.get('name'), entity.name) and
|
||||
equal(self._module.params.get('description'), entity.description) and
|
||||
equal(self._module.params.get('storage_grace'), entity.storage_hard_limit_pct) and
|
||||
equal(self._module.params.get('storage_threshold'), entity.storage_soft_limit_pct) and
|
||||
equal(self._module.params.get('cluster_grace'), entity.cluster_hard_limit_pct) and
|
||||
equal(self._module.params.get('cluster_threshold'), entity.cluster_soft_limit_pct)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
id=dict(default=None),
|
||||
name=dict(required=True),
|
||||
data_center=dict(required=True),
|
||||
description=dict(default=None),
|
||||
cluster_threshold=dict(default=None, type='int', aliases=['cluster_soft_limit']),
|
||||
cluster_grace=dict(default=None, type='int', aliases=['cluster_hard_limit']),
|
||||
storage_threshold=dict(default=None, type='int', aliases=['storage_soft_limit']),
|
||||
storage_grace=dict(default=None, type='int', aliases=['storage_hard_limit']),
|
||||
clusters=dict(default=[], type='list'),
|
||||
storages=dict(default=[], type='list'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
datacenters_service = connection.system_service().data_centers_service()
|
||||
dc_name = module.params['data_center']
|
||||
dc_id = getattr(search_by_name(datacenters_service, dc_name), 'id', None)
|
||||
if dc_id is None:
|
||||
raise Exception("Datacenter '%s' was not found." % dc_name)
|
||||
|
||||
quotas_service = datacenters_service.service(dc_id).quotas_service()
|
||||
quotas_module = QuotasModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=quotas_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = quotas_module.create()
|
||||
|
||||
# Manage cluster limits:
|
||||
cl_limit_service = quotas_service.service(ret['id']).quota_cluster_limits_service()
|
||||
for cluster in module.params.get('clusters'):
|
||||
cl_limit_service.add(
|
||||
limit=otypes.QuotaClusterLimit(
|
||||
memory_limit=float(cluster.get('memory')),
|
||||
vcpu_limit=cluster.get('cpu'),
|
||||
cluster=search_by_name(
|
||||
connection.system_service().clusters_service(),
|
||||
cluster.get('name')
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
# Manage storage limits:
|
||||
sd_limit_service = quotas_service.service(ret['id']).quota_storage_limits_service()
|
||||
for storage in module.params.get('storages'):
|
||||
sd_limit_service.add(
|
||||
limit=otypes.QuotaStorageLimit(
|
||||
limit=storage.get('size'),
|
||||
storage_domain=search_by_name(
|
||||
connection.system_service().storage_domains_service(),
|
||||
storage.get('name')
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
elif state == 'absent':
|
||||
ret = quotas_module.remove()
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,138 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_quota_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV quotas
|
||||
version_added: "2.3"
|
||||
author: "Maor Lipchuk (@machacekondra)"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV quotas."
|
||||
- This module was called C(ovirt_quota_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_quota_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_quotas), which
|
||||
contains a list of quotas. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
data_center:
|
||||
description:
|
||||
- "Name of the datacenter where quota resides."
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- "Name of the quota, can be used as glob expression."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about quota named C<myquota> in Default datacenter:
|
||||
- ovirt_quota_info:
|
||||
data_center: Default
|
||||
name: myquota
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_quotas }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_quotas:
|
||||
description: "List of dictionaries describing the quotas. Quota attributes are mapped to dictionary keys,
|
||||
all quotas attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/quota."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import fnmatch
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
data_center=dict(required=True),
|
||||
name=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_quota_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_quota_facts' module has been renamed to 'ovirt_quota_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
datacenters_service = connection.system_service().data_centers_service()
|
||||
dc_name = module.params['data_center']
|
||||
dc = search_by_name(datacenters_service, dc_name)
|
||||
if dc is None:
|
||||
raise Exception("Datacenter '%s' was not found." % dc_name)
|
||||
|
||||
quotas_service = datacenters_service.service(dc.id).quotas_service()
|
||||
if module.params['name']:
|
||||
quotas = [
|
||||
e for e in quotas_service.list()
|
||||
if fnmatch.fnmatch(e.name, module.params['name'])
|
||||
]
|
||||
else:
|
||||
quotas = quotas_service.list()
|
||||
|
||||
result = dict(
|
||||
ovirt_quotas=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in quotas
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,190 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_role
|
||||
short_description: Module to manage roles in oVirt/RHV
|
||||
version_added: "2.8"
|
||||
author: "Martin Necas (@mnecas)"
|
||||
description:
|
||||
- "Module to manage roles in oVirt/RHV."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the role to manage."
|
||||
id:
|
||||
description:
|
||||
- "ID of the role to manage."
|
||||
description:
|
||||
description:
|
||||
- "Description of the role."
|
||||
state:
|
||||
description:
|
||||
- "Should the role be present/absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
administrative:
|
||||
description:
|
||||
- "Defines the role as administrative-only or not."
|
||||
type: bool
|
||||
permits:
|
||||
description:
|
||||
- "List of permits which role will have"
|
||||
- "Permit 'login' is default and all roles will have it."
|
||||
- "List can contain name of permit."
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create administrative role with two permits
|
||||
- ovirt_role:
|
||||
name: role
|
||||
administrative: true
|
||||
permits:
|
||||
- manipulate_permissions
|
||||
- create_instance
|
||||
|
||||
# Remove role
|
||||
- ovirt_role:
|
||||
name: role
|
||||
state: absent
|
||||
|
||||
# Remove all permit
|
||||
- ovirt_role:
|
||||
name: role
|
||||
administrative: ture
|
||||
permits:
|
||||
- login
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_role:
|
||||
description: "List of dictionaries describing the Roles. Role attributes are mapped to dictionary keys,
|
||||
all Roles attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/role."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
convert_to_bytes,
|
||||
create_connection,
|
||||
equal,
|
||||
get_dict_of_struct,
|
||||
get_link_name,
|
||||
get_id_by_name,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_attributes,
|
||||
search_by_name,
|
||||
)
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class RoleModule(BaseModule):
|
||||
def build_entity(self):
|
||||
if 'login' not in self.param('permits'):
|
||||
self.param('permits').append('login')
|
||||
all_permits = self.get_all_permits()
|
||||
return otypes.Role(
|
||||
id=self.param('id'),
|
||||
name=self.param('name'),
|
||||
administrative=self.param('administrative') if self.param(
|
||||
'administrative') else None,
|
||||
permits=[
|
||||
otypes.Permit(id=all_permits.get(new_permit)) for new_permit in self.param('permits')
|
||||
] if self.param('permits') else None,
|
||||
description=self.param('description') if self.param('administrative') else None,
|
||||
)
|
||||
|
||||
def get_all_permits(self):
|
||||
return dict((permit.name, permit.id) for permit in self._connection.system_service().cluster_levels_service().level_service('4.3').get().permits)
|
||||
|
||||
def update_check(self, entity):
|
||||
def check_permits():
|
||||
if self.param('permits'):
|
||||
if 'login' not in self.param('permits'):
|
||||
self.param('permits').append('login')
|
||||
permits_service = self._service.service(entity.id).permits_service()
|
||||
current = [er.name for er in permits_service.list()]
|
||||
passed = [pr for pr in self.param('permits')]
|
||||
if not sorted(current) == sorted(passed):
|
||||
if self._module.check_mode:
|
||||
return False
|
||||
# remove all
|
||||
for permit in permits_service.list():
|
||||
permits_service.permit_service(permit.id).remove()
|
||||
# add passed permits
|
||||
all_permits = self.get_all_permits()
|
||||
for new_permit in passed:
|
||||
permits_service.add(otypes.Permit(id=all_permits.get(new_permit)))
|
||||
return False
|
||||
return True
|
||||
|
||||
return (
|
||||
check_permits() and
|
||||
equal(self.param('administrative'), entity.administrative) and
|
||||
equal(self.param('description'), entity.description)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
id=dict(default=None),
|
||||
name=dict(default=None),
|
||||
description=dict(default=None),
|
||||
administrative=dict(type='bool', default=False),
|
||||
permits=dict(type='list', default=[]),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=[['id', 'name']],
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
roles_service = connection.system_service().roles_service()
|
||||
roles_module = RoleModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=roles_service,
|
||||
)
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = roles_module.create()
|
||||
elif state == 'absent':
|
||||
ret = roles_module.remove()
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,137 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2017 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_scheduling_policy_info
|
||||
short_description: Retrieve information about one or more oVirt scheduling policies
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.4"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt scheduling policies."
|
||||
- This module was called C(ovirt_scheduling_policy_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_scheduling_policy_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_scheduling_policies),
|
||||
which contains a list of scheduling policies. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the scheduling policy."
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- "Name of the scheduling policy, can be used as glob expression."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all scheduling policies with name InClusterUpgrade:
|
||||
- ovirt_scheduling_policy_info:
|
||||
name: InClusterUpgrade
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_scheduling_policies }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_scheduling_policies:
|
||||
description: "List of dictionaries describing the scheduling policies.
|
||||
Scheduling policies attributes are mapped to dictionary keys,
|
||||
all scheduling policies attributes can be found at following
|
||||
url: https://ovirt.example.com/ovirt-engine/api/model#types/scheduling_policy."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import fnmatch
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
id=dict(default=None),
|
||||
name=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_scheduling_policy_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_scheduling_policy_facts' module has been renamed to 'ovirt_scheduling_policy_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
system_service = connection.system_service()
|
||||
sched_policies_service = system_service.scheduling_policies_service()
|
||||
if module.params['name']:
|
||||
sched_policies = [
|
||||
e for e in sched_policies_service.list()
|
||||
if fnmatch.fnmatch(e.name, module.params['name'])
|
||||
]
|
||||
elif module.params['id']:
|
||||
sched_policies = [
|
||||
sched_policies_service.service(module.params['id']).get()
|
||||
]
|
||||
else:
|
||||
sched_policies = sched_policies_service.list()
|
||||
|
||||
result = dict(
|
||||
ovirt_scheduling_policies=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in sched_policies
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,551 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_snapshot
|
||||
short_description: "Module to manage Virtual Machine Snapshots in oVirt/RHV"
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage Virtual Machine Snapshots in oVirt/RHV"
|
||||
options:
|
||||
snapshot_id:
|
||||
description:
|
||||
- "ID of the snapshot to manage."
|
||||
vm_name:
|
||||
description:
|
||||
- "Name of the Virtual Machine to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the Virtual Machine snapshot be restore/present/absent."
|
||||
choices: ['restore', 'present', 'absent']
|
||||
default: present
|
||||
description:
|
||||
description:
|
||||
- "Description of the snapshot."
|
||||
disk_id:
|
||||
description:
|
||||
- "Disk id which you want to upload or download"
|
||||
- "To get disk, you need to define disk_id or disk_name"
|
||||
version_added: "2.8"
|
||||
disk_name:
|
||||
description:
|
||||
- "Disk name which you want to upload or download"
|
||||
version_added: "2.8"
|
||||
download_image_path:
|
||||
description:
|
||||
- "Path on a file system where snapshot should be downloaded."
|
||||
- "Note that you must have an valid oVirt/RHV engine CA in your system trust store
|
||||
or you must provide it in C(ca_file) parameter."
|
||||
- "Note that the snapshot is not downloaded when the file already exists,
|
||||
but you can forcibly download the snapshot when using C(force) I (true)."
|
||||
version_added: "2.8"
|
||||
upload_image_path:
|
||||
description:
|
||||
- "Path to disk image, which should be uploaded."
|
||||
version_added: "2.8"
|
||||
use_memory:
|
||||
description:
|
||||
- "If I(true) and C(state) is I(present) save memory of the Virtual
|
||||
Machine if it's running."
|
||||
- "If I(true) and C(state) is I(restore) restore memory of the
|
||||
Virtual Machine."
|
||||
- "Note that Virtual Machine will be paused while saving the memory."
|
||||
aliases:
|
||||
- "restore_memory"
|
||||
- "save_memory"
|
||||
type: bool
|
||||
keep_days_old:
|
||||
description:
|
||||
- "Number of days after which should snapshot be deleted."
|
||||
- "It will check all snapshots of virtual machine and delete them, if they are older."
|
||||
version_added: "2.8"
|
||||
disks:
|
||||
description:
|
||||
- "List of disks which should be created with snapshot."
|
||||
suboptions:
|
||||
id:
|
||||
description:
|
||||
- "Id of the disk which should will be created."
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- "Name of the disk which should will be created."
|
||||
type: str
|
||||
type: list
|
||||
version_added: "2.10"
|
||||
notes:
|
||||
- "Note that without a guest agent the data on the created snapshot may be
|
||||
inconsistent."
|
||||
- "Deleting a snapshot does not remove any information from the virtual
|
||||
machine - it simply removes a return-point. However, restoring a virtual
|
||||
machine from a snapshot deletes any content that was written to the
|
||||
virtual machine after the time the snapshot was taken."
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create snapshot:
|
||||
- ovirt_snapshot:
|
||||
vm_name: rhel7
|
||||
description: MySnapshot
|
||||
register: snapshot
|
||||
|
||||
# Create snapshot and save memory:
|
||||
- ovirt_snapshot:
|
||||
vm_name: rhel7
|
||||
description: SnapWithMem
|
||||
use_memory: true
|
||||
register: snapshot
|
||||
|
||||
# Restore snapshot:
|
||||
- ovirt_snapshot:
|
||||
state: restore
|
||||
vm_name: rhel7
|
||||
snapshot_id: "{{ snapshot.id }}"
|
||||
|
||||
# Remove snapshot:
|
||||
- ovirt_snapshot:
|
||||
state: absent
|
||||
vm_name: rhel7
|
||||
snapshot_id: "{{ snapshot.id }}"
|
||||
|
||||
# Upload local image to disk and attach it to vm:
|
||||
# Since Ansible 2.8
|
||||
- ovirt_snapshot:
|
||||
name: mydisk
|
||||
vm_name: myvm
|
||||
upload_image_path: /path/to/mydisk.qcow2
|
||||
|
||||
# Download snapshot to local file system:
|
||||
# Since Ansible 2.8
|
||||
- ovirt_snapshot:
|
||||
snapshot_id: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
disk_name: DiskName
|
||||
vm_name: myvm
|
||||
download_image_path: /home/user/mysnaphost.qcow2
|
||||
|
||||
# Delete all snapshots older than 2 days
|
||||
- ovirt_snapshot:
|
||||
vm_name: test
|
||||
keep_days_old: 2
|
||||
|
||||
- name: Select which disks should be add to snapshot
|
||||
ovirt_snapshot:
|
||||
vm_name: test
|
||||
disks:
|
||||
- id: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
- name: my_disk_name
|
||||
'''
|
||||
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the snapshot which is managed
|
||||
returned: On success if snapshot is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
snapshot:
|
||||
description: "Dictionary of all the snapshot attributes. Snapshot attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/snapshot."
|
||||
returned: On success if snapshot is found.
|
||||
type: dict
|
||||
snapshots:
|
||||
description: List of deleted snapshots when keep_days_old is defined and snapshot is older than the input days
|
||||
returned: On success returns deleted snapshots
|
||||
type: list
|
||||
'''
|
||||
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
import os
|
||||
import ssl
|
||||
import time
|
||||
|
||||
from ansible.module_utils.six.moves.http_client import HTTPSConnection, IncompleteRead
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
||||
|
||||
from datetime import datetime
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
get_entity,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
wait,
|
||||
get_id_by_name,
|
||||
get_link_name
|
||||
)
|
||||
|
||||
|
||||
def transfer(connection, module, direction, transfer_func):
|
||||
transfers_service = connection.system_service().image_transfers_service()
|
||||
transfer = transfers_service.add(
|
||||
otypes.ImageTransfer(
|
||||
image=otypes.Image(
|
||||
id=module.params['disk_id'],
|
||||
),
|
||||
direction=direction,
|
||||
)
|
||||
)
|
||||
transfer_service = transfers_service.image_transfer_service(transfer.id)
|
||||
|
||||
try:
|
||||
# After adding a new transfer for the disk, the transfer's status will be INITIALIZING.
|
||||
# Wait until the init phase is over. The actual transfer can start when its status is "Transferring".
|
||||
while transfer.phase == otypes.ImageTransferPhase.INITIALIZING:
|
||||
time.sleep(module.params['poll_interval'])
|
||||
transfer = transfer_service.get()
|
||||
|
||||
proxy_url = urlparse(transfer.proxy_url)
|
||||
context = ssl.create_default_context()
|
||||
auth = module.params['auth']
|
||||
if auth.get('insecure'):
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
elif auth.get('ca_file'):
|
||||
context.load_verify_locations(cafile=auth.get('ca_file'))
|
||||
|
||||
proxy_connection = HTTPSConnection(
|
||||
proxy_url.hostname,
|
||||
proxy_url.port,
|
||||
context=context,
|
||||
)
|
||||
|
||||
transfer_func(
|
||||
transfer_service,
|
||||
proxy_connection,
|
||||
proxy_url,
|
||||
transfer.signed_ticket
|
||||
)
|
||||
return True
|
||||
finally:
|
||||
transfer_service.finalize()
|
||||
while transfer.phase in [
|
||||
otypes.ImageTransferPhase.TRANSFERRING,
|
||||
otypes.ImageTransferPhase.FINALIZING_SUCCESS,
|
||||
]:
|
||||
time.sleep(module.params['poll_interval'])
|
||||
transfer = transfer_service.get()
|
||||
if transfer.phase in [
|
||||
otypes.ImageTransferPhase.UNKNOWN,
|
||||
otypes.ImageTransferPhase.FINISHED_FAILURE,
|
||||
otypes.ImageTransferPhase.FINALIZING_FAILURE,
|
||||
otypes.ImageTransferPhase.CANCELLED,
|
||||
]:
|
||||
raise Exception(
|
||||
"Error occurred while uploading image. The transfer is in %s" % transfer.phase
|
||||
)
|
||||
if module.params.get('logical_unit'):
|
||||
disks_service = connection.system_service().disks_service()
|
||||
wait(
|
||||
service=disks_service.service(module.params['id']),
|
||||
condition=lambda d: d.status == otypes.DiskStatus.OK,
|
||||
wait=module.params['wait'],
|
||||
timeout=module.params['timeout'],
|
||||
)
|
||||
|
||||
|
||||
def upload_disk_image(connection, module):
|
||||
def _transfer(transfer_service, proxy_connection, proxy_url, transfer_ticket):
|
||||
BUF_SIZE = 128 * 1024
|
||||
path = module.params['upload_image_path']
|
||||
|
||||
image_size = os.path.getsize(path)
|
||||
proxy_connection.putrequest("PUT", proxy_url.path)
|
||||
proxy_connection.putheader('Content-Length', "%d" % (image_size,))
|
||||
proxy_connection.endheaders()
|
||||
with open(path, "rb") as disk:
|
||||
pos = 0
|
||||
while pos < image_size:
|
||||
to_read = min(image_size - pos, BUF_SIZE)
|
||||
chunk = disk.read(to_read)
|
||||
if not chunk:
|
||||
transfer_service.pause()
|
||||
raise RuntimeError("Unexpected end of file at pos=%d" % pos)
|
||||
proxy_connection.send(chunk)
|
||||
pos += len(chunk)
|
||||
|
||||
return transfer(
|
||||
connection,
|
||||
module,
|
||||
otypes.ImageTransferDirection.UPLOAD,
|
||||
transfer_func=_transfer,
|
||||
)
|
||||
|
||||
|
||||
def download_disk_image(connection, module):
|
||||
def _transfer(transfer_service, proxy_connection, proxy_url, transfer_ticket):
|
||||
BUF_SIZE = 128 * 1024
|
||||
transfer_headers = {
|
||||
'Authorization': transfer_ticket,
|
||||
}
|
||||
proxy_connection.request(
|
||||
'GET',
|
||||
proxy_url.path,
|
||||
headers=transfer_headers,
|
||||
)
|
||||
r = proxy_connection.getresponse()
|
||||
path = module.params["download_image_path"]
|
||||
image_size = int(r.getheader('Content-Length'))
|
||||
with open(path, "wb") as mydisk:
|
||||
pos = 0
|
||||
while pos < image_size:
|
||||
to_read = min(image_size - pos, BUF_SIZE)
|
||||
chunk = r.read(to_read)
|
||||
if not chunk:
|
||||
raise RuntimeError("Socket disconnected")
|
||||
mydisk.write(chunk)
|
||||
pos += len(chunk)
|
||||
|
||||
return transfer(
|
||||
connection,
|
||||
module,
|
||||
otypes.ImageTransferDirection.DOWNLOAD,
|
||||
transfer_func=_transfer,
|
||||
)
|
||||
|
||||
|
||||
def get_disk_attachment(disk, disk_attachments, connection):
|
||||
for disk_attachment in disk_attachments:
|
||||
if get_link_name(connection, disk_attachment.disk) == disk.get('name') or\
|
||||
disk_attachment.disk.id == disk.get('id'):
|
||||
return disk_attachment
|
||||
|
||||
|
||||
def create_snapshot(module, vm_service, snapshots_service, connection):
|
||||
changed = False
|
||||
snapshot = get_entity(
|
||||
snapshots_service.snapshot_service(module.params['snapshot_id'])
|
||||
)
|
||||
if snapshot is None:
|
||||
if not module.check_mode:
|
||||
disk_attachments_id = set(
|
||||
get_disk_attachment(disk, vm_service.disk_attachments_service().list(), connection).id
|
||||
for disk in module.params.get('disks')
|
||||
) if module.params.get('disks') else None
|
||||
|
||||
snapshot = snapshots_service.add(
|
||||
otypes.Snapshot(
|
||||
description=module.params.get('description'),
|
||||
persist_memorystate=module.params.get('use_memory'),
|
||||
disk_attachments=[otypes.DiskAttachment(disk=otypes.Disk(id=da_id)) for da_id in disk_attachments_id] if disk_attachments_id else None
|
||||
)
|
||||
)
|
||||
changed = True
|
||||
wait(
|
||||
service=snapshots_service.snapshot_service(snapshot.id),
|
||||
condition=lambda snap: snap.snapshot_status == otypes.SnapshotStatus.OK,
|
||||
wait=module.params['wait'],
|
||||
timeout=module.params['timeout'],
|
||||
)
|
||||
return {
|
||||
'changed': changed,
|
||||
'id': snapshot.id,
|
||||
'snapshot': get_dict_of_struct(snapshot),
|
||||
}
|
||||
|
||||
|
||||
def remove_snapshot(module, vm_service, snapshots_service, snapshot_id=None):
|
||||
changed = False
|
||||
if not snapshot_id:
|
||||
snapshot_id = module.params['snapshot_id']
|
||||
snapshot = get_entity(
|
||||
snapshots_service.snapshot_service(snapshot_id)
|
||||
)
|
||||
|
||||
if snapshot:
|
||||
snapshot_service = snapshots_service.snapshot_service(snapshot.id)
|
||||
if not module.check_mode:
|
||||
snapshot_service.remove()
|
||||
changed = True
|
||||
wait(
|
||||
service=snapshot_service,
|
||||
condition=lambda snapshot: snapshot is None,
|
||||
wait=module.params['wait'],
|
||||
timeout=module.params['timeout'],
|
||||
)
|
||||
|
||||
return {
|
||||
'changed': changed,
|
||||
'id': snapshot.id if snapshot else None,
|
||||
'snapshot': get_dict_of_struct(snapshot),
|
||||
}
|
||||
|
||||
|
||||
def restore_snapshot(module, vm_service, snapshots_service):
|
||||
changed = False
|
||||
snapshot_service = snapshots_service.snapshot_service(
|
||||
module.params['snapshot_id']
|
||||
)
|
||||
snapshot = get_entity(snapshot_service)
|
||||
if snapshot is None:
|
||||
raise Exception(
|
||||
"Snapshot with id '%s' doesn't exist" % module.params['snapshot_id']
|
||||
)
|
||||
|
||||
if snapshot.snapshot_status != otypes.SnapshotStatus.IN_PREVIEW:
|
||||
if not module.check_mode:
|
||||
snapshot_service.restore(
|
||||
restore_memory=module.params.get('use_memory'),
|
||||
)
|
||||
changed = True
|
||||
else:
|
||||
if not module.check_mode:
|
||||
vm_service.commit_snapshot()
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
wait(
|
||||
service=snapshot_service,
|
||||
condition=lambda snap: snap.snapshot_status == otypes.SnapshotStatus.OK,
|
||||
wait=module.params['wait'],
|
||||
timeout=module.params['timeout'],
|
||||
)
|
||||
return {
|
||||
'changed': changed,
|
||||
'id': snapshot.id if snapshot else None,
|
||||
'snapshot': get_dict_of_struct(snapshot),
|
||||
}
|
||||
|
||||
|
||||
def get_snapshot_disk_id(module, snapshots_service):
|
||||
snapshot_service = snapshots_service.snapshot_service(module.params.get('snapshot_id'))
|
||||
snapshot_disks_service = snapshot_service.disks_service()
|
||||
|
||||
disk_id = ''
|
||||
if module.params.get('disk_id'):
|
||||
disk_id = module.params.get('disk_id')
|
||||
elif module.params.get('disk_name'):
|
||||
disk_id = get_id_by_name(snapshot_disks_service, module.params.get('disk_name'))
|
||||
return disk_id
|
||||
|
||||
|
||||
def remove_old_snapshosts(module, vm_service, snapshots_service):
|
||||
deleted_snapshots = []
|
||||
changed = False
|
||||
date_now = datetime.now()
|
||||
for snapshot in snapshots_service.list():
|
||||
if snapshot.vm is not None and snapshot.vm.name == module.params.get('vm_name'):
|
||||
diff = date_now - snapshot.date.replace(tzinfo=None)
|
||||
if diff.days >= module.params.get('keep_days_old'):
|
||||
snapshot = remove_snapshot(module, vm_service, snapshots_service, snapshot.id).get('snapshot')
|
||||
deleted_snapshots.append(snapshot)
|
||||
changed = True
|
||||
return dict(snapshots=deleted_snapshots, changed=changed)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['restore', 'present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
vm_name=dict(required=True),
|
||||
snapshot_id=dict(default=None),
|
||||
disks=dict(
|
||||
type='list',
|
||||
options=dict(
|
||||
name=dict(default=None, type='str'),
|
||||
id=dict(default=None, type='str'),
|
||||
)
|
||||
),
|
||||
disk_id=dict(default=None),
|
||||
disk_name=dict(default=None),
|
||||
description=dict(default=None),
|
||||
download_image_path=dict(default=None),
|
||||
upload_image_path=dict(default=None),
|
||||
keep_days_old=dict(default=None, type='int'),
|
||||
use_memory=dict(
|
||||
default=None,
|
||||
type='bool',
|
||||
aliases=['restore_memory', 'save_memory'],
|
||||
),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
('state', 'absent', ['snapshot_id']),
|
||||
('state', 'restore', ['snapshot_id']),
|
||||
]
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
ret = {}
|
||||
vm_name = module.params.get('vm_name')
|
||||
auth = module.params['auth']
|
||||
connection = create_connection(auth)
|
||||
vms_service = connection.system_service().vms_service()
|
||||
vm = search_by_name(vms_service, vm_name)
|
||||
if not vm:
|
||||
module.fail_json(
|
||||
msg="Vm '{name}' doesn't exist.".format(name=vm_name),
|
||||
)
|
||||
|
||||
vm_service = vms_service.vm_service(vm.id)
|
||||
snapshots_service = vms_service.vm_service(vm.id).snapshots_service()
|
||||
try:
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
if module.params.get('disk_id') or module.params.get('disk_name'):
|
||||
module.params['disk_id'] = get_snapshot_disk_id(module, snapshots_service)
|
||||
if module.params['upload_image_path']:
|
||||
ret['changed'] = upload_disk_image(connection, module)
|
||||
if module.params['download_image_path']:
|
||||
ret['changed'] = download_disk_image(connection, module)
|
||||
if module.params.get('keep_days_old') is not None:
|
||||
ret = remove_old_snapshosts(module, vm_service, snapshots_service)
|
||||
else:
|
||||
ret = create_snapshot(module, vm_service, snapshots_service, connection)
|
||||
elif state == 'restore':
|
||||
ret = restore_snapshot(module, vm_service, snapshots_service)
|
||||
elif state == 'absent':
|
||||
ret = remove_snapshot(module, vm_service, snapshots_service)
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,133 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_snapshot_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV virtual machine snapshots
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV virtual machine snapshots."
|
||||
- This module was called C(ovirt_snapshot_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_snapshot_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_snapshots), which
|
||||
contains a list of snapshots. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
vm:
|
||||
description:
|
||||
- "Name of the VM with snapshot."
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- "Description of the snapshot, can be used as glob expression."
|
||||
snapshot_id:
|
||||
description:
|
||||
- "Id of the snapshot we want to retrieve information about."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all snapshots which description start with C(update) for VM named C(centos7):
|
||||
- ovirt_snapshot_info:
|
||||
vm: centos7
|
||||
description: update*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_snapshots }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_snapshots:
|
||||
description: "List of dictionaries describing the snapshot. Snapshot attributes are mapped to dictionary keys,
|
||||
all snapshot attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/snapshot."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
|
||||
import fnmatch
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
vm=dict(required=True),
|
||||
description=dict(default=None),
|
||||
snapshot_id=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_snapshot_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_snapshot_facts' module has been renamed to 'ovirt_snapshot_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
vms_service = connection.system_service().vms_service()
|
||||
vm_name = module.params['vm']
|
||||
vm = search_by_name(vms_service, vm_name)
|
||||
if vm is None:
|
||||
raise Exception("VM '%s' was not found." % vm_name)
|
||||
|
||||
snapshots_service = vms_service.service(vm.id).snapshots_service()
|
||||
if module.params['description']:
|
||||
snapshots = [
|
||||
e for e in snapshots_service.list()
|
||||
if fnmatch.fnmatch(e.description, module.params['description'])
|
||||
]
|
||||
elif module.params['snapshot_id']:
|
||||
snapshots = [
|
||||
snapshots_service.snapshot_service(module.params['snapshot_id']).get()
|
||||
]
|
||||
else:
|
||||
snapshots = snapshots_service.list()
|
||||
|
||||
result = dict(
|
||||
ovirt_snapshots=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in snapshots
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,285 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2017 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_storage_connection
|
||||
short_description: Module to manage storage connections in oVirt
|
||||
version_added: "2.4"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage storage connections in oVirt"
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "Id of the storage connection to manage."
|
||||
state:
|
||||
description:
|
||||
- "Should the storage connection be present or absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
storage:
|
||||
description:
|
||||
- "Name of the storage domain to be used with storage connection."
|
||||
address:
|
||||
description:
|
||||
- "Address of the storage server. E.g.: myserver.mydomain.com"
|
||||
path:
|
||||
description:
|
||||
- "Path of the mount point of the storage. E.g.: /path/to/my/data"
|
||||
nfs_version:
|
||||
description:
|
||||
- "NFS version. One of: I(auto), I(v3), I(v4) or I(v4_1)."
|
||||
nfs_timeout:
|
||||
description:
|
||||
- "The time in tenths of a second to wait for a response before retrying NFS requests. Range 0 to 65535."
|
||||
nfs_retrans:
|
||||
description:
|
||||
- "The number of times to retry a request before attempting further recovery actions. Range 0 to 65535."
|
||||
mount_options:
|
||||
description:
|
||||
- "Option which will be passed when mounting storage."
|
||||
password:
|
||||
description:
|
||||
- "A CHAP password for logging into a target."
|
||||
username:
|
||||
description:
|
||||
- "A CHAP username for logging into a target."
|
||||
port:
|
||||
description:
|
||||
- "Port of the iSCSI storage server."
|
||||
target:
|
||||
description:
|
||||
- "The target IQN for the storage device."
|
||||
type:
|
||||
description:
|
||||
- "Storage type. For example: I(nfs), I(iscsi), etc."
|
||||
vfs_type:
|
||||
description:
|
||||
- "Virtual File System type."
|
||||
force:
|
||||
description:
|
||||
- "This parameter is relevant only when updating a connection."
|
||||
- "If I(true) the storage domain don't have to be in I(MAINTENANCE)
|
||||
state, so the storage connection is updated."
|
||||
type: bool
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Add new storage connection:
|
||||
- ovirt_storage_connection:
|
||||
storage: myiscsi
|
||||
address: 10.34.63.199
|
||||
target: iqn.2016-08-09.domain-01:nickname
|
||||
port: 3260
|
||||
type: iscsi
|
||||
|
||||
# Update the existing storage connection address:
|
||||
- ovirt_storage_connection:
|
||||
id: 26915c96-92ff-47e5-9e77-b581db2f2d36
|
||||
address: 10.34.63.204
|
||||
force: true
|
||||
|
||||
# Remove storage connection:
|
||||
- ovirt_storage_connection:
|
||||
id: 26915c96-92ff-47e5-9e77-b581db2f2d36
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the storage connection which is managed
|
||||
returned: On success if storage connection is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
storage_connection:
|
||||
description: "Dictionary of all the storage connection attributes. Storage connection attributes can be found on your oVirt instance
|
||||
at following url: https://ovirt.example.com/ovirt-engine/api/model#types/storage_connection."
|
||||
returned: On success if storage connection is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
class StorageConnectionModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.StorageConnection(
|
||||
address=self.param('address'),
|
||||
path=self.param('path'),
|
||||
nfs_version=otypes.NfsVersion(
|
||||
self.param('nfs_version')
|
||||
) if self.param('nfs_version') is not None else None,
|
||||
nfs_timeo=self.param('nfs_timeout'),
|
||||
nfs_retrans=self.param('nfs_retrans'),
|
||||
mount_options=self.param('mount_options'),
|
||||
password=self.param('password'),
|
||||
username=self.param('username'),
|
||||
port=self.param('port'),
|
||||
target=self.param('target'),
|
||||
type=otypes.StorageType(
|
||||
self.param('type')
|
||||
) if self.param('type') is not None else None,
|
||||
vfs_type=self.param('vfs_type'),
|
||||
)
|
||||
|
||||
def _get_storage_domain_service(self):
|
||||
sds_service = self._connection.system_service().storage_domains_service()
|
||||
sd = search_by_name(sds_service, self.param('storage'))
|
||||
if sd is None:
|
||||
raise Exception(
|
||||
"Storage '%s' was not found." % self.param('storage')
|
||||
)
|
||||
return sd, sds_service.storage_domain_service(sd.id)
|
||||
|
||||
def post_present(self, entity_id):
|
||||
if self.param('storage'):
|
||||
sd, sd_service = self._get_storage_domain_service()
|
||||
if entity_id not in [
|
||||
sd_conn.id for sd_conn in self._connection.follow_link(sd.storage_connections)
|
||||
]:
|
||||
scs_service = sd_service.storage_connections_service()
|
||||
if not self._module.check_mode:
|
||||
scs_service.add(
|
||||
connection=otypes.StorageConnection(
|
||||
id=entity_id,
|
||||
),
|
||||
)
|
||||
self.changed = True
|
||||
|
||||
def pre_remove(self, entity_id):
|
||||
if self.param('storage'):
|
||||
sd, sd_service = self._get_storage_domain_service()
|
||||
if entity_id in [
|
||||
sd_conn.id for sd_conn in self._connection.follow_link(sd.storage_connections)
|
||||
]:
|
||||
scs_service = sd_service.storage_connections_service()
|
||||
sc_service = scs_service.connection_service(entity_id)
|
||||
if not self._module.check_mode:
|
||||
sc_service.remove()
|
||||
self.changed = True
|
||||
|
||||
def update_check(self, entity):
|
||||
return (
|
||||
equal(self.param('address'), entity.address) and
|
||||
equal(self.param('path'), entity.path) and
|
||||
equal(self.param('nfs_version'), str(entity.nfs_version)) and
|
||||
equal(self.param('nfs_timeout'), entity.nfs_timeo) and
|
||||
equal(self.param('nfs_retrans'), entity.nfs_retrans) and
|
||||
equal(self.param('mount_options'), entity.mount_options) and
|
||||
equal(self.param('username'), entity.username) and
|
||||
equal(self.param('port'), entity.port) and
|
||||
equal(self.param('target'), entity.target) and
|
||||
equal(self.param('type'), str(entity.type)) and
|
||||
equal(self.param('vfs_type'), entity.vfs_type)
|
||||
)
|
||||
|
||||
|
||||
def find_sc_by_attributes(module, storage_connections_service):
|
||||
for sd_conn in [
|
||||
sc for sc in storage_connections_service.list()
|
||||
if str(sc.type) == module.params['type']
|
||||
]:
|
||||
sd_conn_type = str(sd_conn.type)
|
||||
if sd_conn_type in ['nfs', 'posixfs', 'glusterfs', 'localfs']:
|
||||
if (
|
||||
module.params['address'] == sd_conn.address and
|
||||
module.params['path'] == sd_conn.path
|
||||
):
|
||||
return sd_conn
|
||||
elif sd_conn_type in ['iscsi', 'fcp']:
|
||||
if (
|
||||
module.params['address'] == sd_conn.address and
|
||||
module.params['target'] == sd_conn.target
|
||||
):
|
||||
return sd_conn
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
id=dict(default=None),
|
||||
address=dict(default=None),
|
||||
path=dict(default=None),
|
||||
nfs_version=dict(default=None),
|
||||
nfs_timeout=dict(default=None, type='int'),
|
||||
nfs_retrans=dict(default=None, type='int'),
|
||||
mount_options=dict(default=None),
|
||||
password=dict(default=None, no_log=True),
|
||||
username=dict(default=None),
|
||||
port=dict(default=None, type='int'),
|
||||
target=dict(default=None),
|
||||
type=dict(default=None),
|
||||
vfs_type=dict(default=None),
|
||||
force=dict(type='bool', default=False),
|
||||
storage=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
storage_connections_service = connection.system_service().storage_connections_service()
|
||||
storage_connection_module = StorageConnectionModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=storage_connections_service,
|
||||
)
|
||||
entity = None
|
||||
if module.params['id'] is None:
|
||||
entity = find_sc_by_attributes(module, storage_connections_service)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = storage_connection_module.create(
|
||||
entity=entity,
|
||||
update_params={'force': True},
|
||||
)
|
||||
storage_connection_module.post_present(ret['id'])
|
||||
elif state == 'absent':
|
||||
storage_connection_module.pre_remove(module.params['id'])
|
||||
ret = storage_connection_module.remove(entity=entity)
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,809 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_storage_domain
|
||||
short_description: Module to manage storage domains in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage storage domains in oVirt/RHV"
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "Id of the storage domain to be imported."
|
||||
version_added: "2.4"
|
||||
name:
|
||||
description:
|
||||
- "Name of the storage domain to manage. (Not required when state is I(imported))"
|
||||
state:
|
||||
description:
|
||||
- "Should the storage domain be present/absent/maintenance/unattached/imported/update_ovf_store"
|
||||
- "I(imported) is supported since version 2.4."
|
||||
- "I(update_ovf_store) is supported since version 2.5, currently if C(wait) is (true), we don't wait for update."
|
||||
choices: ['present', 'absent', 'maintenance', 'unattached', 'imported', 'update_ovf_store']
|
||||
default: present
|
||||
description:
|
||||
description:
|
||||
- "Description of the storage domain."
|
||||
comment:
|
||||
description:
|
||||
- "Comment of the storage domain."
|
||||
data_center:
|
||||
description:
|
||||
- "Data center name where storage domain should be attached."
|
||||
- "This parameter isn't idempotent, it's not possible to change data center of storage domain."
|
||||
domain_function:
|
||||
description:
|
||||
- "Function of the storage domain."
|
||||
- "This parameter isn't idempotent, it's not possible to change domain function of storage domain."
|
||||
choices: ['data', 'iso', 'export']
|
||||
default: 'data'
|
||||
aliases: ['type']
|
||||
host:
|
||||
description:
|
||||
- "Host to be used to mount storage."
|
||||
localfs:
|
||||
description:
|
||||
- "Dictionary with values for localfs storage type:"
|
||||
- "Note that these parameters are not idempotent."
|
||||
suboptions:
|
||||
path:
|
||||
description:
|
||||
- "Path of the mount point. E.g.: /path/to/my/data"
|
||||
version_added: "2.4"
|
||||
nfs:
|
||||
description:
|
||||
- "Dictionary with values for NFS storage type:"
|
||||
- "Note that these parameters are not idempotent."
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- "Address of the NFS server. E.g.: myserver.mydomain.com"
|
||||
path:
|
||||
description:
|
||||
- "Path of the mount point. E.g.: /path/to/my/data"
|
||||
version:
|
||||
description:
|
||||
- "NFS version. One of: I(auto), I(v3), I(v4) or I(v4_1)."
|
||||
timeout:
|
||||
description:
|
||||
- "The time in tenths of a second to wait for a response before retrying NFS requests. Range 0 to 65535."
|
||||
retrans:
|
||||
description:
|
||||
- "The number of times to retry a request before attempting further recovery actions. Range 0 to 65535."
|
||||
mount_options:
|
||||
description:
|
||||
- "Option which will be passed when mounting storage."
|
||||
iscsi:
|
||||
description:
|
||||
- "Dictionary with values for iSCSI storage type:"
|
||||
- "Note that these parameters are not idempotent."
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- Address of the iSCSI storage server.
|
||||
port:
|
||||
description:
|
||||
- Port of the iSCSI storage server.
|
||||
target:
|
||||
description:
|
||||
- The target IQN for the storage device.
|
||||
lun_id:
|
||||
description:
|
||||
- LUN id(s).
|
||||
username:
|
||||
description:
|
||||
- A CHAP user name for logging into a target.
|
||||
password:
|
||||
description:
|
||||
- A CHAP password for logging into a target.
|
||||
override_luns:
|
||||
description:
|
||||
- If I(True) ISCSI storage domain luns will be overridden before adding.
|
||||
type: bool
|
||||
target_lun_map:
|
||||
description:
|
||||
- List of dictionary containing targets and LUNs.
|
||||
version_added: 2.5
|
||||
posixfs:
|
||||
description:
|
||||
- "Dictionary with values for PosixFS storage type:"
|
||||
- "Note that these parameters are not idempotent."
|
||||
suboptions:
|
||||
path:
|
||||
description:
|
||||
- "Path of the mount point. E.g.: /path/to/my/data"
|
||||
vfs_type:
|
||||
description:
|
||||
- Virtual File System type.
|
||||
mount_options:
|
||||
description:
|
||||
- Option which will be passed when mounting storage.
|
||||
glusterfs:
|
||||
description:
|
||||
- "Dictionary with values for GlusterFS storage type:"
|
||||
- "Note that these parameters are not idempotent."
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- "Address of the Gluster server. E.g.: myserver.mydomain.com"
|
||||
path:
|
||||
description:
|
||||
- "Path of the mount point. E.g.: /path/to/my/data"
|
||||
mount_options:
|
||||
description:
|
||||
- Option which will be passed when mounting storage.
|
||||
managed_block_storage:
|
||||
description:
|
||||
- "Dictionary with values for managed block storage type"
|
||||
- "Note: available from ovirt 4.3"
|
||||
suboptions:
|
||||
driver_options:
|
||||
description:
|
||||
- "The options to be passed when creating a storage domain using a cinder driver."
|
||||
- "List of dictionary containing C(name) and C(value) of driver option"
|
||||
driver_sensitive_options:
|
||||
description:
|
||||
- "Parameters containing sensitive information, to be passed when creating a storage domain using a cinder driver."
|
||||
- "List of dictionary containing C(name) and C(value) of driver sensitive option"
|
||||
version_added: "2.9"
|
||||
fcp:
|
||||
description:
|
||||
- "Dictionary with values for fibre channel storage type:"
|
||||
- "Note that these parameters are not idempotent."
|
||||
suboptions:
|
||||
lun_id:
|
||||
description:
|
||||
- LUN id.
|
||||
override_luns:
|
||||
description:
|
||||
- If I(True) FCP storage domain LUNs will be overridden before adding.
|
||||
type: bool
|
||||
wipe_after_delete:
|
||||
description:
|
||||
- "Boolean flag which indicates whether the storage domain should wipe the data after delete."
|
||||
type: bool
|
||||
version_added: "2.5"
|
||||
backup:
|
||||
description:
|
||||
- "Boolean flag which indicates whether the storage domain is configured as backup or not."
|
||||
type: bool
|
||||
version_added: "2.5"
|
||||
critical_space_action_blocker:
|
||||
description:
|
||||
- "Indicates the minimal free space the storage domain should contain in percentages."
|
||||
version_added: "2.5"
|
||||
warning_low_space:
|
||||
description:
|
||||
- "Indicates the minimum percentage of a free space in a storage domain to present a warning."
|
||||
version_added: "2.5"
|
||||
destroy:
|
||||
description:
|
||||
- "Logical remove of the storage domain. If I(true) retains the storage domain's data for import."
|
||||
- "This parameter is relevant only when C(state) is I(absent)."
|
||||
type: bool
|
||||
format:
|
||||
description:
|
||||
- "If I(True) storage domain will be formatted after removing it from oVirt/RHV."
|
||||
- "This parameter is relevant only when C(state) is I(absent)."
|
||||
type: bool
|
||||
discard_after_delete:
|
||||
description:
|
||||
- "If I(True) storage domain blocks will be discarded upon deletion. Enabled by default."
|
||||
- "This parameter is relevant only for block based storage domains."
|
||||
type: bool
|
||||
version_added: 2.5
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Add data NFS storage domain
|
||||
- ovirt_storage_domain:
|
||||
name: data_nfs
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
nfs:
|
||||
address: 10.34.63.199
|
||||
path: /path/data
|
||||
|
||||
# Add data NFS storage domain with id for data center
|
||||
- ovirt_storage_domain:
|
||||
name: data_nfs
|
||||
host: myhost
|
||||
data_center: 11111
|
||||
nfs:
|
||||
address: 10.34.63.199
|
||||
path: /path/data
|
||||
mount_options: noexec,nosuid
|
||||
|
||||
# Add data localfs storage domain
|
||||
- ovirt_storage_domain:
|
||||
name: data_localfs
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
localfs:
|
||||
path: /path/to/data
|
||||
|
||||
# Add data iSCSI storage domain:
|
||||
- ovirt_storage_domain:
|
||||
name: data_iscsi
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
iscsi:
|
||||
target: iqn.2016-08-09.domain-01:nickname
|
||||
lun_id:
|
||||
- 1IET_000d0001
|
||||
- 1IET_000d0002
|
||||
address: 10.34.63.204
|
||||
discard_after_delete: True
|
||||
backup: False
|
||||
critical_space_action_blocker: 5
|
||||
warning_low_space: 10
|
||||
|
||||
# Since Ansible 2.5 you can specify multiple targets for storage domain,
|
||||
# Add data iSCSI storage domain with multiple targets:
|
||||
- ovirt_storage_domain:
|
||||
name: data_iscsi
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
iscsi:
|
||||
target_lun_map:
|
||||
- target: iqn.2016-08-09.domain-01:nickname
|
||||
lun_id: 1IET_000d0001
|
||||
- target: iqn.2016-08-09.domain-02:nickname
|
||||
lun_id: 1IET_000d0002
|
||||
address: 10.34.63.204
|
||||
discard_after_delete: True
|
||||
|
||||
# Add data glusterfs storage domain
|
||||
- ovirt_storage_domain:
|
||||
name: glusterfs_1
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
glusterfs:
|
||||
address: 10.10.10.10
|
||||
path: /path/data
|
||||
|
||||
# Create export NFS storage domain:
|
||||
- ovirt_storage_domain:
|
||||
name: myexportdomain
|
||||
domain_function: export
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
nfs:
|
||||
address: 10.34.63.199
|
||||
path: /path/export
|
||||
wipe_after_delete: False
|
||||
backup: True
|
||||
critical_space_action_blocker: 2
|
||||
warning_low_space: 5
|
||||
|
||||
# Import export NFS storage domain:
|
||||
- ovirt_storage_domain:
|
||||
state: imported
|
||||
domain_function: export
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
nfs:
|
||||
address: 10.34.63.199
|
||||
path: /path/export
|
||||
|
||||
# Import FCP storage domain:
|
||||
- ovirt_storage_domain:
|
||||
state: imported
|
||||
name: data_fcp
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
fcp: {}
|
||||
|
||||
# Update OVF_STORE:
|
||||
- ovirt_storage_domain:
|
||||
state: update_ovf_store
|
||||
name: domain
|
||||
|
||||
# Create ISO NFS storage domain
|
||||
- ovirt_storage_domain:
|
||||
name: myiso
|
||||
domain_function: iso
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
nfs:
|
||||
address: 10.34.63.199
|
||||
path: /path/iso
|
||||
|
||||
# Create managed storage domain
|
||||
# Available from ovirt 4.3 and ansible 2.9
|
||||
- ovirt_storage_domain:
|
||||
name: my_managed_domain
|
||||
host: myhost
|
||||
data_center: mydatacenter
|
||||
managed_block_storage:
|
||||
driver_options:
|
||||
- name: rbd_pool
|
||||
value: pool1
|
||||
- name: rbd_user
|
||||
value: admin
|
||||
- name: volume_driver
|
||||
value: cinder.volume.drivers.rbd.RBDDriver
|
||||
- name: rbd_keyring_conf
|
||||
value: /etc/ceph/keyring
|
||||
driver_sensitive_options:
|
||||
- name: secret_password
|
||||
value: password
|
||||
|
||||
# Remove storage domain
|
||||
- ovirt_storage_domain:
|
||||
state: absent
|
||||
name: mystorage_domain
|
||||
format: true
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the storage domain which is managed
|
||||
returned: On success if storage domain is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
storage_domain:
|
||||
description: "Dictionary of all the storage domain attributes. Storage domain attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/storage_domain."
|
||||
returned: On success if storage domain is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
|
||||
from ovirtsdk4.types import StorageDomainStatus as sdstate
|
||||
from ovirtsdk4.types import HostStatus as hoststate
|
||||
from ovirtsdk4.types import DataCenterStatus as dcstatus
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_entity,
|
||||
get_id_by_name,
|
||||
OvirtRetry,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
search_by_attributes,
|
||||
wait,
|
||||
)
|
||||
|
||||
|
||||
class StorageDomainModule(BaseModule):
|
||||
|
||||
def _get_storage_type(self):
|
||||
for sd_type in ['nfs', 'iscsi', 'posixfs', 'glusterfs', 'fcp', 'localfs', 'managed_block_storage']:
|
||||
if self.param(sd_type) is not None:
|
||||
return sd_type
|
||||
|
||||
def _get_storage(self):
|
||||
for sd_type in ['nfs', 'iscsi', 'posixfs', 'glusterfs', 'fcp', 'localfs', 'managed_block_storage']:
|
||||
if self.param(sd_type) is not None:
|
||||
return self.param(sd_type)
|
||||
|
||||
def _login(self, storage_type, storage):
|
||||
if storage_type == 'iscsi':
|
||||
hosts_service = self._connection.system_service().hosts_service()
|
||||
host_id = get_id_by_name(hosts_service, self.param('host'))
|
||||
if storage.get('target'):
|
||||
hosts_service.host_service(host_id).iscsi_login(
|
||||
iscsi=otypes.IscsiDetails(
|
||||
username=storage.get('username'),
|
||||
password=storage.get('password'),
|
||||
address=storage.get('address'),
|
||||
target=storage.get('target'),
|
||||
),
|
||||
)
|
||||
elif storage.get('target_lun_map'):
|
||||
for target in [m['target'] for m in storage.get('target_lun_map')]:
|
||||
hosts_service.host_service(host_id).iscsi_login(
|
||||
iscsi=otypes.IscsiDetails(
|
||||
username=storage.get('username'),
|
||||
password=storage.get('password'),
|
||||
address=storage.get('address'),
|
||||
target=target,
|
||||
),
|
||||
)
|
||||
|
||||
def __target_lun_map(self, storage):
|
||||
if storage.get('target'):
|
||||
lun_ids = storage.get('lun_id') if isinstance(storage.get('lun_id'), list) else [(storage.get('lun_id'))]
|
||||
return [(lun_id, storage.get('target')) for lun_id in lun_ids]
|
||||
elif storage.get('target_lun_map'):
|
||||
return [(target_map.get('lun_id'), target_map.get('target')) for target_map in storage.get('target_lun_map')]
|
||||
else:
|
||||
lun_ids = storage.get('lun_id') if isinstance(storage.get('lun_id'), list) else [(storage.get('lun_id'))]
|
||||
return [(lun_id, None) for lun_id in lun_ids]
|
||||
|
||||
def build_entity(self):
|
||||
storage_type = self._get_storage_type()
|
||||
storage = self._get_storage()
|
||||
self._login(storage_type, storage)
|
||||
|
||||
return otypes.StorageDomain(
|
||||
name=self.param('name'),
|
||||
description=self.param('description'),
|
||||
comment=self.param('comment'),
|
||||
wipe_after_delete=self.param('wipe_after_delete'),
|
||||
backup=self.param('backup'),
|
||||
critical_space_action_blocker=self.param('critical_space_action_blocker'),
|
||||
warning_low_space_indicator=self.param('warning_low_space'),
|
||||
import_=True if self.param('state') == 'imported' else None,
|
||||
id=self.param('id') if self.param('state') == 'imported' else None,
|
||||
type=otypes.StorageDomainType(storage_type if storage_type == 'managed_block_storage' else self.param('domain_function')),
|
||||
host=otypes.Host(name=self.param('host')),
|
||||
discard_after_delete=self.param('discard_after_delete'),
|
||||
storage=otypes.HostStorage(
|
||||
driver_options=[
|
||||
otypes.Property(
|
||||
name=do.get('name'),
|
||||
value=do.get('value')
|
||||
) for do in storage.get('driver_options')
|
||||
] if storage.get('driver_options') else None,
|
||||
driver_sensitive_options=[
|
||||
otypes.Property(
|
||||
name=dso.get('name'),
|
||||
value=dso.get('value')
|
||||
) for dso in storage.get('driver_sensitive_options')
|
||||
] if storage.get('driver_sensitive_options') else None,
|
||||
type=otypes.StorageType(storage_type),
|
||||
logical_units=[
|
||||
otypes.LogicalUnit(
|
||||
id=lun_id,
|
||||
address=storage.get('address'),
|
||||
port=int(storage.get('port', 3260)),
|
||||
target=target,
|
||||
username=storage.get('username'),
|
||||
password=storage.get('password'),
|
||||
) for lun_id, target in self.__target_lun_map(storage)
|
||||
] if storage_type in ['iscsi', 'fcp'] else None,
|
||||
override_luns=storage.get('override_luns'),
|
||||
mount_options=storage.get('mount_options'),
|
||||
vfs_type=(
|
||||
'glusterfs'
|
||||
if storage_type in ['glusterfs'] else storage.get('vfs_type')
|
||||
),
|
||||
address=storage.get('address'),
|
||||
path=storage.get('path'),
|
||||
nfs_retrans=storage.get('retrans'),
|
||||
nfs_timeo=storage.get('timeout'),
|
||||
nfs_version=otypes.NfsVersion(
|
||||
storage.get('version')
|
||||
) if storage.get('version') else None,
|
||||
) if storage_type is not None else None
|
||||
)
|
||||
|
||||
def _find_attached_datacenter_name(self, sd_name):
|
||||
"""
|
||||
Finds the name of the datacenter that a given
|
||||
storage domain is attached to.
|
||||
|
||||
Args:
|
||||
sd_name (str): Storage Domain name
|
||||
|
||||
Returns:
|
||||
str: Data Center name
|
||||
|
||||
Raises:
|
||||
Exception: In case storage domain in not attached to
|
||||
an active Datacenter
|
||||
"""
|
||||
dcs_service = self._connection.system_service().data_centers_service()
|
||||
dc = search_by_attributes(dcs_service, storage=sd_name)
|
||||
if dc is None:
|
||||
raise Exception(
|
||||
"Can't bring storage to state `%s`, because it seems that"
|
||||
"it is not attached to any datacenter"
|
||||
% self.param('state')
|
||||
)
|
||||
else:
|
||||
if dc.status == dcstatus.UP:
|
||||
return dc.name
|
||||
else:
|
||||
raise Exception(
|
||||
"Can't bring storage to state `%s`, because Datacenter "
|
||||
"%s is not UP" % (self.param('state'), dc.name)
|
||||
)
|
||||
|
||||
def _attached_sds_service(self, dc_name):
|
||||
# Get data center object of the storage domain:
|
||||
dcs_service = self._connection.system_service().data_centers_service()
|
||||
|
||||
# Search the data_center name, if it does not exist, try to search by guid.
|
||||
dc = search_by_name(dcs_service, dc_name)
|
||||
if dc is None:
|
||||
dc = get_entity(dcs_service.service(dc_name))
|
||||
if dc is None:
|
||||
return None
|
||||
|
||||
dc_service = dcs_service.data_center_service(dc.id)
|
||||
return dc_service.storage_domains_service()
|
||||
|
||||
def _attached_sd_service(self, storage_domain):
|
||||
dc_name = self.param('data_center')
|
||||
if not dc_name:
|
||||
# Find the DC, where the storage resides:
|
||||
dc_name = self._find_attached_datacenter_name(storage_domain.name)
|
||||
attached_sds_service = self._attached_sds_service(dc_name)
|
||||
attached_sd_service = attached_sds_service.storage_domain_service(storage_domain.id)
|
||||
return attached_sd_service
|
||||
|
||||
def _maintenance(self, storage_domain):
|
||||
attached_sd_service = self._attached_sd_service(storage_domain)
|
||||
attached_sd = get_entity(attached_sd_service)
|
||||
|
||||
if attached_sd and attached_sd.status != sdstate.MAINTENANCE:
|
||||
if not self._module.check_mode:
|
||||
attached_sd_service.deactivate()
|
||||
self.changed = True
|
||||
|
||||
wait(
|
||||
service=attached_sd_service,
|
||||
condition=lambda sd: sd.status == sdstate.MAINTENANCE,
|
||||
wait=self.param('wait'),
|
||||
timeout=self.param('timeout'),
|
||||
)
|
||||
|
||||
def _unattach(self, storage_domain):
|
||||
attached_sd_service = self._attached_sd_service(storage_domain)
|
||||
attached_sd = get_entity(attached_sd_service)
|
||||
|
||||
if attached_sd and attached_sd.status == sdstate.MAINTENANCE:
|
||||
if not self._module.check_mode:
|
||||
# Detach the storage domain:
|
||||
attached_sd_service.remove()
|
||||
self.changed = True
|
||||
# Wait until storage domain is detached:
|
||||
wait(
|
||||
service=attached_sd_service,
|
||||
condition=lambda sd: sd is None,
|
||||
wait=self.param('wait'),
|
||||
timeout=self.param('timeout'),
|
||||
)
|
||||
|
||||
def pre_remove(self, storage_domain):
|
||||
# In case the user chose to destroy the storage domain there is no need to
|
||||
# move it to maintenance or detach it, it should simply be removed from the DB.
|
||||
# Also if storage domain in already unattached skip this step.
|
||||
if storage_domain.status == sdstate.UNATTACHED or self.param('destroy'):
|
||||
return
|
||||
# Before removing storage domain we need to put it into maintenance state:
|
||||
self._maintenance(storage_domain)
|
||||
|
||||
# Before removing storage domain we need to detach it from data center:
|
||||
self._unattach(storage_domain)
|
||||
|
||||
def post_create_check(self, sd_id):
|
||||
storage_domain = self._service.service(sd_id).get()
|
||||
dc_name = self.param('data_center')
|
||||
if not dc_name:
|
||||
# Find the DC, where the storage resides:
|
||||
dc_name = self._find_attached_datacenter_name(storage_domain.name)
|
||||
self._service = self._attached_sds_service(dc_name)
|
||||
|
||||
# If storage domain isn't attached, attach it:
|
||||
attached_sd_service = self._service.service(storage_domain.id)
|
||||
if get_entity(attached_sd_service) is None:
|
||||
self._service.add(
|
||||
otypes.StorageDomain(
|
||||
id=storage_domain.id,
|
||||
),
|
||||
)
|
||||
self.changed = True
|
||||
# Wait until storage domain is in maintenance:
|
||||
wait(
|
||||
service=attached_sd_service,
|
||||
condition=lambda sd: sd.status == sdstate.ACTIVE,
|
||||
wait=self.param('wait'),
|
||||
timeout=self.param('timeout'),
|
||||
)
|
||||
|
||||
def unattached_pre_action(self, storage_domain):
|
||||
dc_name = self.param('data_center')
|
||||
if not dc_name:
|
||||
# Find the DC, where the storage resides:
|
||||
dc_name = self._find_attached_datacenter_name(storage_domain.name)
|
||||
self._service = self._attached_sds_service(dc_name)
|
||||
self._maintenance(storage_domain)
|
||||
|
||||
def update_check(self, entity):
|
||||
return (
|
||||
equal(self.param('comment'), entity.comment) and
|
||||
equal(self.param('description'), entity.description) and
|
||||
equal(self.param('backup'), entity.backup) and
|
||||
equal(self.param('critical_space_action_blocker'), entity.critical_space_action_blocker) and
|
||||
equal(self.param('discard_after_delete'), entity.discard_after_delete) and
|
||||
equal(self.param('wipe_after_delete'), entity.wipe_after_delete) and
|
||||
equal(self.param('warning_low_space_indicator'), entity.warning_low_space_indicator)
|
||||
)
|
||||
|
||||
|
||||
def failed_state(sd):
|
||||
return sd.status in [sdstate.UNKNOWN, sdstate.INACTIVE]
|
||||
|
||||
|
||||
def control_state(sd_module):
|
||||
sd = sd_module.search_entity()
|
||||
if sd is None:
|
||||
return
|
||||
|
||||
sd_service = sd_module._service.service(sd.id)
|
||||
|
||||
# In the case of no status returned, it's an attached storage domain.
|
||||
# Redetermine the corresponding service and entity:
|
||||
if sd.status is None:
|
||||
sd_service = sd_module._attached_sd_service(sd)
|
||||
sd = get_entity(sd_service)
|
||||
|
||||
if sd.status == sdstate.LOCKED:
|
||||
wait(
|
||||
service=sd_service,
|
||||
condition=lambda sd: sd.status != sdstate.LOCKED,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
|
||||
if failed_state(sd):
|
||||
raise Exception("Not possible to manage storage domain '%s'." % sd.name)
|
||||
elif sd.status == sdstate.ACTIVATING:
|
||||
wait(
|
||||
service=sd_service,
|
||||
condition=lambda sd: sd.status == sdstate.ACTIVE,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
elif sd.status == sdstate.DETACHING:
|
||||
wait(
|
||||
service=sd_service,
|
||||
condition=lambda sd: sd.status == sdstate.UNATTACHED,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
elif sd.status == sdstate.PREPARING_FOR_MAINTENANCE:
|
||||
wait(
|
||||
service=sd_service,
|
||||
condition=lambda sd: sd.status == sdstate.MAINTENANCE,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent', 'maintenance', 'unattached', 'imported', 'update_ovf_store'],
|
||||
default='present',
|
||||
),
|
||||
id=dict(default=None),
|
||||
name=dict(default=None),
|
||||
description=dict(default=None),
|
||||
comment=dict(default=None),
|
||||
data_center=dict(default=None),
|
||||
domain_function=dict(choices=['data', 'iso', 'export'], default='data', aliases=['type']),
|
||||
host=dict(default=None),
|
||||
localfs=dict(default=None, type='dict'),
|
||||
nfs=dict(default=None, type='dict'),
|
||||
iscsi=dict(default=None, type='dict'),
|
||||
managed_block_storage=dict(default=None, type='dict', options=dict(
|
||||
driver_options=dict(type='list'),
|
||||
driver_sensitive_options=dict(type='list', no_log=True))),
|
||||
posixfs=dict(default=None, type='dict'),
|
||||
glusterfs=dict(default=None, type='dict'),
|
||||
fcp=dict(default=None, type='dict'),
|
||||
wipe_after_delete=dict(type='bool', default=None),
|
||||
backup=dict(type='bool', default=None),
|
||||
critical_space_action_blocker=dict(type='int', default=None),
|
||||
warning_low_space=dict(type='int', default=None),
|
||||
destroy=dict(type='bool', default=None),
|
||||
format=dict(type='bool', default=None),
|
||||
discard_after_delete=dict(type='bool', default=None)
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
storage_domains_service = connection.system_service().storage_domains_service()
|
||||
storage_domains_module = StorageDomainModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=storage_domains_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
control_state(storage_domains_module)
|
||||
if state == 'absent':
|
||||
# Pick random available host when host parameter is missing
|
||||
host_param = module.params['host']
|
||||
if not host_param:
|
||||
host = search_by_attributes(connection.system_service().hosts_service(), status='up')
|
||||
if host is None:
|
||||
raise Exception(
|
||||
"Not possible to remove storage domain '%s' "
|
||||
"because no host found with status `up`." % module.params['name']
|
||||
)
|
||||
host_param = host.name
|
||||
ret = storage_domains_module.remove(
|
||||
destroy=module.params['destroy'],
|
||||
format=module.params['format'],
|
||||
host=host_param,
|
||||
)
|
||||
elif state == 'present' or state == 'imported':
|
||||
sd_id = storage_domains_module.create()['id']
|
||||
storage_domains_module.post_create_check(sd_id)
|
||||
ret = storage_domains_module.action(
|
||||
action='activate',
|
||||
action_condition=lambda s: s.status == sdstate.MAINTENANCE,
|
||||
wait_condition=lambda s: s.status == sdstate.ACTIVE,
|
||||
fail_condition=failed_state,
|
||||
search_params={'id': sd_id} if state == 'imported' else None
|
||||
)
|
||||
elif state == 'maintenance':
|
||||
sd_id = storage_domains_module.create()['id']
|
||||
storage_domains_module.post_create_check(sd_id)
|
||||
|
||||
ret = OvirtRetry.backoff(tries=5, delay=1, backoff=2)(
|
||||
storage_domains_module.action
|
||||
)(
|
||||
action='deactivate',
|
||||
action_condition=lambda s: s.status == sdstate.ACTIVE,
|
||||
wait_condition=lambda s: s.status == sdstate.MAINTENANCE,
|
||||
fail_condition=failed_state,
|
||||
)
|
||||
elif state == 'unattached':
|
||||
ret = storage_domains_module.create()
|
||||
storage_domains_module.pre_remove(
|
||||
storage_domain=storage_domains_service.service(ret['id']).get()
|
||||
)
|
||||
ret['changed'] = storage_domains_module.changed
|
||||
elif state == 'update_ovf_store':
|
||||
ret = storage_domains_module.action(
|
||||
action='update_ovf_store'
|
||||
)
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,120 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_storage_domain_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV storage domains
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV storage domains."
|
||||
- This module was called C(ovirt_storage_domain_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_storage_domain_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_storage_domains), which
|
||||
contains a list of storage domains. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search storage domain X from datacenter Y use following pattern:
|
||||
name=X and datacenter=Y"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all storage domains which names start with C(data) and
|
||||
# belong to data center C(west):
|
||||
- ovirt_storage_domain_info:
|
||||
pattern: name=data* and datacenter=west
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_storage_domains }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_storage_domains:
|
||||
description: "List of dictionaries describing the storage domains. Storage_domain attributes are mapped to dictionary keys,
|
||||
all storage domains attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/storage_domain."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_storage_domain_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_storage_domain_facts' module has been renamed to 'ovirt_storage_domain_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
storage_domains_service = connection.system_service().storage_domains_service()
|
||||
storage_domains = storage_domains_service.list(search=module.params['pattern'])
|
||||
result = dict(
|
||||
ovirt_storage_domains=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in storage_domains
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,138 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2017 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_storage_template_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV templates relate to a storage domain.
|
||||
author: "Maor Lipchuk (@machacekondra)"
|
||||
version_added: "2.4"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV templates relate to a storage domain."
|
||||
- This module was called C(ovirt_storage_template_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_storage_template_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_storage_templates), which
|
||||
contains a list of templates. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
unregistered:
|
||||
description:
|
||||
- "Flag which indicates whether to get unregistered templates which contain one or more
|
||||
disks which reside on a storage domain or diskless templates."
|
||||
type: bool
|
||||
default: false
|
||||
max:
|
||||
description:
|
||||
- "Sets the maximum number of templates to return. If not specified all the templates are returned."
|
||||
storage_domain:
|
||||
description:
|
||||
- "The storage domain name where the templates should be listed."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all Templates which relate to a storage domain and
|
||||
# are unregistered:
|
||||
- ovirt_storage_template_info:
|
||||
unregistered=True
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_storage_templates }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_storage_templates:
|
||||
description: "List of dictionaries describing the Templates. Template attributes are mapped to dictionary keys,
|
||||
all Templates attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/template."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
get_id_by_name
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
storage_domain=dict(default=None),
|
||||
max=dict(default=None, type='int'),
|
||||
unregistered=dict(default=False, type='bool'),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_storage_template_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_storage_template_facts' module has been renamed to 'ovirt_storage_template_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
storage_domains_service = connection.system_service().storage_domains_service()
|
||||
sd_id = get_id_by_name(storage_domains_service, module.params['storage_domain'])
|
||||
storage_domain_service = storage_domains_service.storage_domain_service(sd_id)
|
||||
templates_service = storage_domain_service.templates_service()
|
||||
|
||||
# Find the unregistered Template we want to register:
|
||||
if module.params.get('unregistered'):
|
||||
templates = templates_service.list(unregistered=True)
|
||||
else:
|
||||
templates = templates_service.list(max=module.params['max'])
|
||||
result = dict(
|
||||
ovirt_storage_templates=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in templates
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,138 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2017 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_storage_vm_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV virtual machines relate to a storage domain.
|
||||
author: "Maor Lipchuk (@machacekondra)"
|
||||
version_added: "2.4"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV virtual machines relate to a storage domain."
|
||||
- This module was called C(ovirt_storage_vm_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_storage_vm_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_storage_vms), which
|
||||
contains a list of virtual machines. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
unregistered:
|
||||
description:
|
||||
- "Flag which indicates whether to get unregistered virtual machines which contain one or more
|
||||
disks which reside on a storage domain or diskless virtual machines."
|
||||
type: bool
|
||||
default: false
|
||||
max:
|
||||
description:
|
||||
- "Sets the maximum number of virtual machines to return. If not specified all the virtual machines are returned."
|
||||
storage_domain:
|
||||
description:
|
||||
- "The storage domain name where the virtual machines should be listed."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all VMs which relate to a storage domain and
|
||||
# are unregistered:
|
||||
- ovirt_vms_info:
|
||||
unregistered=True
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_storage_vms }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_storage_vms:
|
||||
description: "List of dictionaries describing the VMs. VM attributes are mapped to dictionary keys,
|
||||
all VMs attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/vm."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
get_id_by_name
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
storage_domain=dict(default=None),
|
||||
max=dict(default=None, type='int'),
|
||||
unregistered=dict(default=False, type='bool'),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_storage_vm_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_storage_vm_facts' module has been renamed to 'ovirt_storage_vm_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
storage_domains_service = connection.system_service().storage_domains_service()
|
||||
sd_id = get_id_by_name(storage_domains_service, module.params['storage_domain'])
|
||||
storage_domain_service = storage_domains_service.storage_domain_service(sd_id)
|
||||
vms_service = storage_domain_service.vms_service()
|
||||
|
||||
# Find the unregistered VM we want to register:
|
||||
if module.params.get('unregistered'):
|
||||
vms = vms_service.list(unregistered=True)
|
||||
else:
|
||||
vms = vms_service.list()
|
||||
result = dict(
|
||||
ovirt_storage_vms=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in vms
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,257 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_tag
|
||||
short_description: Module to manage tags in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "This module manage tags in oVirt/RHV. It can also manage assignments
|
||||
of those tags to entities."
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the tag to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- "Name of the tag to manage."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the tag be present/absent/attached/detached."
|
||||
- "C(Note): I(attached) and I(detached) states are supported since version 2.4."
|
||||
choices: ['present', 'absent', 'attached', 'detached']
|
||||
default: present
|
||||
description:
|
||||
description:
|
||||
- "Description of the tag to manage."
|
||||
parent:
|
||||
description:
|
||||
- "Name of the parent tag."
|
||||
vms:
|
||||
description:
|
||||
- "List of the VMs names, which should have assigned this tag."
|
||||
hosts:
|
||||
description:
|
||||
- "List of the hosts names, which should have assigned this tag."
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Create(if not exists) and assign tag to vms vm1 and vm2:
|
||||
- ovirt_tag:
|
||||
name: mytag
|
||||
vms:
|
||||
- vm1
|
||||
- vm2
|
||||
|
||||
# Attach a tag to VM 'vm3', keeping the rest already attached tags on VM:
|
||||
- ovirt_tag:
|
||||
name: mytag
|
||||
state: attached
|
||||
vms:
|
||||
- vm3
|
||||
|
||||
# Detach a tag from VM 'vm3', keeping the rest already attached tags on VM:
|
||||
- ovirt_tag:
|
||||
name: mytag
|
||||
state: detached
|
||||
vms:
|
||||
- vm3
|
||||
|
||||
# To detach all VMs from tag:
|
||||
- ovirt_tag:
|
||||
name: mytag
|
||||
vms: []
|
||||
|
||||
# Remove tag
|
||||
- ovirt_tag:
|
||||
state: absent
|
||||
name: mytag
|
||||
|
||||
# Change Tag Name
|
||||
- ovirt_tag:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new_tag_name"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the tag which is managed
|
||||
returned: On success if tag is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
tag:
|
||||
description: "Dictionary of all the tag attributes. Tag attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/tag."
|
||||
returned: On success if tag is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_id_by_name,
|
||||
ovirt_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
class TagsModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.Tag(
|
||||
id=self._module.params['id'],
|
||||
name=self._module.params['name'],
|
||||
description=self._module.params['description'],
|
||||
parent=otypes.Tag(
|
||||
name=self._module.params['parent'],
|
||||
) if self._module.params['parent'] else None,
|
||||
)
|
||||
|
||||
def post_create(self, entity):
|
||||
self.update_check(entity)
|
||||
|
||||
def _update_tag_assignments(self, entity, name):
|
||||
if self._module.params[name] is None:
|
||||
return
|
||||
|
||||
state = self.param('state')
|
||||
entities_service = getattr(self._connection.system_service(), '%s_service' % name)()
|
||||
current_vms = [
|
||||
vm.name
|
||||
for vm in entities_service.list(search='tag=%s' % self._module.params['name'])
|
||||
]
|
||||
# Assign tags:
|
||||
if state in ['present', 'attached', 'detached']:
|
||||
for entity_name in self._module.params[name]:
|
||||
entity_id = get_id_by_name(entities_service, entity_name)
|
||||
tags_service = entities_service.service(entity_id).tags_service()
|
||||
current_tags = [tag.name for tag in tags_service.list()]
|
||||
# Assign the tag:
|
||||
if state in ['attached', 'present']:
|
||||
if self._module.params['name'] not in current_tags:
|
||||
if not self._module.check_mode:
|
||||
tags_service.add(
|
||||
tag=otypes.Tag(
|
||||
name=self._module.params['name'],
|
||||
),
|
||||
)
|
||||
self.changed = True
|
||||
# Detach the tag:
|
||||
elif state == 'detached':
|
||||
if self._module.params['name'] in current_tags:
|
||||
tag_id = get_id_by_name(tags_service, self.param('name'))
|
||||
if not self._module.check_mode:
|
||||
tags_service.tag_service(tag_id).remove()
|
||||
self.changed = True
|
||||
|
||||
# Unassign tags:
|
||||
if state == 'present':
|
||||
for entity_name in [e for e in current_vms if e not in self._module.params[name]]:
|
||||
if not self._module.check_mode:
|
||||
entity_id = get_id_by_name(entities_service, entity_name)
|
||||
tags_service = entities_service.service(entity_id).tags_service()
|
||||
tag_id = get_id_by_name(tags_service, self.param('name'))
|
||||
tags_service.tag_service(tag_id).remove()
|
||||
self.changed = True
|
||||
|
||||
def _get_parent(self, entity):
|
||||
parent = None
|
||||
if entity.parent:
|
||||
parent = self._connection.follow_link(entity.parent).name
|
||||
return parent
|
||||
|
||||
def update_check(self, entity):
|
||||
self._update_tag_assignments(entity, 'vms')
|
||||
self._update_tag_assignments(entity, 'hosts')
|
||||
return (
|
||||
equal(self._module.params.get('description'), entity.description) and
|
||||
equal(self._module.params.get('name'), entity.name) and
|
||||
equal(self._module.params.get('parent'), self._get_parent(entity))
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent', 'attached', 'detached'],
|
||||
default='present',
|
||||
),
|
||||
id=dict(default=None),
|
||||
name=dict(required=True),
|
||||
description=dict(default=None),
|
||||
parent=dict(default=None),
|
||||
vms=dict(default=None, type='list'),
|
||||
hosts=dict(default=None, type='list'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
tags_service = connection.system_service().tags_service()
|
||||
tags_module = TagsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=tags_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state in ['present', 'attached', 'detached']:
|
||||
ret = tags_module.create()
|
||||
elif state == 'absent':
|
||||
ret = tags_module.remove()
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,167 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_tag_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV tags
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV tags."
|
||||
- This module was called C(ovirt_tag_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_tag_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_tags), which
|
||||
contains a list of tags. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the tag which should be listed."
|
||||
vm:
|
||||
description:
|
||||
- "Name of the VM, which tags should be listed."
|
||||
host:
|
||||
description:
|
||||
- "Name of the host, which tags should be listed."
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all tags, which names start with C(tag):
|
||||
- ovirt_tag_info:
|
||||
name: tag*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_tags }}"
|
||||
|
||||
# Gather information about all tags, which are assigned to VM C(postgres):
|
||||
- ovirt_tag_info:
|
||||
vm: postgres
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_tags }}"
|
||||
|
||||
# Gather information about all tags, which are assigned to host C(west):
|
||||
- ovirt_tag_info:
|
||||
host: west
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_tags }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_tags:
|
||||
description: "List of dictionaries describing the tags. Tags attributes are mapped to dictionary keys,
|
||||
all tags attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/tag."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import fnmatch
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
name=dict(default=None),
|
||||
host=dict(default=None),
|
||||
vm=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_tag_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_tag_facts' module has been renamed to 'ovirt_tag_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
tags_service = connection.system_service().tags_service()
|
||||
tags = []
|
||||
all_tags = tags_service.list()
|
||||
if module.params['name']:
|
||||
tags.extend([
|
||||
t for t in all_tags
|
||||
if fnmatch.fnmatch(t.name, module.params['name'])
|
||||
])
|
||||
if module.params['host']:
|
||||
hosts_service = connection.system_service().hosts_service()
|
||||
host = search_by_name(hosts_service, module.params['host'])
|
||||
if host is None:
|
||||
raise Exception("Host '%s' was not found." % module.params['host'])
|
||||
tags.extend([
|
||||
tag for tag in hosts_service.host_service(host.id).tags_service().list()
|
||||
])
|
||||
if module.params['vm']:
|
||||
vms_service = connection.system_service().vms_service()
|
||||
vm = search_by_name(vms_service, module.params['vm'])
|
||||
if vm is None:
|
||||
raise Exception("Vm '%s' was not found." % module.params['vm'])
|
||||
tags.extend([
|
||||
tag for tag in vms_service.vm_service(vm.id).tags_service().list()
|
||||
])
|
||||
|
||||
if not (module.params['vm'] or module.params['host'] or module.params['name']):
|
||||
tags = all_tags
|
||||
|
||||
result = dict(
|
||||
ovirt_tags=[
|
||||
get_dict_of_struct(
|
||||
struct=t,
|
||||
connection=connection,
|
||||
fetch_nested=module.params['fetch_nested'],
|
||||
attributes=module.params['nested_attributes'],
|
||||
) for t in tags
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
File diff suppressed because it is too large
Load Diff
@ -1,120 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_template_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV templates
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV templates."
|
||||
- This module was called C(ovirt_template_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_template_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_templates), which
|
||||
contains a list of templates. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search template X from datacenter Y use following pattern:
|
||||
name=X and datacenter=Y"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all templates which names start with C(centos) and
|
||||
# belongs to data center C(west):
|
||||
- ovirt_template_info:
|
||||
pattern: name=centos* and datacenter=west
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_templates }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_templates:
|
||||
description: "List of dictionaries describing the templates. Template attributes are mapped to dictionary keys,
|
||||
all templates attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/template."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_template_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_template_facts' module has been renamed to 'ovirt_template_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
templates_service = connection.system_service().templates_service()
|
||||
templates = templates_service.list(search=module.params['pattern'])
|
||||
result = dict(
|
||||
ovirt_templates=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in templates
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,176 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_user
|
||||
short_description: Module to manage users in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage users in oVirt/RHV."
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "Name of the user to manage. In most LDAPs it's I(uid) of the user, but in Active Directory you must specify I(UPN) of the user."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the user be present/absent."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
authz_name:
|
||||
description:
|
||||
- "Authorization provider of the user. In previous versions of oVirt/RHV known as domain."
|
||||
required: true
|
||||
aliases: ['domain']
|
||||
namespace:
|
||||
description:
|
||||
- "Namespace where the user resides. When using the authorization provider that stores users in the LDAP server,
|
||||
this attribute equals the naming context of the LDAP server."
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Add user user1 from authorization provider example.com-authz
|
||||
- ovirt_user:
|
||||
name: user1
|
||||
domain: example.com-authz
|
||||
|
||||
# Add user user1 from authorization provider example.com-authz
|
||||
# In case of Active Directory specify UPN:
|
||||
- ovirt_user:
|
||||
name: user1@ad2.example.com
|
||||
domain: example.com-authz
|
||||
|
||||
# Remove user user1 with authorization provider example.com-authz
|
||||
- ovirt_user:
|
||||
state: absent
|
||||
name: user1
|
||||
authz_name: example.com-authz
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the user which is managed
|
||||
returned: On success if user is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
user:
|
||||
description: "Dictionary of all the user attributes. User attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/user."
|
||||
returned: On success if user is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
check_params,
|
||||
create_connection,
|
||||
ovirt_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def username(module):
|
||||
return '{0}@{1}'.format(module.params['name'], module.params['authz_name'])
|
||||
|
||||
|
||||
class UsersModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.User(
|
||||
domain=otypes.Domain(
|
||||
name=self._module.params['authz_name']
|
||||
),
|
||||
user_name=username(self._module),
|
||||
principal=self._module.params['name'],
|
||||
namespace=self._module.params['namespace'],
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(required=True),
|
||||
authz_name=dict(required=True, aliases=['domain']),
|
||||
namespace=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
users_service = connection.system_service().users_service()
|
||||
users_module = UsersModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=users_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = users_module.create(
|
||||
search_params={
|
||||
'usrname': username(module),
|
||||
}
|
||||
)
|
||||
elif state == 'absent':
|
||||
ret = users_module.remove(
|
||||
search_params={
|
||||
'usrname': username(module),
|
||||
}
|
||||
)
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,118 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_user_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV users
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV users."
|
||||
- This module was called C(ovirt_user_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_user_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_users), which
|
||||
contains a list of users. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search user X use following pattern: name=X"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all users which first names start with C(john):
|
||||
- ovirt_user_info:
|
||||
pattern: name=john*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_users }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_users:
|
||||
description: "List of dictionaries describing the users. User attributes are mapped to dictionary keys,
|
||||
all users attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/user."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_user_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_user_facts' module has been renamed to 'ovirt_user_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
users_service = connection.system_service().users_service()
|
||||
users = users_service.list(search=module.params['pattern'])
|
||||
result = dict(
|
||||
ovirt_users=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in users
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
File diff suppressed because it is too large
Load Diff
@ -1,160 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_vm_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV virtual machines
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV virtual machines."
|
||||
- This module was called C(ovirt_vm_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_vm_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_vms), which
|
||||
contains a list of virtual machines. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search VM X from cluster Y use following pattern:
|
||||
name=X and cluster=Y"
|
||||
all_content:
|
||||
description:
|
||||
- "If I(true) all the attributes of the virtual machines should be
|
||||
included in the response."
|
||||
type: bool
|
||||
case_sensitive:
|
||||
description:
|
||||
- "If I(true) performed search will take case into account."
|
||||
type: bool
|
||||
default: true
|
||||
max:
|
||||
description:
|
||||
- "The maximum number of results to return."
|
||||
next_run:
|
||||
description:
|
||||
- "Indicates if the returned result describes the virtual machine as it is currently running or if describes
|
||||
the virtual machine with the modifications that have already been performed but that will only come into
|
||||
effect when the virtual machine is restarted. By default the value is set by engine."
|
||||
type: bool
|
||||
version_added: "2.8"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all VMs which names start with C(centos) and
|
||||
# belong to cluster C(west):
|
||||
- ovirt_vm_info:
|
||||
pattern: name=centos* and cluster=west
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_vms }}"
|
||||
|
||||
# Gather info about next run configuration of virtual machine named myvm
|
||||
- ovirt_vm_info:
|
||||
pattern: name=myvm
|
||||
next_run: true
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_vms[0] }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_vms:
|
||||
description: "List of dictionaries describing the VMs. VM attributes are mapped to dictionary keys,
|
||||
all VMs attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/vm."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
all_content=dict(default=False, type='bool'),
|
||||
next_run=dict(default=None, type='bool'),
|
||||
case_sensitive=dict(default=True, type='bool'),
|
||||
max=dict(default=None, type='int'),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_vm_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_vm_facts' module has been renamed to 'ovirt_vm_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
vms_service = connection.system_service().vms_service()
|
||||
vms = vms_service.list(
|
||||
search=module.params['pattern'],
|
||||
all_content=module.params['all_content'],
|
||||
case_sensitive=module.params['case_sensitive'],
|
||||
max=module.params['max'],
|
||||
)
|
||||
if module.params['next_run']:
|
||||
vms = [vms_service.vm_service(vm.id).get(next_run=True) for vm in vms]
|
||||
|
||||
result = dict(
|
||||
ovirt_vms=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in vms
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,485 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_vmpool
|
||||
short_description: Module to manage VM pools in oVirt/RHV
|
||||
version_added: "2.3"
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
description:
|
||||
- "Module to manage VM pools in oVirt/RHV."
|
||||
options:
|
||||
id:
|
||||
description:
|
||||
- "ID of the vmpool to manage."
|
||||
version_added: "2.8"
|
||||
name:
|
||||
description:
|
||||
- "Name of the VM pool to manage."
|
||||
required: true
|
||||
comment:
|
||||
description:
|
||||
- Comment of the Virtual Machine pool.
|
||||
state:
|
||||
description:
|
||||
- "Should the VM pool be present/absent."
|
||||
- "Note that when C(state) is I(absent) all VMs in VM pool are stopped and removed."
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
template:
|
||||
description:
|
||||
- "Name of the template, which will be used to create VM pool."
|
||||
description:
|
||||
description:
|
||||
- "Description of the VM pool."
|
||||
cluster:
|
||||
description:
|
||||
- "Name of the cluster, where VM pool should be created."
|
||||
type:
|
||||
description:
|
||||
- "Type of the VM pool. Either manual or automatic."
|
||||
- "C(manual) - The administrator is responsible for explicitly returning the virtual machine to the pool.
|
||||
The virtual machine reverts to the original base image after the administrator returns it to the pool."
|
||||
- "C(Automatic) - When the virtual machine is shut down, it automatically reverts to its base image and
|
||||
is returned to the virtual machine pool."
|
||||
- "Default value is set by engine."
|
||||
choices: ['manual', 'automatic']
|
||||
vm_per_user:
|
||||
description:
|
||||
- "Maximum number of VMs a single user can attach to from this pool."
|
||||
- "Default value is set by engine."
|
||||
prestarted:
|
||||
description:
|
||||
- "Number of pre-started VMs defines the number of VMs in run state, that are waiting
|
||||
to be attached to Users."
|
||||
- "Default value is set by engine."
|
||||
vm_count:
|
||||
description:
|
||||
- "Number of VMs in the pool."
|
||||
- "Default value is set by engine."
|
||||
vm:
|
||||
description:
|
||||
- "For creating vm pool without editing template."
|
||||
- "Note: You can use C(vm) only for creating vm pool."
|
||||
type: dict
|
||||
suboptions:
|
||||
comment:
|
||||
description:
|
||||
- Comment of the Virtual Machine.
|
||||
timezone:
|
||||
description:
|
||||
- Sets time zone offset of the guest hardware clock.
|
||||
- For example C(Etc/GMT)
|
||||
memory:
|
||||
description:
|
||||
- Amount of memory of the Virtual Machine. Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB).
|
||||
- Default value is set by engine.
|
||||
memory_guaranteed:
|
||||
description:
|
||||
- Amount of minimal guaranteed memory of the Virtual Machine.
|
||||
Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB).
|
||||
- C(memory_guaranteed) parameter can't be lower than C(memory) parameter.
|
||||
- Default value is set by engine.
|
||||
memory_max:
|
||||
description:
|
||||
- Upper bound of virtual machine memory up to which memory hot-plug can be performed.
|
||||
Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB).
|
||||
- Default value is set by engine.
|
||||
cloud_init:
|
||||
description:
|
||||
- Dictionary with values for Unix-like Virtual Machine initialization using cloud init.
|
||||
- C(host_name) - Hostname to be set to Virtual Machine when deployed.
|
||||
- C(timezone) - Timezone to be set to Virtual Machine when deployed.
|
||||
- C(user_name) - Username to be used to set password to Virtual Machine when deployed.
|
||||
- C(root_password) - Password to be set for user specified by C(user_name) parameter.
|
||||
- C(authorized_ssh_keys) - Use this SSH keys to login to Virtual Machine.
|
||||
- C(regenerate_ssh_keys) - If I(True) SSH keys will be regenerated on Virtual Machine.
|
||||
- C(custom_script) - Cloud-init script which will be executed on Virtual Machine when deployed. This is appended to the end of the
|
||||
cloud-init script generated by any other options.
|
||||
- C(dns_servers) - DNS servers to be configured on Virtual Machine.
|
||||
- C(dns_search) - DNS search domains to be configured on Virtual Machine.
|
||||
- C(nic_boot_protocol) - Set boot protocol of the network interface of Virtual Machine. Can be one of C(none), C(dhcp) or C(static).
|
||||
- C(nic_ip_address) - If boot protocol is static, set this IP address to network interface of Virtual Machine.
|
||||
- C(nic_netmask) - If boot protocol is static, set this netmask to network interface of Virtual Machine.
|
||||
- C(nic_gateway) - If boot protocol is static, set this gateway to network interface of Virtual Machine.
|
||||
- C(nic_name) - Set name to network interface of Virtual Machine.
|
||||
- C(nic_on_boot) - If I(True) network interface will be set to start on boot.
|
||||
sso:
|
||||
description:
|
||||
- "I(True) enable Single Sign On by Guest Agent, I(False) to disable it. By default is chosen by oVirt/RHV engine."
|
||||
type: bool
|
||||
smartcard_enabled:
|
||||
description:
|
||||
- "If I(true), use smart card authentication."
|
||||
type: bool
|
||||
nics:
|
||||
description:
|
||||
- List of NICs, which should be attached to Virtual Machine. NIC is described by following dictionary.
|
||||
- C(name) - Name of the NIC.
|
||||
- C(profile_name) - Profile name where NIC should be attached.
|
||||
- C(interface) - Type of the network interface. One of following I(virtio), I(e1000), I(rtl8139), default is I(virtio).
|
||||
- C(mac_address) - Custom MAC address of the network interface, by default it's obtained from MAC pool.
|
||||
- NOTE - This parameter is used only when C(state) is I(running) or I(present) and is able to only create NICs.
|
||||
- To manage NICs of the VM in more depth please use M(ovirt_nics) module instead.
|
||||
version_added: "2.9"
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
- name: Create VM pool from template
|
||||
ovirt_vmpool:
|
||||
cluster: mycluster
|
||||
name: myvmpool
|
||||
template: rhel7
|
||||
vm_count: 2
|
||||
prestarted: 2
|
||||
vm_per_user: 1
|
||||
|
||||
- name: Remove vmpool, note that all VMs in pool will be stopped and removed
|
||||
ovirt_vmpool:
|
||||
state: absent
|
||||
name: myvmpool
|
||||
|
||||
- name: Change Pool Name
|
||||
ovirt_vmpool:
|
||||
id: 00000000-0000-0000-0000-000000000000
|
||||
name: "new_pool_name"
|
||||
|
||||
- name: Create vm pool and override the pool values
|
||||
ovirt_vmpool:
|
||||
cluster: mycluster
|
||||
name: vmpool
|
||||
template: blank
|
||||
vm_count: 2
|
||||
prestarted: 1
|
||||
vm_per_user: 1
|
||||
vm:
|
||||
memory: 4GiB
|
||||
memory_guaranteed: 4GiB
|
||||
memory_max: 10GiB
|
||||
comment: vncomment
|
||||
cloud_init:
|
||||
nic_boot_protocol: static
|
||||
nic_ip_address: 10.34.60.86
|
||||
nic_netmask: 255.255.252.0
|
||||
nic_gateway: 10.34.63.254
|
||||
nic_name: eth1
|
||||
nic_on_boot: true
|
||||
host_name: example.com
|
||||
custom_script: |
|
||||
write_files:
|
||||
- content: |
|
||||
Hello, world!
|
||||
path: /tmp/greeting.txt
|
||||
permissions: '0644'
|
||||
user_name: root
|
||||
root_password: super_password
|
||||
nics:
|
||||
- name: nicname
|
||||
interface: virtio
|
||||
profile_name: network
|
||||
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the VM pool which is managed
|
||||
returned: On success if VM pool is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
vm_pool:
|
||||
description: "Dictionary of all the VM pool attributes. VM pool attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/vm_pool."
|
||||
returned: On success if VM pool is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_params,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_link_name,
|
||||
ovirt_full_argument_spec,
|
||||
wait,
|
||||
convert_to_bytes,
|
||||
search_by_name,
|
||||
)
|
||||
|
||||
|
||||
class VmPoolsModule(BaseModule):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(VmPoolsModule, self).__init__(*args, **kwargs)
|
||||
self._initialization = None
|
||||
|
||||
def build_entity(self):
|
||||
vm = self.param('vm')
|
||||
return otypes.VmPool(
|
||||
id=self._module.params['id'],
|
||||
name=self._module.params['name'],
|
||||
description=self._module.params['description'],
|
||||
comment=self._module.params['comment'],
|
||||
cluster=otypes.Cluster(
|
||||
name=self._module.params['cluster']
|
||||
) if self._module.params['cluster'] else None,
|
||||
template=otypes.Template(
|
||||
name=self._module.params['template']
|
||||
) if self._module.params['template'] else None,
|
||||
max_user_vms=self._module.params['vm_per_user'],
|
||||
prestarted_vms=self._module.params['prestarted'],
|
||||
size=self._module.params['vm_count'],
|
||||
type=otypes.VmPoolType(
|
||||
self._module.params['type']
|
||||
) if self._module.params['type'] else None,
|
||||
vm=self.build_vm(vm) if self._module.params['vm'] else None,
|
||||
)
|
||||
|
||||
def build_vm(self, vm):
|
||||
return otypes.Vm(
|
||||
comment=vm.get('comment'),
|
||||
memory=convert_to_bytes(
|
||||
vm.get('memory')
|
||||
) if vm.get('memory') else None,
|
||||
memory_policy=otypes.MemoryPolicy(
|
||||
guaranteed=convert_to_bytes(vm.get('memory_guaranteed')),
|
||||
max=convert_to_bytes(vm.get('memory_max')),
|
||||
) if any((
|
||||
vm.get('memory_guaranteed'),
|
||||
vm.get('memory_max')
|
||||
)) else None,
|
||||
initialization=self.get_initialization(vm),
|
||||
display=otypes.Display(
|
||||
smartcard_enabled=vm.get('smartcard_enabled')
|
||||
) if vm.get('smartcard_enabled') is not None else None,
|
||||
sso=(
|
||||
otypes.Sso(
|
||||
methods=[otypes.Method(id=otypes.SsoMethod.GUEST_AGENT)] if vm.get('sso') else []
|
||||
)
|
||||
) if vm.get('sso') is not None else None,
|
||||
time_zone=otypes.TimeZone(
|
||||
name=vm.get('timezone'),
|
||||
) if vm.get('timezone') else None,
|
||||
)
|
||||
|
||||
def get_initialization(self, vm):
|
||||
if self._initialization is not None:
|
||||
return self._initialization
|
||||
|
||||
sysprep = vm.get('sysprep')
|
||||
cloud_init = vm.get('cloud_init')
|
||||
cloud_init_nics = vm.get('cloud_init_nics') or []
|
||||
if cloud_init is not None:
|
||||
cloud_init_nics.append(cloud_init)
|
||||
|
||||
if cloud_init or cloud_init_nics:
|
||||
self._initialization = otypes.Initialization(
|
||||
nic_configurations=[
|
||||
otypes.NicConfiguration(
|
||||
boot_protocol=otypes.BootProtocol(
|
||||
nic.pop('nic_boot_protocol').lower()
|
||||
) if nic.get('nic_boot_protocol') else None,
|
||||
name=nic.pop('nic_name', None),
|
||||
on_boot=nic.pop('nic_on_boot', None),
|
||||
ip=otypes.Ip(
|
||||
address=nic.pop('nic_ip_address', None),
|
||||
netmask=nic.pop('nic_netmask', None),
|
||||
gateway=nic.pop('nic_gateway', None),
|
||||
) if (
|
||||
nic.get('nic_gateway') is not None or
|
||||
nic.get('nic_netmask') is not None or
|
||||
nic.get('nic_ip_address') is not None
|
||||
) else None,
|
||||
)
|
||||
for nic in cloud_init_nics
|
||||
if (
|
||||
nic.get('nic_gateway') is not None or
|
||||
nic.get('nic_netmask') is not None or
|
||||
nic.get('nic_ip_address') is not None or
|
||||
nic.get('nic_boot_protocol') is not None or
|
||||
nic.get('nic_on_boot') is not None
|
||||
)
|
||||
] if cloud_init_nics else None,
|
||||
**cloud_init
|
||||
)
|
||||
elif sysprep:
|
||||
self._initialization = otypes.Initialization(
|
||||
**sysprep
|
||||
)
|
||||
return self._initialization
|
||||
|
||||
def get_vms(self, entity):
|
||||
vms = self._connection.system_service().vms_service().list()
|
||||
resp = []
|
||||
for vm in vms:
|
||||
if vm.vm_pool is not None and vm.vm_pool.id == entity.id:
|
||||
resp.append(vm)
|
||||
return resp
|
||||
|
||||
def post_create(self, entity):
|
||||
vm_param = self.param('vm')
|
||||
if vm_param is not None and vm_param.get('nics') is not None:
|
||||
vms = self.get_vms(entity)
|
||||
for vm in vms:
|
||||
self.__attach_nics(vm, vm_param)
|
||||
|
||||
def __attach_nics(self, entity, vm_param):
|
||||
# Attach NICs to VM, if specified:
|
||||
vms_service = self._connection.system_service().vms_service()
|
||||
nics_service = vms_service.service(entity.id).nics_service()
|
||||
for nic in vm_param.get('nics'):
|
||||
if search_by_name(nics_service, nic.get('name')) is None:
|
||||
if not self._module.check_mode:
|
||||
nics_service.add(
|
||||
otypes.Nic(
|
||||
name=nic.get('name'),
|
||||
interface=otypes.NicInterface(
|
||||
nic.get('interface', 'virtio')
|
||||
),
|
||||
vnic_profile=otypes.VnicProfile(
|
||||
id=self.__get_vnic_profile_id(nic),
|
||||
) if nic.get('profile_name') else None,
|
||||
mac=otypes.Mac(
|
||||
address=nic.get('mac_address')
|
||||
) if nic.get('mac_address') else None,
|
||||
)
|
||||
)
|
||||
self.changed = True
|
||||
|
||||
def __get_vnic_profile_id(self, nic):
|
||||
"""
|
||||
Return VNIC profile ID looked up by it's name, because there can be
|
||||
more VNIC profiles with same name, other criteria of filter is cluster.
|
||||
"""
|
||||
vnics_service = self._connection.system_service().vnic_profiles_service()
|
||||
clusters_service = self._connection.system_service().clusters_service()
|
||||
cluster = search_by_name(clusters_service, self.param('cluster'))
|
||||
profiles = [
|
||||
profile for profile in vnics_service.list()
|
||||
if profile.name == nic.get('profile_name')
|
||||
]
|
||||
cluster_networks = [
|
||||
net.id for net in self._connection.follow_link(cluster.networks)
|
||||
]
|
||||
try:
|
||||
return next(
|
||||
profile.id for profile in profiles
|
||||
if profile.network.id in cluster_networks
|
||||
)
|
||||
except StopIteration:
|
||||
raise Exception(
|
||||
"Profile '%s' was not found in cluster '%s'" % (
|
||||
nic.get('profile_name'),
|
||||
self.param('cluster')
|
||||
)
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
return (
|
||||
equal(self._module.params.get('name'), entity.name) and
|
||||
equal(self._module.params.get('cluster'), get_link_name(self._connection, entity.cluster)) and
|
||||
equal(self._module.params.get('description'), entity.description) and
|
||||
equal(self._module.params.get('comment'), entity.comment) and
|
||||
equal(self._module.params.get('vm_per_user'), entity.max_user_vms) and
|
||||
equal(self._module.params.get('prestarted'), entity.prestarted_vms) and
|
||||
equal(self._module.params.get('vm_count'), entity.size)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
id=dict(default=None),
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(required=True),
|
||||
template=dict(default=None),
|
||||
cluster=dict(default=None),
|
||||
description=dict(default=None),
|
||||
vm=dict(default=None, type='dict'),
|
||||
comment=dict(default=None),
|
||||
vm_per_user=dict(default=None, type='int'),
|
||||
prestarted=dict(default=None, type='int'),
|
||||
vm_count=dict(default=None, type='int'),
|
||||
type=dict(default=None, choices=['automatic', 'manual']),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
vm_pools_service = connection.system_service().vm_pools_service()
|
||||
vm_pools_module = VmPoolsModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=vm_pools_service,
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = vm_pools_module.create()
|
||||
|
||||
# Wait for all VM pool VMs to be created:
|
||||
if module.params['wait']:
|
||||
vms_service = connection.system_service().vms_service()
|
||||
for vm in vms_service.list(search='pool=%s' % module.params['name']):
|
||||
wait(
|
||||
service=vms_service.service(vm.id),
|
||||
condition=lambda vm: vm.status in [otypes.VmStatus.DOWN, otypes.VmStatus.UP],
|
||||
timeout=module.params['timeout'],
|
||||
)
|
||||
|
||||
elif state == 'absent':
|
||||
ret = vm_pools_module.remove()
|
||||
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,118 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_vmpool_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV vmpools
|
||||
author: "Ondra Machacek (@machacekondra)"
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV vmpools."
|
||||
- This module was called C(ovirt_vmpool_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(ovirt_vmpool_info) module no longer returns C(ansible_facts)!
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_vmpools), which
|
||||
contains a list of vmpools. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
pattern:
|
||||
description:
|
||||
- "Search term which is accepted by oVirt/RHV search backend."
|
||||
- "For example to search vmpool X: name=X"
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information about all vm pools which names start with C(centos):
|
||||
- ovirt_vmpool_info:
|
||||
pattern: name=centos*
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_vm_pools }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_vm_pools:
|
||||
description: "List of dictionaries describing the vmpools. Vm pool attributes are mapped to dictionary keys,
|
||||
all vmpools attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/vm_pool."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
pattern=dict(default='', required=False),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
is_old_facts = module._name == 'ovirt_vmpool_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'ovirt_vmpool_facts' module has been renamed to 'ovirt_vmpool_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
vmpools_service = connection.system_service().vm_pools_service()
|
||||
vmpools = vmpools_service.list(search=module.params['pattern'])
|
||||
result = dict(
|
||||
ovirt_vm_pools=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in vmpools
|
||||
],
|
||||
)
|
||||
if is_old_facts:
|
||||
module.exit_json(changed=False, ansible_facts=result)
|
||||
else:
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,321 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_vnic_profile
|
||||
short_description: Module to manage vNIC profile of network in oVirt/RHV
|
||||
version_added: "2.8"
|
||||
author:
|
||||
- "Ondra Machacek (@machacekondra)"
|
||||
- "Martin Necas (@mnecas)"
|
||||
description:
|
||||
- "Module to manage vNIC profile of network in oVirt/RHV"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- "A human-readable name in plain text."
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- "Should the vNIC be absent/present."
|
||||
choices: ['absent', 'present']
|
||||
default: present
|
||||
description:
|
||||
description:
|
||||
- "A human-readable description in plain text."
|
||||
data_center:
|
||||
description:
|
||||
- "Datacenter name where network reside."
|
||||
required: true
|
||||
network:
|
||||
description:
|
||||
- "Name of network to which is vNIC attached."
|
||||
required: true
|
||||
network_filter:
|
||||
description:
|
||||
- "The network filter enables to filter packets send to/from the VM's nic according to defined rules."
|
||||
custom_properties:
|
||||
description:
|
||||
- "Custom properties applied to the vNIC profile."
|
||||
- "Custom properties is a list of dictionary which can have following values:"
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- "Name of the custom property. For example: I(hugepages), I(vhost), I(sap_agent), etc."
|
||||
regexp:
|
||||
description:
|
||||
- Regular expression to set for custom property.
|
||||
value:
|
||||
description:
|
||||
- Value to set for custom property.
|
||||
qos:
|
||||
description:
|
||||
- "Quality of Service attributes regulate inbound and outbound network traffic of the NIC."
|
||||
port_mirroring:
|
||||
description:
|
||||
- "Enables port mirroring."
|
||||
type: bool
|
||||
pass_through:
|
||||
description:
|
||||
- "Enables passthrough to an SR-IOV-enabled host NIC."
|
||||
- "When enabled C(qos) and C(network_filter) are automatically set to None and C(port_mirroring) to False."
|
||||
- "When enabled and C(migratable) not specified then C(migratable) is enabled."
|
||||
- "Port mirroring, QoS and network filters are not supported on passthrough profiles."
|
||||
choices: ['disabled', 'enabled']
|
||||
migratable:
|
||||
description:
|
||||
- "Marks whether pass_through NIC is migratable or not."
|
||||
type: bool
|
||||
extends_documentation_fragment: ovirt
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
- name: Add vNIC
|
||||
ovirt_vnic_profile:
|
||||
name: myvnic
|
||||
network: mynetwork
|
||||
state: present
|
||||
data_center: datacenter
|
||||
|
||||
- name: Editing vNICs network_filter, custom_properties, qos
|
||||
ovirt_vnic_profile:
|
||||
name: myvnic
|
||||
network: mynetwork
|
||||
data_center: datacenter
|
||||
qos: myqos
|
||||
custom_properties:
|
||||
- name: SecurityGroups
|
||||
value: 9bd9bde9-39da-44a8-9541-aa39e1a81c9d
|
||||
network_filter: allow-dhcp
|
||||
|
||||
- name: Remove vNICs network_filter, custom_properties, qos
|
||||
ovirt_vnic_profile:
|
||||
name: myvnic
|
||||
network: mynetwork
|
||||
data_center: datacenter
|
||||
qos: ""
|
||||
custom_properties: ""
|
||||
network_filter: ""
|
||||
|
||||
- name: Dont use migratable
|
||||
ovirt_vnic_profile:
|
||||
name: myvnic
|
||||
network: mynetwork
|
||||
data_center: datacenter
|
||||
migratable: False
|
||||
pass_through: enabled
|
||||
|
||||
- name: Remove vNIC
|
||||
ovirt_vnic_profile:
|
||||
name: myvnic
|
||||
network: mynetwork
|
||||
state: absent
|
||||
data_center: datacenter
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: ID of the vNIC profile which is managed
|
||||
returned: On success if vNIC profile is found.
|
||||
type: str
|
||||
sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
vnic:
|
||||
description: "Dictionary of all the vNIC profile attributes. Network interface attributes can be found on your oVirt/RHV instance
|
||||
at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/nic."
|
||||
returned: On success if vNIC profile is found.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
check_sdk,
|
||||
create_connection,
|
||||
equal,
|
||||
get_link_name,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
get_id_by_name
|
||||
)
|
||||
|
||||
|
||||
class EntityVnicPorfileModule(BaseModule):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EntityVnicPorfileModule, self).__init__(*args, **kwargs)
|
||||
|
||||
def _get_dcs_service(self):
|
||||
return self._connection.system_service().data_centers_service()
|
||||
|
||||
def _get_dcs_id(self):
|
||||
return get_id_by_name(self._get_dcs_service(), self.param('data_center'))
|
||||
|
||||
def _get_network_id(self):
|
||||
networks_service = self._get_dcs_service().service(self._get_dcs_id()).networks_service()
|
||||
return get_id_by_name(networks_service, self.param('network'))
|
||||
|
||||
def _get_qos_id(self):
|
||||
if self.param('qos'):
|
||||
qoss_service = self._get_dcs_service().service(self._get_dcs_id()).qoss_service()
|
||||
return get_id_by_name(qoss_service, self.param('qos')) if self.param('qos') else None
|
||||
return None
|
||||
|
||||
def _get_network_filter_id(self):
|
||||
nf_service = self._connection.system_service().network_filters_service()
|
||||
return get_id_by_name(nf_service, self.param('network_filter')) if self.param('network_filter') else None
|
||||
|
||||
def _get_network_filter(self):
|
||||
network_filter = None
|
||||
# The order of these condition is necessary.
|
||||
# When would network_filter and pass_through specified it would try to create and network_filter and fail on engine.
|
||||
if self.param('network_filter') == '' or self.param('pass_through') == 'enabled':
|
||||
network_filter = otypes.NetworkFilter()
|
||||
elif self.param('network_filter'):
|
||||
network_filter = otypes.NetworkFilter(id=self._get_network_filter_id())
|
||||
return network_filter
|
||||
|
||||
def _get_qos(self):
|
||||
qos = None
|
||||
# The order of these condition is necessary. When would qos and pass_through specified it would try to create and qos and fail on engine.
|
||||
if self.param('qos') == '' or self.param('pass_through') == 'enabled':
|
||||
qos = otypes.Qos()
|
||||
elif self.param('qos'):
|
||||
qos = otypes.Qos(id=self._get_qos_id())
|
||||
return qos
|
||||
|
||||
def _get_port_mirroring(self):
|
||||
if self.param('pass_through') == 'enabled':
|
||||
return False
|
||||
return self.param('port_mirroring')
|
||||
|
||||
def _get_migratable(self):
|
||||
if self.param('migratable') is not None:
|
||||
return self.param('migratable')
|
||||
if self.param('pass_through') == 'enabled':
|
||||
return True
|
||||
|
||||
def build_entity(self):
|
||||
return otypes.VnicProfile(
|
||||
name=self.param('name'),
|
||||
network=otypes.Network(id=self._get_network_id()),
|
||||
description=self.param('description') if self.param('description') is not None else None,
|
||||
pass_through=otypes.VnicPassThrough(mode=otypes.VnicPassThroughMode(self.param('pass_through'))) if self.param('pass_through') else None,
|
||||
custom_properties=[
|
||||
otypes.CustomProperty(
|
||||
name=cp.get('name'),
|
||||
regexp=cp.get('regexp'),
|
||||
value=str(cp.get('value')),
|
||||
) for cp in self.param('custom_properties') if cp
|
||||
] if self.param('custom_properties') else None,
|
||||
migratable=self._get_migratable(),
|
||||
qos=self._get_qos(),
|
||||
port_mirroring=self._get_port_mirroring(),
|
||||
network_filter=self._get_network_filter()
|
||||
)
|
||||
|
||||
def update_check(self, entity):
|
||||
def check_custom_properties():
|
||||
if self.param('custom_properties'):
|
||||
current = []
|
||||
if entity.custom_properties:
|
||||
current = [(cp.name, cp.regexp, str(cp.value)) for cp in entity.custom_properties]
|
||||
passed = [(cp.get('name'), cp.get('regexp'), str(cp.get('value'))) for cp in self.param('custom_properties') if cp]
|
||||
return sorted(current) == sorted(passed)
|
||||
return True
|
||||
|
||||
pass_through = getattr(entity.pass_through.mode, 'name', None)
|
||||
return (
|
||||
check_custom_properties() and
|
||||
# The reason why we can't use equal method, is we get None from _get_network_filter_id or _get_qos_id method, when passing empty string.
|
||||
# And when first param of equal method is None it returns true.
|
||||
self._get_network_filter_id() == getattr(entity.network_filter, 'id', None) and
|
||||
self._get_qos_id() == getattr(entity.qos, 'id', None) and
|
||||
equal(self.param('migratable'), getattr(entity, 'migratable', None)) and
|
||||
equal(self.param('pass_through'), pass_through.lower() if pass_through else None) and
|
||||
equal(self.param('description'), entity.description) and
|
||||
equal(self.param('port_mirroring'), getattr(entity, 'port_mirroring', None))
|
||||
)
|
||||
|
||||
|
||||
def get_entity(vnic_services, entitynics_module):
|
||||
vnic_profiles = vnic_services.list()
|
||||
network_id = entitynics_module._get_network_id()
|
||||
for vnic in vnic_profiles:
|
||||
# When vNIC already exist update it, when not create it
|
||||
if vnic.name == entitynics_module.param('name') and network_id == vnic.network.id:
|
||||
return vnic
|
||||
|
||||
|
||||
def check_params(module):
|
||||
if (module.params.get('port_mirroring') or module.params.get('network_filter') or module.params.get('qos'))\
|
||||
and module.params.get('pass_through') == 'enabled':
|
||||
module.fail_json(msg="Cannot edit VM network interface profile. 'Port Mirroring,'Qos' and 'Network Filter' are not supported on passthrough profiles.")
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
network=dict(type='str', required=True),
|
||||
data_center=dict(type='str', required=True),
|
||||
description=dict(type='str'),
|
||||
name=dict(type='str', required=True),
|
||||
network_filter=dict(type='str'),
|
||||
custom_properties=dict(type='list'),
|
||||
qos=dict(type='str'),
|
||||
pass_through=dict(type='str', choices=['disabled', 'enabled']),
|
||||
port_mirroring=dict(type='bool'),
|
||||
migratable=dict(type='bool'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
|
||||
)
|
||||
check_sdk(module)
|
||||
check_params(module)
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
|
||||
vnic_services = connection.system_service().vnic_profiles_service()
|
||||
|
||||
entitynics_module = EntityVnicPorfileModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=vnic_services,
|
||||
)
|
||||
state = module.params['state']
|
||||
entity = get_entity(vnic_services, entitynics_module)
|
||||
if state == 'present':
|
||||
ret = entitynics_module.create(entity=entity, force_create=entity is None)
|
||||
elif state == 'absent':
|
||||
if entity is not None:
|
||||
ret = entitynics_module.remove(entity=entity)
|
||||
else:
|
||||
raise Exception("Vnic profile '%s' in network '%s' was not found." % (module.params['name'], module.params['network']))
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,119 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2016 Red Hat, 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/>.
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ovirt_vnic_profile_info
|
||||
short_description: Retrieve information about one or more oVirt/RHV vnic profiles
|
||||
author: "Martin Necas (@mnecas)"
|
||||
version_added: "2.10"
|
||||
description:
|
||||
- "Retrieve information about one or more oVirt/RHV vnic profiles."
|
||||
notes:
|
||||
- "This module returns a variable C(ovirt_vnic_profiles), which
|
||||
contains a list of vnic profiles. You need to register the result with
|
||||
the I(register) keyword to use it."
|
||||
options:
|
||||
max:
|
||||
description:
|
||||
- "The maximum number of results to return."
|
||||
type: int
|
||||
name:
|
||||
description:
|
||||
- "Name of vnic profile."
|
||||
type: str
|
||||
extends_documentation_fragment: ovirt_info
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples don't contain auth parameter for simplicity,
|
||||
# look at ovirt_auth module to see how to reuse authentication:
|
||||
|
||||
# Gather information 10 vnic profiles
|
||||
- ovirt_vnic_profile_info:
|
||||
max: 10
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.ovirt_vnic_profiles }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ovirt_vnic_profiles:
|
||||
description: "List of dictionaries describing the vnic profiles. Vnic_profile attributes are mapped to dictionary keys,
|
||||
all vnic profiles attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/vnic_profile."
|
||||
returned: On success.
|
||||
type: list
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
get_dict_of_struct,
|
||||
ovirt_info_full_argument_spec,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ovirt_info_full_argument_spec(
|
||||
max=dict(default=None, type='int'),
|
||||
name=dict(default=None),
|
||||
)
|
||||
module = AnsibleModule(argument_spec)
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
connection = create_connection(auth)
|
||||
vnic_profiles_service = connection.system_service().vnic_profiles_service()
|
||||
vnic_profiles = vnic_profiles_service.list(max=module.params.get('max'))
|
||||
if module.params.get('name') and vnic_profiles:
|
||||
vnic_profiles = [vnic_profile for vnic_profile in vnic_profiles if vnic_profile.name == module.params.get("name")]
|
||||
|
||||
result = dict(
|
||||
ovirt_vnic_profiles=[
|
||||
get_dict_of_struct(
|
||||
struct=c,
|
||||
connection=connection,
|
||||
fetch_nested=module.params.get('fetch_nested'),
|
||||
attributes=module.params.get('nested_attributes'),
|
||||
) for c in vnic_profiles
|
||||
],
|
||||
)
|
||||
module.exit_json(changed=False, **result)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,102 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2016, Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
# Standard oVirt documentation fragment
|
||||
DOCUMENTATION = r'''
|
||||
options:
|
||||
wait:
|
||||
description:
|
||||
- "C(yes) if the module should wait for the entity to get into desired state."
|
||||
type: bool
|
||||
default: yes
|
||||
fetch_nested:
|
||||
description:
|
||||
- "If I(True) the module will fetch additional data from the API."
|
||||
- "It will fetch IDs of the VMs disks, snapshots, etc. User can configure to fetch other
|
||||
attributes of the nested entities by specifying C(nested_attributes)."
|
||||
type: bool
|
||||
version_added: "2.3"
|
||||
nested_attributes:
|
||||
description:
|
||||
- "Specifies list of the attributes which should be fetched from the API."
|
||||
- "This parameter apply only when C(fetch_nested) is I(true)."
|
||||
type: list
|
||||
version_added: "2.3"
|
||||
auth:
|
||||
description:
|
||||
- "Dictionary with values needed to create HTTP/HTTPS connection to oVirt:"
|
||||
suboptions:
|
||||
username:
|
||||
description:
|
||||
- The name of the user, something like I(admin@internal).
|
||||
- Default value is set by C(OVIRT_USERNAME) environment variable.
|
||||
type: str
|
||||
required: true
|
||||
password:
|
||||
description:
|
||||
- The password of the user.
|
||||
- Default value is set by C(OVIRT_PASSWORD) environment variable.
|
||||
type: str
|
||||
required: true
|
||||
url:
|
||||
description:
|
||||
- A string containing the API URL of the server, usually something like `I(https://server.example.com/ovirt-engine/api)`.
|
||||
- Default value is set by C(OVIRT_URL) environment variable.
|
||||
- Either C(url) or C(hostname) is required.
|
||||
type: str
|
||||
hostname:
|
||||
description:
|
||||
- A string containing the hostname of the server, usually something like `I(server.example.com)`.
|
||||
- Default value is set by C(OVIRT_HOSTNAME) environment variable.
|
||||
- Either C(url) or C(hostname) is required.
|
||||
type: str
|
||||
token:
|
||||
description:
|
||||
- Token to be used instead of login with username/password.
|
||||
- Default value is set by C(OVIRT_TOKEN) environment variable.
|
||||
type: str
|
||||
insecure:
|
||||
description:
|
||||
- A boolean flag that indicates if the server TLS certificate and host name should be checked.
|
||||
type: bool
|
||||
ca_file:
|
||||
description:
|
||||
- A PEM file containing the trusted CA certificates.
|
||||
- The certificate presented by the server will be verified using these CA certificates.
|
||||
- If C(ca_file) parameter is not set, system wide CA certificate store is used.
|
||||
- Default value is set by C(OVIRT_CAFILE) environment variable.
|
||||
type: str
|
||||
kerberos:
|
||||
description:
|
||||
- A boolean flag indicating if Kerberos authentication should be used instead of the default basic authentication.
|
||||
type: bool
|
||||
headers:
|
||||
description:
|
||||
- Dictionary of HTTP headers to be added to each API call.
|
||||
type: dict
|
||||
type: dict
|
||||
required: true
|
||||
timeout:
|
||||
description:
|
||||
- "The amount of time in seconds the module should wait for the instance to
|
||||
get into desired state."
|
||||
type: int
|
||||
default: 180
|
||||
poll_interval:
|
||||
description:
|
||||
- "Number of the seconds the module waits until another poll request on entity status is sent."
|
||||
type: int
|
||||
default: 3
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- ovirt-engine-sdk-python >= 4.3.0
|
||||
notes:
|
||||
- "In order to use this module you have to install oVirt Python SDK.
|
||||
To ensure it's installed with correct version you can create the following task:
|
||||
I(pip: name=ovirt-engine-sdk-python version=4.3.0)"
|
||||
'''
|
@ -1,57 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2016, Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
# info standard oVirt documentation fragment
|
||||
DOCUMENTATION = r'''
|
||||
options:
|
||||
fetch_nested:
|
||||
description:
|
||||
- If I(yes) the module will fetch additional data from the API.
|
||||
- It will fetch only IDs of nested entity. It doesn't fetch multiple levels of nested attributes.
|
||||
Only the attributes of the current entity. User can configure to fetch other
|
||||
attributes of the nested entities by specifying C(nested_attributes).
|
||||
type: bool
|
||||
version_added: "2.3"
|
||||
nested_attributes:
|
||||
description:
|
||||
- Specifies list of the attributes which should be fetched from the API.
|
||||
- This parameter apply only when C(fetch_nested) is I(true).
|
||||
type: list
|
||||
version_added: "2.3"
|
||||
auth:
|
||||
description:
|
||||
- "Dictionary with values needed to create HTTP/HTTPS connection to oVirt:"
|
||||
- C(username)[I(required)] - The name of the user, something like I(admin@internal).
|
||||
Default value is set by I(OVIRT_USERNAME) environment variable.
|
||||
- "C(password)[I(required)] - The password of the user. Default value is set by I(OVIRT_PASSWORD) environment variable."
|
||||
- "C(url)- A string containing the API URL of the server, usually
|
||||
something like `I(https://server.example.com/ovirt-engine/api)`. Default value is set by I(OVIRT_URL) environment variable.
|
||||
Either C(url) or C(hostname) is required."
|
||||
- "C(hostname) - A string containing the hostname of the server, usually
|
||||
something like `I(server.example.com)`. Default value is set by I(OVIRT_HOSTNAME) environment variable.
|
||||
Either C(url) or C(hostname) is required."
|
||||
- "C(token) - Token to be used instead of login with username/password. Default value is set by I(OVIRT_TOKEN) environment variable."
|
||||
- "C(insecure) - A boolean flag that indicates if the server TLS
|
||||
certificate and host name should be checked."
|
||||
- "C(ca_file) - A PEM file containing the trusted CA certificates. The
|
||||
certificate presented by the server will be verified using these CA
|
||||
certificates. If `C(ca_file)` parameter is not set, system wide
|
||||
CA certificate store is used. Default value is set by I(OVIRT_CAFILE) environment variable."
|
||||
- "C(kerberos) - A boolean flag indicating if Kerberos authentication
|
||||
should be used instead of the default basic authentication."
|
||||
- "C(headers) - Dictionary of HTTP headers to be added to each API call."
|
||||
type: dict
|
||||
required: true
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- ovirt-engine-sdk-python >= 4.3.0
|
||||
notes:
|
||||
- "In order to use this module you have to install oVirt Python SDK.
|
||||
To ensure it's installed with correct version you can create the following task:
|
||||
pip: name=ovirt-engine-sdk-python version=4.3.0"
|
||||
'''
|
Loading…
Reference in New Issue