From 07feec30d38539bec814ad215c4d9267cd794444 Mon Sep 17 00:00:00 2001 From: maorlipchuk Date: Wed, 23 Aug 2017 15:44:02 +0300 Subject: [PATCH] [oVirt] Update/Add functionalities for import storage domain (#26568) * ovirt_templates: Update the argument spec of templates. Add id of template since it is needed for register. * ovirt_vms: Register unregistered VM. Use register of VM with id instead of name since an unregitered entity can be registered also without name attribute. * ovirt_hosts: Add iscsidiscover to ovirt_hosts Adding functionality of iscsidiscover to be used to discover iscsi targets. * ovirt_storage_domains: Add support for import block storage domain. * Add functionality of partial import to unregistered VMs. * Add functionality of partial import to unregistered Templates. * ovirt_hosts: Add iscsilogin to ovirt hosts. Add functionality of iscsi login to ovirt hosts to be used to connect to iscsi targets and to be able to import iSCSI storage domain eventually. * Add ovirt_storage_templates_facts Adding fact module for storage templates. The module should help with registering unregistered templates. * Add ovirt_storage_vms_facts Adding fact module for storage VMs. The module should help with registering unregistered VMs. --- lib/ansible/module_utils/ovirt.py | 15 ++- .../modules/cloud/ovirt/ovirt_hosts.py | 72 +++++++++- .../cloud/ovirt/ovirt_storage_domains.py | 14 +- .../ovirt/ovirt_storage_templates_facts.py | 123 ++++++++++++++++++ .../cloud/ovirt/ovirt_storage_vms_facts.py | 122 +++++++++++++++++ .../modules/cloud/ovirt/ovirt_templates.py | 67 ++++++---- lib/ansible/modules/cloud/ovirt/ovirt_vms.py | 83 +++++++++++- 7 files changed, 457 insertions(+), 39 deletions(-) create mode 100644 lib/ansible/modules/cloud/ovirt/ovirt_storage_templates_facts.py create mode 100644 lib/ansible/modules/cloud/ovirt/ovirt_storage_vms_facts.py diff --git a/lib/ansible/module_utils/ovirt.py b/lib/ansible/module_utils/ovirt.py index 3e001c0d16f..bd08315d3a0 100644 --- a/lib/ansible/module_utils/ovirt.py +++ b/lib/ansible/module_utils/ovirt.py @@ -746,6 +746,17 @@ class BaseModule(object): 'diff': self._diff, } + def wait_for_import(self): + 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: + return entity + time.sleep(poll_interval) + def search_entity(self, search_params=None): """ Always first try to search by `ID`, if ID isn't specified, @@ -755,10 +766,10 @@ class BaseModule(object): entity = None if 'id' in self._module.params and self._module.params['id'] is not None: - entity = search_by_attributes(self._service, id=self._module.params['id']) + entity = get_entity(self._service.service(self._module.params['id'])) elif search_params is not None: entity = search_by_attributes(self._service, **search_params) - elif 'name' in self._module.params and self._module.params['name'] is not None: + elif self._module.params.get('name') is not None: entity = search_by_attributes(self._service, name=self._module.params['name']) return entity diff --git a/lib/ansible/modules/cloud/ovirt/ovirt_hosts.py b/lib/ansible/modules/cloud/ovirt/ovirt_hosts.py index 34437ffbb39..4c343d5dfdc 100644 --- a/lib/ansible/modules/cloud/ovirt/ovirt_hosts.py +++ b/lib/ansible/modules/cloud/ovirt/ovirt_hosts.py @@ -40,9 +40,10 @@ options: 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' + 'restarted', 'stopped', 'reinstalled', 'iscsidiscover', 'iscsilogin' ] default: present comment: @@ -108,6 +109,13 @@ options: - "Enable or disable power management of the host." - "For more comprehensive setup of PM use C(ovirt_host_pm) module." 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" + version_added: "2.4" extends_documentation_fragment: ovirt ''' @@ -158,6 +166,29 @@ EXAMPLES = ''' state: upgraded name: myhost +# discover iscsi targets +- ovirt_hosts: + state: iscsidiscover + name: myhost + iscsi: + username: iscsi_user + password: secret + address: 10.34.61.145 + port: 3260 + + +# login to iscsi targets +- ovirt_hosts: + 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_hosts: state: reinstalled @@ -182,6 +213,10 @@ host: 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 @@ -200,6 +235,7 @@ from ansible.module_utils.ovirt import ( check_sdk, create_connection, equal, + get_id_by_name, ovirt_full_argument_spec, wait, ) @@ -327,7 +363,7 @@ def main(): state=dict( choices=[ 'present', 'absent', 'maintenance', 'upgraded', 'started', - 'restarted', 'stopped', 'reinstalled', + 'restarted', 'stopped', 'reinstalled', 'iscsidiscover', 'iscsilogin' ], default='present', ), @@ -346,10 +382,15 @@ def main(): kernel_params=dict(default=None, type='list'), hosted_engine=dict(default=None, choices=['deploy', 'undeploy']), power_management_enabled=dict(default=None, type='bool'), + iscsi=dict(default=None, type='dict'), ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, + required_if=[ + ['state', 'iscsidiscover', ['iscsi']], + ['state', 'iscsilogin', ['iscsi']] + ] ) check_sdk(module) @@ -396,6 +437,33 @@ def main(): post_action=lambda h: time.sleep(module.params['poll_interval']), fail_condition=failed_state, ) + elif state == 'iscsidiscover': + host_id = get_id_by_name(hosts_service, module.params['name']) + iscsi_targets = hosts_service.service(host_id).iscsi_discover( + iscsi=otypes.IscsiDetails( + port=int(module.params['iscsi']['port']) if module.params['iscsi']['port'].isdigit() else None, + username=module.params['iscsi']['username'], + password=module.params['iscsi']['password'], + address=module.params['iscsi']['address'], + ), + ) + ret = { + 'changed': False, + 'id': host_id, + 'iscsi_targets': iscsi_targets, + } + elif state == 'iscsilogin': + host_id = get_id_by_name(hosts_service, module.params['name']) + ret = hosts_module.action( + action='iscsi_login', + iscsi=otypes.IscsiDetails( + port=int(module.params['iscsi']['port']) if module.params['iscsi']['port'].isdigit() else None, + username=module.params['iscsi']['username'], + password=module.params['iscsi']['password'], + address=module.params['iscsi']['address'], + target=module.params['iscsi']['target'], + ), + ) elif state == 'started': ret = hosts_module.action( action='fence', diff --git a/lib/ansible/modules/cloud/ovirt/ovirt_storage_domains.py b/lib/ansible/modules/cloud/ovirt/ovirt_storage_domains.py index ba914b5b703..45aaef37ef5 100644 --- a/lib/ansible/modules/cloud/ovirt/ovirt_storage_domains.py +++ b/lib/ansible/modules/cloud/ovirt/ovirt_storage_domains.py @@ -33,12 +33,17 @@ 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." state: description: - - "Should the storage domain be present/absent/maintenance/unattached" + - "Should the storage domain be present/absent/maintenance/unattached/imported" + - "I(imported) is supported since version 2.4." choices: ['present', 'absent', 'maintenance', 'unattached'] default: present description: @@ -256,6 +261,8 @@ class StorageDomainModule(BaseModule): name=self._module.params['name'], description=self._module.params['description'], comment=self._module.params['comment'], + import_=True if (self._module.params['state'] == 'imported' and storage_type in ['iscsi', 'fcp']) else None, + id=self._module.params['id'] if (self._module.params['state'] == 'imported' and storage_type in ['iscsi', 'fcp']) else None, type=otypes.StorageDomainType( self._module.params['domain_function'] ), @@ -423,9 +430,10 @@ def control_state(sd_module): def main(): argument_spec = ovirt_full_argument_spec( state=dict( - choices=['present', 'absent', 'maintenance', 'unattached'], + choices=['present', 'absent', 'maintenance', 'unattached', 'imported'], default='present', ), + id=dict(default=None), name=dict(required=True), description=dict(default=None), comment=dict(default=None), @@ -465,7 +473,7 @@ def main(): format=module.params['format'], host=module.params['host'], ) - elif state == 'present': + 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( diff --git a/lib/ansible/modules/cloud/ovirt/ovirt_storage_templates_facts.py b/lib/ansible/modules/cloud/ovirt/ovirt_storage_templates_facts.py new file mode 100644 index 00000000000..cacb778b87a --- /dev/null +++ b/lib/ansible/modules/cloud/ovirt/ovirt_storage_templates_facts.py @@ -0,0 +1,123 @@ +#!/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 . +# + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: ovirt_storage_templates_facts +short_description: Retrieve facts about one or more oVirt/RHV templates relate to a storage domain. +author: "Maor Lipchuk" +version_added: "2.4" +description: + - "Retrieve facts about one or more oVirt/RHV templates relate to a storage domain." +notes: + - "This module creates a new top-level C(ovirt_storage_templates) fact, which + contains a list of templates." +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." + +extends_documentation_fragment: ovirt_facts +''' + +EXAMPLES = ''' +# Examples don't contain auth parameter for simplicity, +# look at ovirt_auth module to see how to reuse authentication: + +# Gather facts about all Templates which relate to a storage domain and +# are unregistered: +- ovirt_storage_templates_facts: + unregistered=True +- debug: + var: ovirt_storage_templates +''' + +RETURN = ''' +ovirt_storage_templates: + description: "List of dictionaries describing the Templates. Template attribues 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_facts_full_argument_spec, + get_id_by_name +) + + +def main(): + argument_spec = ovirt_facts_full_argument_spec( + all_content=dict(default=False, type='bool'), + case_sensitive=dict(default=True, type='bool'), + storage_domain=dict(default=None), + max=dict(default=None, type='int'), + unregistered=dict(default=False, type='bool'), + ) + module = AnsibleModule(argument_spec) + 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 the unregistered Template we want to register: + if module.params.get('unregistered'): + templates = templates_service.list(unregistered=True) + else: + templates = templates_service.list() + module.exit_json( + changed=False, + ansible_facts=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 + ], + ), + ) + 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() diff --git a/lib/ansible/modules/cloud/ovirt/ovirt_storage_vms_facts.py b/lib/ansible/modules/cloud/ovirt/ovirt_storage_vms_facts.py new file mode 100644 index 00000000000..ac3a68f93b5 --- /dev/null +++ b/lib/ansible/modules/cloud/ovirt/ovirt_storage_vms_facts.py @@ -0,0 +1,122 @@ +#!/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 . +# + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: ovirt_storage_vms_facts +short_description: Retrieve facts about one or more oVirt/RHV virtual machines relate to a storage domain. +author: "Maor Lipchuk" +version_added: "2.4" +description: + - "Retrieve facts about one or more oVirt/RHV virtual machines relate to a storage domain." +notes: + - "This module creates a new top-level C(ovirt_storage_vms) fact, which + contains a list of virtual machines." +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." +extends_documentation_fragment: ovirt_facts +''' + +EXAMPLES = ''' +# Examples don't contain auth parameter for simplicity, +# look at ovirt_auth module to see how to reuse authentication: + +# Gather facts about all VMs which relate to a storage domain and +# are unregistered: +- ovirt_vms_facts: + unregistered=True +- debug: + var: ovirt_storage_vms +''' + +RETURN = ''' +ovirt_storage_vms: + description: "List of dictionaries describing the VMs. VM attribues 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_facts_full_argument_spec, + get_id_by_name +) + + +def main(): + argument_spec = ovirt_facts_full_argument_spec( + all_content=dict(default=False, type='bool'), + case_sensitive=dict(default=True, type='bool'), + storage_domain=dict(default=None), + max=dict(default=None, type='int'), + unregistered=dict(default=False, type='bool'), + ) + module = AnsibleModule(argument_spec) + 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 the unregistered VM we want to register: + if module.params.get('unregistered'): + vms = vms_service.list(unregistered=True) + else: + vms = vms_service.list() + module.exit_json( + changed=False, + ansible_facts=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 + ], + ), + ) + 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() diff --git a/lib/ansible/modules/cloud/ovirt/ovirt_templates.py b/lib/ansible/modules/cloud/ovirt/ovirt_templates.py index 10c046ed5f9..4051c51a409 100644 --- a/lib/ansible/modules/cloud/ovirt/ovirt_templates.py +++ b/lib/ansible/modules/cloud/ovirt/ovirt_templates.py @@ -36,13 +36,16 @@ options: name: description: - "Name of the template to manage." - required: true + id: + description: + - "ID of the template to be registered." + version_added: "2.4" state: description: - "Should the template be present/absent/exported/imported/registered. When C(state) is I(registered) and the unregistered template's name - belongs to an already registered in engine template then we fail - to register the unregistered template." + belongs to an already registered in engine template in the same DC + then we fail to register the unregistered template." choices: ['present', 'absent', 'exported', 'imported', 'registered'] default: present vm: @@ -57,6 +60,10 @@ options: cluster: description: - "Name of the cluster, where template should be created/imported." + allow_partial_import: + description: + - "Boolean indication whether to allow partial registration of a template when C(state) is registered." + version_added: "2.4" exclusive: description: - "When C(state) is I(exported) this parameter indicates if the existing templates with the @@ -120,9 +127,24 @@ EXAMPLES = ''' # Register template - ovirt_templates: state: registered + storage_domain: mystorage + cluster: mycluster name: mytemplate + +# Register template using id +- ovirt_templates: + state: registered storage_domain: mystorage cluster: mycluster + id: 1111-1111-1111-1111 + +# Register template, allowing partial import +- ovirt_templates: + state: registered + storage_domain: mystorage + allow_partial_import: "True" + cluster: mycluster + id: 1111-1111-1111-1111 # Import image from Glance s a template - ovirt_templates: @@ -216,28 +238,18 @@ class TemplatesModule(BaseModule): self._service = self._connection.system_service().templates_service() -def wait_for_import(module, templates_service): - if module.params['wait']: - start = time.time() - timeout = module.params['timeout'] - poll_interval = module.params['poll_interval'] - while time.time() < start + timeout: - template = search_by_name(templates_service, module.params['name']) - if template: - return template - time.sleep(poll_interval) - - def main(): argument_spec = ovirt_full_argument_spec( state=dict( choices=['present', 'absent', 'exported', 'imported', 'registered'], default='present', ), - name=dict(default=None, required=True), + id=dict(default=None), + name=dict(default=None), vm=dict(default=None), description=dict(default=None), cluster=dict(default=None), + allow_partial_import=dict(default=None, type='bool'), cpu_profile=dict(default=None), disks=dict(default=[], type='list'), clone_permissions=dict(type='bool'), @@ -251,6 +263,7 @@ def main(): module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, + required_one_of=[['id', 'name']], ) check_sdk(module) @@ -328,7 +341,7 @@ def main(): ) if module.params['cluster'] else None, **kwargs ) - template = wait_for_import(module, templates_service) + template = templates_module.wait_for_import() ret = { 'changed': True, 'id': template.id, @@ -344,32 +357,32 @@ def main(): # Find the unregistered Template we want to register: templates = templates_service.list(unregistered=True) template = next( - (t for t in templates if t.name == module.params['name']), + (t for t in templates if (t.id == module.params['id'] or t.name == module.params['name'])), None ) changed = False if template is None: - # Test if template is registered: template = templates_module.search_entity() if template is None: raise ValueError( - "Template with name '%s' wasn't found." % module.params['name'] + "Template '%s(%s)' wasn't found." % (module.params['name'], module.params['id']) ) else: + # Register the template into the system: changed = True template_service = templates_service.template_service(template.id) - # Register the template into the system: template_service.register( + allow_partial_import=module.params['allow_partial_import'], cluster=otypes.Cluster( name=module.params['cluster'] - ) if module.params['cluster'] else None, - template=otypes.Template( - name=module.params['name'], - ), + ) if module.params['cluster'] else None ) - if module.params['wait']: - template = wait_for_import(module, templates_service) + if module.params['wait']: + template = templates_module.wait_for_import() + else: + # Fetch template to initialize return. + template = template_service.get() ret = { 'changed': changed, 'id': template.id, diff --git a/lib/ansible/modules/cloud/ovirt/ovirt_vms.py b/lib/ansible/modules/cloud/ovirt/ovirt_vms.py index 4cf601051dc..a38246476a7 100644 --- a/lib/ansible/modules/cloud/ovirt/ovirt_vms.py +++ b/lib/ansible/modules/cloud/ovirt/ovirt_vms.py @@ -43,15 +43,23 @@ options: - "ID of the Virtual Machine to manage." state: description: - - "Should the Virtual Machine be running/stopped/present/absent/suspended/next_run." + - "Should the Virtual Machine be running/stopped/present/absent/suspended/next_run/registered. + When C(state) is I(registered) and the unregistered VM's name + belongs to an already registered in engine VM in the same DC + then we fail to register the unregistered template." - "I(present) and I(running) are equal states." - "I(next_run) state updates the VM and if the VM has next run configuration it will be rebooted." - "Please check I(notes) to more detailed description of states." - choices: ['running', 'stopped', 'present', 'absent', 'suspended', 'next_run'] + - "I(registered) is supported since 2.4" + choices: ['running', 'stopped', 'present', 'absent', 'suspended', 'next_run', 'registered'] default: present cluster: description: - "Name of the cluster, where Virtual Machine should be created. Required if creating VM." + allow_partial_import: + description: + - "Boolean indication whether to allow partial registration of Virtual Machine when C(state) is registered." + version_added: "2.4" template: description: - "Name of the template, which should be used to create Virtual Machine. Required if creating VM." @@ -354,6 +362,28 @@ ovirt_vms: name: myvm template: rhel7_template +# Register VM +ovirt_vms: + state: registered + storage_domain: mystorage + cluster: mycluster + name: myvm + +# Register VM using id +ovirt_vms: + state: registered + storage_domain: mystorage + cluster: mycluster + id: 1111-1111-1111-1111 + +# Register VM, allowing partial import +ovirt_vms: + state: registered + storage_domain: mystorage + allow_partial_import: "True" + cluster: mycluster + id: 1111-1111-1111-1111 + # Creates a stateless VM which will always use latest template version: ovirt_vms: name: myvm @@ -522,7 +552,6 @@ vm: returned: On success if VM is found. type: dict ''' - import traceback try: @@ -538,6 +567,7 @@ from ansible.module_utils.ovirt import ( convert_to_bytes, create_connection, equal, + get_dict_of_struct, get_entity, get_link_name, get_id_by_name, @@ -1053,16 +1083,16 @@ def control_state(vm, vms_service, module): condition=lambda vm: vm.status in [otypes.VmStatus.DOWN, otypes.VmStatus.UP], ) - def main(): argument_spec = ovirt_full_argument_spec( state=dict( - choices=['running', 'stopped', 'present', 'absent', 'suspended', 'next_run'], + choices=['running', 'stopped', 'present', 'absent', 'suspended', 'next_run', 'registered'], default='present', ), name=dict(default=None), id=dict(default=None), cluster=dict(default=None), + allow_partial_import=dict(default=None, type='bool'), template=dict(default=None), template_version=dict(default=None, type='int'), use_latest_template_version=dict(default=None, type='bool'), @@ -1119,6 +1149,7 @@ def main(): module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, + required_one_of=[['id', 'name']], ) check_sdk(module) check_params(module) @@ -1243,6 +1274,48 @@ def main(): ) elif state == 'absent': ret = vms_module.remove() + elif state == 'registered': + storage_domains_service = connection.system_service().storage_domains_service() + + # Find the storage domain with unregistered VM: + 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 the unregistered VM we want to register: + vms = vms_service.list(unregistered=True) + vm = next( + (vm for vm in vms if (vm.id == module.params['id'] or vm.name == module.params['name'])), + None + ) + changed = False + if vm is None: + vm = vms_module.search_entity() + if vm is None: + raise ValueError( + "VM '%s(%s)' wasn't found." % (module.params['name'], module.params['id']) + ) + else: + # Register the vm into the system: + changed = True + vm_service = vms_service.vm_service(vm.id) + vm_service.register( + allow_partial_import=module.params['allow_partial_import'], + cluster=otypes.Cluster( + name=module.params['cluster'] + ) if module.params['cluster'] else None + ) + + if module.params['wait']: + vm = vms_module.wait_for_import() + else: + # Fetch vm to initialize return. + vm = vm_service.get() + ret = { + 'changed': changed, + 'id': vm.id, + 'vm': get_dict_of_struct(vm) + } module.exit_json(**ret) except Exception as e: