diff --git a/plugins/connection/libvirt_lxc.py b/plugins/connection/libvirt_lxc.py deleted file mode 100644 index 4f80299646..0000000000 --- a/plugins/connection/libvirt_lxc.py +++ /dev/null @@ -1,181 +0,0 @@ -# Based on local.py (c) 2012, Michael DeHaan -# Based on chroot.py (c) 2013, Maykel Moya -# (c) 2013, Michael Scherer -# (c) 2015, Toshio Kuratomi -# (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 - -DOCUMENTATION = ''' - author: Michael Scherer - connection: libvirt_lxc - short_description: Run tasks in lxc containers via libvirt - description: - - Run commands or put/fetch files to an existing lxc container using libvirt - options: - remote_addr: - description: - - Container identifier - default: The set user as per docker's configuration - vars: - - name: ansible_host - - name: ansible_libvirt_lxc_host -''' - -import distutils.spawn -import os -import os.path -import subprocess -import traceback - -from ansible import constants as C -from ansible.errors import AnsibleError -from ansible.module_utils.six.moves import shlex_quote -from ansible.module_utils._text import to_bytes -from ansible.plugins.connection import ConnectionBase, BUFSIZE -from ansible.utils.display import Display - -display = Display() - - -class Connection(ConnectionBase): - ''' Local lxc based connections ''' - - transport = 'community.general.libvirt_lxc' - has_pipelining = True - # su currently has an undiagnosed issue with calculating the file - # checksums (so copy, for instance, doesn't work right) - # Have to look into that before re-enabling this - default_user = 'root' - has_tty = False - - def __init__(self, play_context, new_stdin, *args, **kwargs): - super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) - self.lxc = self._play_context.remote_addr - - self.virsh = self._search_executable('virsh') - - self._check_domain(self.lxc) - - def _search_executable(self, executable): - cmd = distutils.spawn.find_executable(executable) - if not cmd: - raise AnsibleError("%s command not found in PATH") % executable - return cmd - - def _check_domain(self, domain): - p = subprocess.Popen([self.virsh, '-q', '-c', 'lxc:///', 'dominfo', to_bytes(domain)], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - p.communicate() - if p.returncode: - raise AnsibleError("%s is not a lxc defined in libvirt" % domain) - - def _connect(self): - ''' connect to the lxc; nothing to do here ''' - super(Connection, self)._connect() - if not self._connected: - display.vvv("THIS IS A LOCAL LXC DIR", host=self.lxc) - self._connected = True - - def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE): - ''' run a command on the chroot. This is only needed for implementing - put_file() get_file() so that we don't have to read the whole file - into memory. - - compared to exec_command() it looses some niceties like being able to - return the process's exit code immediately. - ''' - executable = C.DEFAULT_EXECUTABLE.split()[0] if C.DEFAULT_EXECUTABLE else '/bin/sh' - local_cmd = [self.virsh, '-q', '-c', 'lxc:///', 'lxc-enter-namespace'] - - if C.DEFAULT_LIBVIRT_LXC_NOSECLABEL: - local_cmd += ['--noseclabel'] - - local_cmd += [self.lxc, '--', executable, '-c', cmd] - - display.vvv("EXEC %s" % (local_cmd,), host=self.lxc) - local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd] - p = subprocess.Popen(local_cmd, shell=False, stdin=stdin, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - return p - - def exec_command(self, cmd, in_data=None, sudoable=False): - ''' run a command on the chroot ''' - super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) - - p = self._buffered_exec_command(cmd) - - stdout, stderr = p.communicate(in_data) - return (p.returncode, stdout, stderr) - - def _prefix_login_path(self, remote_path): - ''' Make sure that we put files into a standard path - - If a path is relative, then we need to choose where to put it. - ssh chooses $HOME but we aren't guaranteed that a home dir will - exist in any given chroot. So for now we're choosing "/" instead. - This also happens to be the former default. - - Can revisit using $HOME instead if it's a problem - ''' - if not remote_path.startswith(os.path.sep): - remote_path = os.path.join(os.path.sep, remote_path) - return os.path.normpath(remote_path) - - def put_file(self, in_path, out_path): - ''' transfer a file from local to lxc ''' - super(Connection, self).put_file(in_path, out_path) - display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.lxc) - - out_path = shlex_quote(self._prefix_login_path(out_path)) - try: - with open(to_bytes(in_path, errors='surrogate_or_strict'), 'rb') as in_file: - if not os.fstat(in_file.fileno()).st_size: - count = ' count=0' - else: - count = '' - try: - p = self._buffered_exec_command('dd of=%s bs=%s%s' % (out_path, BUFSIZE, count), stdin=in_file) - except OSError: - raise AnsibleError("chroot connection requires dd command in the chroot") - try: - stdout, stderr = p.communicate() - except Exception: - traceback.print_exc() - raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path)) - if p.returncode != 0: - raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr)) - except IOError: - raise AnsibleError("file or module does not exist at: %s" % in_path) - - def fetch_file(self, in_path, out_path): - ''' fetch a file from lxc to local ''' - super(Connection, self).fetch_file(in_path, out_path) - display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.lxc) - - in_path = shlex_quote(self._prefix_login_path(in_path)) - try: - p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE)) - except OSError: - raise AnsibleError("chroot connection requires dd command in the chroot") - - with open(to_bytes(out_path, errors='surrogate_or_strict'), 'wb+') as out_file: - try: - chunk = p.stdout.read(BUFSIZE) - while chunk: - out_file.write(chunk) - chunk = p.stdout.read(BUFSIZE) - except Exception: - traceback.print_exc() - raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path)) - stdout, stderr = p.communicate() - if p.returncode != 0: - raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr)) - - def close(self): - ''' terminate the connection; nothing to do here ''' - super(Connection, self).close() - self._connected = False diff --git a/plugins/modules/cloud/misc/virt.py b/plugins/modules/cloud/misc/virt.py deleted file mode 100644 index fefefd03dc..0000000000 --- a/plugins/modules/cloud/misc/virt.py +++ /dev/null @@ -1,602 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2007, 2012 Red Hat, Inc -# Michael DeHaan -# Seth Vidal -# 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: virt -short_description: Manages virtual machines supported by libvirt -description: - - Manages virtual machines supported by I(libvirt). -options: - name: - description: - - name of the guest VM being managed. Note that VM must be previously - defined with xml. - - This option is required unless I(command) is C(list_vms) or C(info). - state: - description: - - Note that there may be some lag for state requests like C(shutdown) - since these refer only to VM states. After starting a guest, it may not - be immediately accessible. - state and command are mutually exclusive except when command=list_vms. In - this case all VMs in specified state will be listed. - choices: [ destroyed, paused, running, shutdown ] - command: - description: - - In addition to state management, various non-idempotent commands are available. - choices: [ create, define, destroy, freemem, get_xml, info, list_vms, nodeinfo, pause, shutdown, start, status, stop, undefine, unpause, virttype ] - autostart: - description: - - start VM at host startup. - type: bool - uri: - description: - - libvirt connection uri. - default: qemu:///system - xml: - description: - - XML document used with the define command. - - Must be raw XML content using C(lookup). XML cannot be reference to a file. -requirements: - - python >= 2.6 - - libvirt-python -author: - - Ansible Core Team - - Michael DeHaan - - Seth Vidal (@skvidal) -''' - -EXAMPLES = ''' -# a playbook task line: -- virt: - name: alpha - state: running - -# /usr/bin/ansible invocations -# ansible host -m virt -a "name=alpha command=status" -# ansible host -m virt -a "name=alpha command=get_xml" -# ansible host -m virt -a "name=alpha command=create uri=lxc:///" - -# defining and launching an LXC guest -- name: define vm - virt: - command: define - xml: "{{ lookup('template', 'container-template.xml.j2') }}" - uri: 'lxc:///' -- name: start vm - virt: - name: foo - state: running - uri: 'lxc:///' - -# setting autostart on a qemu VM (default uri) -- name: set autostart for a VM - virt: - name: foo - autostart: yes - -# Defining a VM and making is autostart with host. VM will be off after this task -- name: define vm from xml and set autostart - virt: - command: define - xml: "{{ lookup('template', 'vm_template.xml.j2') }}" - autostart: yes - -# Listing VMs -- name: list all VMs - virt: - command: list_vms - register: all_vms - -- name: list only running VMs - virt: - command: list_vms - state: running - register: running_vms -''' - -RETURN = ''' -# for list_vms command -list_vms: - description: The list of vms defined on the remote system - type: list - returned: success - sample: [ - "build.example.org", - "dev.example.org" - ] -# for status command -status: - description: The status of the VM, among running, crashed, paused and shutdown - type: str - sample: "success" - returned: success -''' - -import traceback - -try: - import libvirt - from libvirt import libvirtError -except ImportError: - HAS_VIRT = False -else: - HAS_VIRT = True - -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - - -VIRT_FAILED = 1 -VIRT_SUCCESS = 0 -VIRT_UNAVAILABLE = 2 - -ALL_COMMANDS = [] -VM_COMMANDS = ['create', 'define', 'destroy', 'get_xml', 'pause', 'shutdown', 'status', 'start', 'stop', 'undefine', 'unpause'] -HOST_COMMANDS = ['freemem', 'info', 'list_vms', 'nodeinfo', 'virttype'] -ALL_COMMANDS.extend(VM_COMMANDS) -ALL_COMMANDS.extend(HOST_COMMANDS) - -VIRT_STATE_NAME_MAP = { - 0: 'running', - 1: 'running', - 2: 'running', - 3: 'paused', - 4: 'shutdown', - 5: 'shutdown', - 6: 'crashed', -} - - -class VMNotFound(Exception): - pass - - -class LibvirtConnection(object): - - def __init__(self, uri, module): - - self.module = module - - cmd = "uname -r" - rc, stdout, stderr = self.module.run_command(cmd) - - if "xen" in stdout: - conn = libvirt.open(None) - elif "esx" in uri: - auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT], [], None] - conn = libvirt.openAuth(uri, auth) - else: - conn = libvirt.open(uri) - - if not conn: - raise Exception("hypervisor connection failure") - - self.conn = conn - - def find_vm(self, vmid): - """ - Extra bonus feature: vmid = -1 returns a list of everything - """ - conn = self.conn - - vms = [] - - # this block of code borrowed from virt-manager: - # get working domain's name - ids = conn.listDomainsID() - for id in ids: - vm = conn.lookupByID(id) - vms.append(vm) - # get defined domain - names = conn.listDefinedDomains() - for name in names: - vm = conn.lookupByName(name) - vms.append(vm) - - if vmid == -1: - return vms - - for vm in vms: - if vm.name() == vmid: - return vm - - raise VMNotFound("virtual machine %s not found" % vmid) - - def shutdown(self, vmid): - return self.find_vm(vmid).shutdown() - - def pause(self, vmid): - return self.suspend(vmid) - - def unpause(self, vmid): - return self.resume(vmid) - - def suspend(self, vmid): - return self.find_vm(vmid).suspend() - - def resume(self, vmid): - return self.find_vm(vmid).resume() - - def create(self, vmid): - return self.find_vm(vmid).create() - - def destroy(self, vmid): - return self.find_vm(vmid).destroy() - - def undefine(self, vmid): - return self.find_vm(vmid).undefine() - - def get_status2(self, vm): - state = vm.info()[0] - return VIRT_STATE_NAME_MAP.get(state, "unknown") - - def get_status(self, vmid): - state = self.find_vm(vmid).info()[0] - return VIRT_STATE_NAME_MAP.get(state, "unknown") - - def nodeinfo(self): - return self.conn.getInfo() - - def get_type(self): - return self.conn.getType() - - def get_xml(self, vmid): - vm = self.conn.lookupByName(vmid) - return vm.XMLDesc(0) - - def get_maxVcpus(self, vmid): - vm = self.conn.lookupByName(vmid) - return vm.maxVcpus() - - def get_maxMemory(self, vmid): - vm = self.conn.lookupByName(vmid) - return vm.maxMemory() - - def getFreeMemory(self): - return self.conn.getFreeMemory() - - def get_autostart(self, vmid): - vm = self.conn.lookupByName(vmid) - return vm.autostart() - - def set_autostart(self, vmid, val): - vm = self.conn.lookupByName(vmid) - return vm.setAutostart(val) - - def define_from_xml(self, xml): - return self.conn.defineXML(xml) - - -class Virt(object): - - def __init__(self, uri, module): - self.module = module - self.uri = uri - - def __get_conn(self): - self.conn = LibvirtConnection(self.uri, self.module) - return self.conn - - def get_vm(self, vmid): - self.__get_conn() - return self.conn.find_vm(vmid) - - def state(self): - vms = self.list_vms() - state = [] - for vm in vms: - state_blurb = self.conn.get_status(vm) - state.append("%s %s" % (vm, state_blurb)) - return state - - def info(self): - vms = self.list_vms() - info = dict() - for vm in vms: - data = self.conn.find_vm(vm).info() - # libvirt returns maxMem, memory, and cpuTime as long()'s, which - # xmlrpclib tries to convert to regular int's during serialization. - # This throws exceptions, so convert them to strings here and - # assume the other end of the xmlrpc connection can figure things - # out or doesn't care. - info[vm] = dict( - state=VIRT_STATE_NAME_MAP.get(data[0], "unknown"), - maxMem=str(data[1]), - memory=str(data[2]), - nrVirtCpu=data[3], - cpuTime=str(data[4]), - autostart=self.conn.get_autostart(vm), - ) - - return info - - def nodeinfo(self): - self.__get_conn() - data = self.conn.nodeinfo() - info = dict( - cpumodel=str(data[0]), - phymemory=str(data[1]), - cpus=str(data[2]), - cpumhz=str(data[3]), - numanodes=str(data[4]), - sockets=str(data[5]), - cpucores=str(data[6]), - cputhreads=str(data[7]) - ) - return info - - def list_vms(self, state=None): - self.conn = self.__get_conn() - vms = self.conn.find_vm(-1) - results = [] - for x in vms: - try: - if state: - vmstate = self.conn.get_status2(x) - if vmstate == state: - results.append(x.name()) - else: - results.append(x.name()) - except Exception: - pass - return results - - def virttype(self): - return self.__get_conn().get_type() - - def autostart(self, vmid, as_flag): - self.conn = self.__get_conn() - # Change autostart flag only if needed - if self.conn.get_autostart(vmid) != as_flag: - self.conn.set_autostart(vmid, as_flag) - return True - - return False - - def freemem(self): - self.conn = self.__get_conn() - return self.conn.getFreeMemory() - - def shutdown(self, vmid): - """ Make the machine with the given vmid stop running. Whatever that takes. """ - self.__get_conn() - self.conn.shutdown(vmid) - return 0 - - def pause(self, vmid): - """ Pause the machine with the given vmid. """ - - self.__get_conn() - return self.conn.suspend(vmid) - - def unpause(self, vmid): - """ Unpause the machine with the given vmid. """ - - self.__get_conn() - return self.conn.resume(vmid) - - def create(self, vmid): - """ Start the machine via the given vmid """ - - self.__get_conn() - return self.conn.create(vmid) - - def start(self, vmid): - """ Start the machine via the given id/name """ - - self.__get_conn() - return self.conn.create(vmid) - - def destroy(self, vmid): - """ Pull the virtual power from the virtual domain, giving it virtually no time to virtually shut down. """ - self.__get_conn() - return self.conn.destroy(vmid) - - def undefine(self, vmid): - """ Stop a domain, and then wipe it from the face of the earth. (delete disk/config file) """ - - self.__get_conn() - return self.conn.undefine(vmid) - - def status(self, vmid): - """ - Return a state suitable for server consumption. Aka, codes.py values, not XM output. - """ - self.__get_conn() - return self.conn.get_status(vmid) - - def get_xml(self, vmid): - """ - Receive a Vm id as input - Return an xml describing vm config returned by a libvirt call - """ - - self.__get_conn() - return self.conn.get_xml(vmid) - - def get_maxVcpus(self, vmid): - """ - Gets the max number of VCPUs on a guest - """ - - self.__get_conn() - return self.conn.get_maxVcpus(vmid) - - def get_max_memory(self, vmid): - """ - Gets the max memory on a guest - """ - - self.__get_conn() - return self.conn.get_MaxMemory(vmid) - - def define(self, xml): - """ - Define a guest with the given xml - """ - self.__get_conn() - return self.conn.define_from_xml(xml) - - -def core(module): - - state = module.params.get('state', None) - autostart = module.params.get('autostart', None) - guest = module.params.get('name', None) - command = module.params.get('command', None) - uri = module.params.get('uri', None) - xml = module.params.get('xml', None) - - v = Virt(uri, module) - res = dict() - - if state and command == 'list_vms': - res = v.list_vms(state=state) - if not isinstance(res, dict): - res = {command: res} - return VIRT_SUCCESS, res - - if autostart is not None and command != 'define': - if not guest: - module.fail_json(msg="autostart requires 1 argument: name") - try: - v.get_vm(guest) - except VMNotFound: - module.fail_json(msg="domain %s not found" % guest) - res['changed'] = v.autostart(guest, autostart) - if not command and not state: - return VIRT_SUCCESS, res - - if state: - if not guest: - module.fail_json(msg="state change requires a guest specified") - - if state == 'running': - if v.status(guest) == 'paused': - res['changed'] = True - res['msg'] = v.unpause(guest) - elif v.status(guest) != 'running': - res['changed'] = True - res['msg'] = v.start(guest) - elif state == 'shutdown': - if v.status(guest) != 'shutdown': - res['changed'] = True - res['msg'] = v.shutdown(guest) - elif state == 'destroyed': - if v.status(guest) != 'shutdown': - res['changed'] = True - res['msg'] = v.destroy(guest) - elif state == 'paused': - if v.status(guest) == 'running': - res['changed'] = True - res['msg'] = v.pause(guest) - else: - module.fail_json(msg="unexpected state") - - return VIRT_SUCCESS, res - - if command: - if command in VM_COMMANDS: - if command == 'define': - if not xml: - module.fail_json(msg="define requires xml argument") - if guest: - # there might be a mismatch between quest 'name' in the module and in the xml - module.warn("'xml' is given - ignoring 'name'") - found_name = re.search('(.*)', xml).groups() - if found_name: - domain_name = found_name[0] - else: - module.fail_json(msg="Could not find domain 'name' in xml") - - # From libvirt docs (https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainDefineXML): - # -- A previous definition for this domain would be overridden if it already exists. - # - # In real world testing with libvirt versions 1.2.17-13, 2.0.0-10 and 3.9.0-14 - # on qemu and lxc domains results in: - # operation failed: domain '' already exists with - # - # In case a domain would be indeed overwritten, we should protect idempotency: - try: - existing_domain = v.get_vm(domain_name) - except VMNotFound: - existing_domain = None - try: - domain = v.define(xml) - if existing_domain: - # if we are here, then libvirt redefined existing domain as the doc promised - if existing_domain.XMLDesc() != domain.XMLDesc(): - res = {'changed': True, 'change_reason': 'config changed'} - else: - res = {'changed': True, 'created': domain.name()} - except libvirtError as e: - if e.get_error_code() != 9: # 9 means 'domain already exists' error - module.fail_json(msg='libvirtError: %s' % e.message) - if autostart is not None and v.autostart(domain_name, autostart): - res = {'changed': True, 'change_reason': 'autostart'} - - elif not guest: - module.fail_json(msg="%s requires 1 argument: guest" % command) - else: - res = getattr(v, command)(guest) - if not isinstance(res, dict): - res = {command: res} - - return VIRT_SUCCESS, res - - elif hasattr(v, command): - res = getattr(v, command)() - if not isinstance(res, dict): - res = {command: res} - return VIRT_SUCCESS, res - - else: - module.fail_json(msg="Command %s not recognized" % command) - - module.fail_json(msg="expected state or command parameter to be specified") - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(type='str', aliases=['guest']), - state=dict(type='str', choices=['destroyed', 'pause', 'running', 'shutdown']), - autostart=dict(type='bool'), - command=dict(type='str', choices=ALL_COMMANDS), - uri=dict(type='str', default='qemu:///system'), - xml=dict(type='str'), - ), - ) - - if not HAS_VIRT: - module.fail_json(msg='The `libvirt` module is not importable. Check the requirements.') - - rc = VIRT_SUCCESS - try: - rc, result = core(module) - except Exception as e: - module.fail_json(msg=to_native(e), exception=traceback.format_exc()) - - if rc != 0: # something went wrong emit the msg - module.fail_json(rc=rc, msg=result) - else: - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/cloud/misc/virt_net.py b/plugins/modules/cloud/misc/virt_net.py deleted file mode 100644 index 86c80072e6..0000000000 --- a/plugins/modules/cloud/misc/virt_net.py +++ /dev/null @@ -1,638 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2015, Maciej Delmanowski -# 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: virt_net -author: "Maciej Delmanowski (@drybjed)" -short_description: Manage libvirt network configuration -description: - - Manage I(libvirt) networks. -options: - name: - required: true - aliases: ['network'] - description: - - name of the network being managed. Note that network must be previously - defined with xml. - state: - required: false - choices: [ "active", "inactive", "present", "absent" ] - description: - - specify which state you want a network to be in. - If 'active', network will be started. - If 'present', ensure that network is present but do not change its - state; if it's missing, you need to specify xml argument. - If 'inactive', network will be stopped. - If 'undefined' or 'absent', network will be removed from I(libvirt) configuration. - command: - required: false - choices: [ "define", "create", "start", "stop", "destroy", - "undefine", "get_xml", "list_nets", "facts", - "info", "status", "modify"] - description: - - in addition to state management, various non-idempotent commands are available. - See examples. - Modify was added in version 2.1 - autostart: - required: false - type: bool - description: - - Specify if a given network should be started automatically on system boot. - uri: - required: false - default: "qemu:///system" - description: - - libvirt connection uri. - xml: - required: false - description: - - XML document used with the define command. -requirements: - - "python >= 2.6" - - "python-libvirt" - - "python-lxml" -''' - -EXAMPLES = ''' -# Define a new network -- virt_net: - command: define - name: br_nat - xml: '{{ lookup("template", "network/bridge.xml.j2") }}' - -# Start a network -- virt_net: - command: create - name: br_nat - -# List available networks -- virt_net: - command: list_nets - -# Get XML data of a specified network -- virt_net: - command: get_xml - name: br_nat - -# Stop a network -- virt_net: - command: destroy - name: br_nat - -# Undefine a network -- virt_net: - command: undefine - name: br_nat - -# Gather facts about networks -# Facts will be available as 'ansible_libvirt_networks' -- virt_net: - command: facts - -# Gather information about network managed by 'libvirt' remotely using uri -- virt_net: - command: info - uri: '{{ item }}' - with_items: '{{ libvirt_uris }}' - register: networks - -# Ensure that a network is active (needs to be defined and built first) -- virt_net: - state: active - name: br_nat - -# Ensure that a network is inactive -- virt_net: - state: inactive - name: br_nat - -# Ensure that a given network will be started at boot -- virt_net: - autostart: yes - name: br_nat - -# Disable autostart for a given network -- virt_net: - autostart: no - name: br_nat - -# Add a new host in the dhcp pool -- virt_net: - name: br_nat - command: modify - xml: "" -''' - -try: - import libvirt -except ImportError: - HAS_VIRT = False -else: - HAS_VIRT = True - -try: - from lxml import etree -except ImportError: - HAS_XML = False -else: - HAS_XML = True - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - - -VIRT_FAILED = 1 -VIRT_SUCCESS = 0 -VIRT_UNAVAILABLE = 2 - -ALL_COMMANDS = [] -ENTRY_COMMANDS = ['create', 'status', 'start', 'stop', - 'undefine', 'destroy', 'get_xml', 'define', - 'modify'] -HOST_COMMANDS = ['list_nets', 'facts', 'info'] -ALL_COMMANDS.extend(ENTRY_COMMANDS) -ALL_COMMANDS.extend(HOST_COMMANDS) - -ENTRY_STATE_ACTIVE_MAP = { - 0: "inactive", - 1: "active" -} - -ENTRY_STATE_AUTOSTART_MAP = { - 0: "no", - 1: "yes" -} - -ENTRY_STATE_PERSISTENT_MAP = { - 0: "no", - 1: "yes" -} - - -class EntryNotFound(Exception): - pass - - -class LibvirtConnection(object): - - def __init__(self, uri, module): - - self.module = module - - conn = libvirt.open(uri) - - if not conn: - raise Exception("hypervisor connection failure") - - self.conn = conn - - def find_entry(self, entryid): - if entryid == -1: # Get active entries - names = self.conn.listNetworks() + self.conn.listDefinedNetworks() - return [self.conn.networkLookupByName(n) for n in names] - - try: - return self.conn.networkLookupByName(entryid) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_NETWORK: - raise EntryNotFound("network %s not found" % entryid) - raise - - def create(self, entryid): - if not self.module.check_mode: - return self.find_entry(entryid).create() - else: - try: - state = self.find_entry(entryid).isActive() - except Exception: - return self.module.exit_json(changed=True) - if not state: - return self.module.exit_json(changed=True) - - def modify(self, entryid, xml): - network = self.find_entry(entryid) - # identify what type of entry is given in the xml - new_data = etree.fromstring(xml) - old_data = etree.fromstring(network.XMLDesc(0)) - if new_data.tag == 'host': - mac_addr = new_data.get('mac') - hosts = old_data.xpath('/network/ip/dhcp/host') - # find the one mac we're looking for - host = None - for h in hosts: - if h.get('mac') == mac_addr: - host = h - break - if host is None: - # add the host - if not self.module.check_mode: - res = network.update(libvirt.VIR_NETWORK_UPDATE_COMMAND_ADD_LAST, - libvirt.VIR_NETWORK_SECTION_IP_DHCP_HOST, - -1, xml, libvirt.VIR_NETWORK_UPDATE_AFFECT_CURRENT) - else: - # pretend there was a change - res = 0 - if res == 0: - return True - else: - # change the host - if host.get('name') == new_data.get('name') and host.get('ip') == new_data.get('ip'): - return False - else: - if not self.module.check_mode: - res = network.update(libvirt.VIR_NETWORK_UPDATE_COMMAND_MODIFY, - libvirt.VIR_NETWORK_SECTION_IP_DHCP_HOST, - -1, xml, libvirt.VIR_NETWORK_UPDATE_AFFECT_CURRENT) - else: - # pretend there was a change - res = 0 - if res == 0: - return True - # command, section, parentIndex, xml, flags=0 - self.module.fail_json(msg='updating this is not supported yet %s' % to_native(xml)) - - def destroy(self, entryid): - if not self.module.check_mode: - return self.find_entry(entryid).destroy() - else: - if self.find_entry(entryid).isActive(): - return self.module.exit_json(changed=True) - - def undefine(self, entryid): - entry = None - try: - entry = self.find_entry(entryid) - found = True - except EntryNotFound: - found = False - - if found: - return self.find_entry(entryid).undefine() - - if self.module.check_mode: - return self.module.exit_json(changed=found) - - def get_status2(self, entry): - state = entry.isActive() - return ENTRY_STATE_ACTIVE_MAP.get(state, "unknown") - - def get_status(self, entryid): - if not self.module.check_mode: - state = self.find_entry(entryid).isActive() - return ENTRY_STATE_ACTIVE_MAP.get(state, "unknown") - else: - try: - state = self.find_entry(entryid).isActive() - return ENTRY_STATE_ACTIVE_MAP.get(state, "unknown") - except Exception: - return ENTRY_STATE_ACTIVE_MAP.get("inactive", "unknown") - - def get_uuid(self, entryid): - return self.find_entry(entryid).UUIDString() - - def get_xml(self, entryid): - return self.find_entry(entryid).XMLDesc(0) - - def get_forward(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - try: - result = xml.xpath('/network/forward')[0].get('mode') - except Exception: - raise ValueError('Forward mode not specified') - return result - - def get_domain(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - try: - result = xml.xpath('/network/domain')[0].get('name') - except Exception: - raise ValueError('Domain not specified') - return result - - def get_macaddress(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - try: - result = xml.xpath('/network/mac')[0].get('address') - except Exception: - raise ValueError('MAC address not specified') - return result - - def get_autostart(self, entryid): - state = self.find_entry(entryid).autostart() - return ENTRY_STATE_AUTOSTART_MAP.get(state, "unknown") - - def get_autostart2(self, entryid): - if not self.module.check_mode: - return self.find_entry(entryid).autostart() - else: - try: - return self.find_entry(entryid).autostart() - except Exception: - return self.module.exit_json(changed=True) - - def set_autostart(self, entryid, val): - if not self.module.check_mode: - return self.find_entry(entryid).setAutostart(val) - else: - try: - state = self.find_entry(entryid).autostart() - except Exception: - return self.module.exit_json(changed=True) - if bool(state) != val: - return self.module.exit_json(changed=True) - - def get_bridge(self, entryid): - return self.find_entry(entryid).bridgeName() - - def get_persistent(self, entryid): - state = self.find_entry(entryid).isPersistent() - return ENTRY_STATE_PERSISTENT_MAP.get(state, "unknown") - - def get_dhcp_leases(self, entryid): - network = self.find_entry(entryid) - return network.DHCPLeases() - - def define_from_xml(self, entryid, xml): - if not self.module.check_mode: - return self.conn.networkDefineXML(xml) - else: - try: - self.find_entry(entryid) - except Exception: - return self.module.exit_json(changed=True) - - -class VirtNetwork(object): - - def __init__(self, uri, module): - self.module = module - self.uri = uri - self.conn = LibvirtConnection(self.uri, self.module) - - def get_net(self, entryid): - return self.conn.find_entry(entryid) - - def list_nets(self, state=None): - results = [] - for entry in self.conn.find_entry(-1): - if state: - if state == self.conn.get_status2(entry): - results.append(entry.name()) - else: - results.append(entry.name()) - return results - - def state(self): - results = [] - for entry in self.list_nets(): - state_blurb = self.conn.get_status(entry) - results.append("%s %s" % (entry, state_blurb)) - return results - - def autostart(self, entryid): - return self.conn.set_autostart(entryid, True) - - def get_autostart(self, entryid): - return self.conn.get_autostart2(entryid) - - def set_autostart(self, entryid, state): - return self.conn.set_autostart(entryid, state) - - def create(self, entryid): - if self.conn.get_status(entryid) == "active": - return - try: - return self.conn.create(entryid) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NETWORK_EXIST: - return None - raise - - def modify(self, entryid, xml): - return self.conn.modify(entryid, xml) - - def start(self, entryid): - return self.create(entryid) - - def stop(self, entryid): - if self.conn.get_status(entryid) == "active": - return self.conn.destroy(entryid) - - def destroy(self, entryid): - return self.stop(entryid) - - def undefine(self, entryid): - return self.conn.undefine(entryid) - - def status(self, entryid): - return self.conn.get_status(entryid) - - def get_xml(self, entryid): - return self.conn.get_xml(entryid) - - def define(self, entryid, xml): - return self.conn.define_from_xml(entryid, xml) - - def info(self): - return self.facts(facts_mode='info') - - def facts(self, name=None, facts_mode='facts'): - results = dict() - if name: - entries = [name] - else: - entries = self.list_nets() - for entry in entries: - results[entry] = dict() - results[entry]["autostart"] = self.conn.get_autostart(entry) - results[entry]["persistent"] = self.conn.get_persistent(entry) - results[entry]["state"] = self.conn.get_status(entry) - results[entry]["bridge"] = self.conn.get_bridge(entry) - results[entry]["uuid"] = self.conn.get_uuid(entry) - try: - results[entry]["dhcp_leases"] = self.conn.get_dhcp_leases(entry) - # not supported on RHEL 6 - except AttributeError: - pass - - try: - results[entry]["forward_mode"] = self.conn.get_forward(entry) - except ValueError: - pass - - try: - results[entry]["domain"] = self.conn.get_domain(entry) - except ValueError: - pass - - try: - results[entry]["macaddress"] = self.conn.get_macaddress(entry) - except ValueError: - pass - - facts = dict() - if facts_mode == 'facts': - facts["ansible_facts"] = dict() - facts["ansible_facts"]["ansible_libvirt_networks"] = results - elif facts_mode == 'info': - facts['networks'] = results - return facts - - -def core(module): - - state = module.params.get('state', None) - name = module.params.get('name', None) - command = module.params.get('command', None) - uri = module.params.get('uri', None) - xml = module.params.get('xml', None) - autostart = module.params.get('autostart', None) - - v = VirtNetwork(uri, module) - res = {} - - if state and command == 'list_nets': - res = v.list_nets(state=state) - if not isinstance(res, dict): - res = {command: res} - return VIRT_SUCCESS, res - - if state: - if not name: - module.fail_json(msg="state change requires a specified name") - - res['changed'] = False - if state in ['active']: - if v.status(name) != 'active': - res['changed'] = True - res['msg'] = v.start(name) - elif state in ['present']: - try: - v.get_net(name) - except EntryNotFound: - if not xml: - module.fail_json(msg="network '" + name + "' not present, but xml not specified") - v.define(name, xml) - res = {'changed': True, 'created': name} - elif state in ['inactive']: - entries = v.list_nets() - if name in entries: - if v.status(name) != 'inactive': - res['changed'] = True - res['msg'] = v.destroy(name) - elif state in ['undefined', 'absent']: - entries = v.list_nets() - if name in entries: - if v.status(name) != 'inactive': - v.destroy(name) - res['changed'] = True - res['msg'] = v.undefine(name) - else: - module.fail_json(msg="unexpected state") - - return VIRT_SUCCESS, res - - if command: - if command in ENTRY_COMMANDS: - if not name: - module.fail_json(msg="%s requires 1 argument: name" % command) - if command in ('define', 'modify'): - if not xml: - module.fail_json(msg=command + " requires xml argument") - try: - v.get_net(name) - except EntryNotFound: - v.define(name, xml) - res = {'changed': True, 'created': name} - else: - if command == 'modify': - mod = v.modify(name, xml) - res = {'changed': mod, 'modified': name} - return VIRT_SUCCESS, res - res = getattr(v, command)(name) - if not isinstance(res, dict): - res = {command: res} - return VIRT_SUCCESS, res - - elif hasattr(v, command): - if command == 'facts' and name: - res = v.facts(name) - else: - res = getattr(v, command)() - if not isinstance(res, dict): - res = {command: res} - return VIRT_SUCCESS, res - - else: - module.fail_json(msg="Command %s not recognized" % command) - - if autostart is not None: - if not name: - module.fail_json(msg="state change requires a specified name") - - res['changed'] = False - if autostart: - if not v.get_autostart(name): - res['changed'] = True - res['msg'] = v.set_autostart(name, True) - else: - if v.get_autostart(name): - res['changed'] = True - res['msg'] = v.set_autostart(name, False) - - return VIRT_SUCCESS, res - - module.fail_json(msg="expected state or command parameter to be specified") - - -def main(): - - module = AnsibleModule( - argument_spec=dict( - name=dict(aliases=['network']), - state=dict(choices=['active', 'inactive', 'present', 'absent']), - command=dict(choices=ALL_COMMANDS), - uri=dict(default='qemu:///system'), - xml=dict(), - autostart=dict(type='bool') - ), - supports_check_mode=True - ) - - if not HAS_VIRT: - module.fail_json( - msg='The `libvirt` module is not importable. Check the requirements.' - ) - - if not HAS_XML: - module.fail_json( - msg='The `lxml` module is not importable. Check the requirements.' - ) - - rc = VIRT_SUCCESS - try: - rc, result = core(module) - except Exception as e: - module.fail_json(msg=str(e)) - - if rc != 0: # something went wrong emit the msg - module.fail_json(rc=rc, msg=result) - else: - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/cloud/misc/virt_pool.py b/plugins/modules/cloud/misc/virt_pool.py deleted file mode 100644 index 656aa1e300..0000000000 --- a/plugins/modules/cloud/misc/virt_pool.py +++ /dev/null @@ -1,711 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2015, Maciej Delmanowski -# 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: virt_pool -author: "Maciej Delmanowski (@drybjed)" -short_description: Manage libvirt storage pools -description: - - Manage I(libvirt) storage pools. -options: - name: - required: false - aliases: [ "pool" ] - description: - - name of the storage pool being managed. Note that pool must be previously - defined with xml. - state: - required: false - choices: [ "active", "inactive", "present", "absent", "undefined", "deleted" ] - description: - - specify which state you want a storage pool to be in. - If 'active', pool will be started. - If 'present', ensure that pool is present but do not change its - state; if it's missing, you need to specify xml argument. - If 'inactive', pool will be stopped. - If 'undefined' or 'absent', pool will be removed from I(libvirt) configuration. - If 'deleted', pool contents will be deleted and then pool undefined. - command: - required: false - choices: [ "define", "build", "create", "start", "stop", "destroy", - "delete", "undefine", "get_xml", "list_pools", "facts", - "info", "status" ] - description: - - in addition to state management, various non-idempotent commands are available. - See examples. - autostart: - required: false - type: bool - description: - - Specify if a given storage pool should be started automatically on system boot. - uri: - required: false - default: "qemu:///system" - description: - - I(libvirt) connection uri. - xml: - required: false - description: - - XML document used with the define command. - mode: - required: false - choices: [ 'new', 'repair', 'resize', 'no_overwrite', 'overwrite', 'normal', 'zeroed' ] - description: - - Pass additional parameters to 'build' or 'delete' commands. -requirements: - - "python >= 2.6" - - "python-libvirt" - - "python-lxml" -''' - -EXAMPLES = ''' -# Define a new storage pool -- virt_pool: - command: define - name: vms - xml: '{{ lookup("template", "pool/dir.xml.j2") }}' - -# Build a storage pool if it does not exist -- virt_pool: - command: build - name: vms - -# Start a storage pool -- virt_pool: - command: create - name: vms - -# List available pools -- virt_pool: - command: list_pools - -# Get XML data of a specified pool -- virt_pool: - command: get_xml - name: vms - -# Stop a storage pool -- virt_pool: - command: destroy - name: vms - -# Delete a storage pool (destroys contents) -- virt_pool: - command: delete - name: vms - -# Undefine a storage pool -- virt_pool: - command: undefine - name: vms - -# Gather facts about storage pools -# Facts will be available as 'ansible_libvirt_pools' -- virt_pool: - command: facts - -# Gather information about pools managed by 'libvirt' remotely using uri -- virt_pool: - command: info - uri: '{{ item }}' - with_items: '{{ libvirt_uris }}' - register: storage_pools - -# Ensure that a pool is active (needs to be defined and built first) -- virt_pool: - state: active - name: vms - -# Ensure that a pool is inactive -- virt_pool: - state: inactive - name: vms - -# Ensure that a given pool will be started at boot -- virt_pool: - autostart: yes - name: vms - -# Disable autostart for a given pool -- virt_pool: - autostart: no - name: vms -''' - -try: - import libvirt -except ImportError: - HAS_VIRT = False -else: - HAS_VIRT = True - -try: - from lxml import etree -except ImportError: - HAS_XML = False -else: - HAS_XML = True - -from ansible.module_utils.basic import AnsibleModule - - -VIRT_FAILED = 1 -VIRT_SUCCESS = 0 -VIRT_UNAVAILABLE = 2 - -ALL_COMMANDS = [] -ENTRY_COMMANDS = ['create', 'status', 'start', 'stop', 'build', 'delete', - 'undefine', 'destroy', 'get_xml', 'define', 'refresh'] -HOST_COMMANDS = ['list_pools', 'facts', 'info'] -ALL_COMMANDS.extend(ENTRY_COMMANDS) -ALL_COMMANDS.extend(HOST_COMMANDS) - -ENTRY_STATE_ACTIVE_MAP = { - 0: "inactive", - 1: "active" -} - -ENTRY_STATE_AUTOSTART_MAP = { - 0: "no", - 1: "yes" -} - -ENTRY_STATE_PERSISTENT_MAP = { - 0: "no", - 1: "yes" -} - -ENTRY_STATE_INFO_MAP = { - 0: "inactive", - 1: "building", - 2: "running", - 3: "degraded", - 4: "inaccessible" -} - -ENTRY_BUILD_FLAGS_MAP = { - "new": 0, - "repair": 1, - "resize": 2, - "no_overwrite": 4, - "overwrite": 8 -} - -ENTRY_DELETE_FLAGS_MAP = { - "normal": 0, - "zeroed": 1 -} - -ALL_MODES = [] -ALL_MODES.extend(ENTRY_BUILD_FLAGS_MAP.keys()) -ALL_MODES.extend(ENTRY_DELETE_FLAGS_MAP.keys()) - - -class EntryNotFound(Exception): - pass - - -class LibvirtConnection(object): - - def __init__(self, uri, module): - - self.module = module - - conn = libvirt.open(uri) - - if not conn: - raise Exception("hypervisor connection failure") - - self.conn = conn - - def find_entry(self, entryid): - # entryid = -1 returns a list of everything - - results = [] - - # Get active entries - for name in self.conn.listStoragePools(): - entry = self.conn.storagePoolLookupByName(name) - results.append(entry) - - # Get inactive entries - for name in self.conn.listDefinedStoragePools(): - entry = self.conn.storagePoolLookupByName(name) - results.append(entry) - - if entryid == -1: - return results - - for entry in results: - if entry.name() == entryid: - return entry - - raise EntryNotFound("storage pool %s not found" % entryid) - - def create(self, entryid): - if not self.module.check_mode: - return self.find_entry(entryid).create() - else: - try: - state = self.find_entry(entryid).isActive() - except Exception: - return self.module.exit_json(changed=True) - if not state: - return self.module.exit_json(changed=True) - - def destroy(self, entryid): - if not self.module.check_mode: - return self.find_entry(entryid).destroy() - else: - if self.find_entry(entryid).isActive(): - return self.module.exit_json(changed=True) - - def undefine(self, entryid): - if not self.module.check_mode: - return self.find_entry(entryid).undefine() - else: - if not self.find_entry(entryid): - return self.module.exit_json(changed=True) - - def get_status2(self, entry): - state = entry.isActive() - return ENTRY_STATE_ACTIVE_MAP.get(state, "unknown") - - def get_status(self, entryid): - if not self.module.check_mode: - state = self.find_entry(entryid).isActive() - return ENTRY_STATE_ACTIVE_MAP.get(state, "unknown") - else: - try: - state = self.find_entry(entryid).isActive() - return ENTRY_STATE_ACTIVE_MAP.get(state, "unknown") - except Exception: - return ENTRY_STATE_ACTIVE_MAP.get("inactive", "unknown") - - def get_uuid(self, entryid): - return self.find_entry(entryid).UUIDString() - - def get_xml(self, entryid): - return self.find_entry(entryid).XMLDesc(0) - - def get_info(self, entryid): - return self.find_entry(entryid).info() - - def get_volume_count(self, entryid): - return self.find_entry(entryid).numOfVolumes() - - def get_volume_names(self, entryid): - return self.find_entry(entryid).listVolumes() - - def get_devices(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - if xml.xpath('/pool/source/device'): - result = [] - for device in xml.xpath('/pool/source/device'): - result.append(device.get('path')) - try: - return result - except Exception: - raise ValueError('No devices specified') - - def get_format(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - try: - result = xml.xpath('/pool/source/format')[0].get('type') - except Exception: - raise ValueError('Format not specified') - return result - - def get_host(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - try: - result = xml.xpath('/pool/source/host')[0].get('name') - except Exception: - raise ValueError('Host not specified') - return result - - def get_source_path(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - try: - result = xml.xpath('/pool/source/dir')[0].get('path') - except Exception: - raise ValueError('Source path not specified') - return result - - def get_path(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - return xml.xpath('/pool/target/path')[0].text - - def get_type(self, entryid): - xml = etree.fromstring(self.find_entry(entryid).XMLDesc(0)) - return xml.get('type') - - def build(self, entryid, flags): - if not self.module.check_mode: - return self.find_entry(entryid).build(flags) - else: - try: - state = self.find_entry(entryid) - except Exception: - return self.module.exit_json(changed=True) - if not state: - return self.module.exit_json(changed=True) - - def delete(self, entryid, flags): - if not self.module.check_mode: - return self.find_entry(entryid).delete(flags) - else: - try: - state = self.find_entry(entryid) - except Exception: - return self.module.exit_json(changed=True) - if state: - return self.module.exit_json(changed=True) - - def get_autostart(self, entryid): - state = self.find_entry(entryid).autostart() - return ENTRY_STATE_AUTOSTART_MAP.get(state, "unknown") - - def get_autostart2(self, entryid): - if not self.module.check_mode: - return self.find_entry(entryid).autostart() - else: - try: - return self.find_entry(entryid).autostart() - except Exception: - return self.module.exit_json(changed=True) - - def set_autostart(self, entryid, val): - if not self.module.check_mode: - return self.find_entry(entryid).setAutostart(val) - else: - try: - state = self.find_entry(entryid).autostart() - except Exception: - return self.module.exit_json(changed=True) - if bool(state) != val: - return self.module.exit_json(changed=True) - - def refresh(self, entryid): - return self.find_entry(entryid).refresh() - - def get_persistent(self, entryid): - state = self.find_entry(entryid).isPersistent() - return ENTRY_STATE_PERSISTENT_MAP.get(state, "unknown") - - def define_from_xml(self, entryid, xml): - if not self.module.check_mode: - return self.conn.storagePoolDefineXML(xml) - else: - try: - self.find_entry(entryid) - except Exception: - return self.module.exit_json(changed=True) - - -class VirtStoragePool(object): - - def __init__(self, uri, module): - self.module = module - self.uri = uri - self.conn = LibvirtConnection(self.uri, self.module) - - def get_pool(self, entryid): - return self.conn.find_entry(entryid) - - def list_pools(self, state=None): - results = [] - for entry in self.conn.find_entry(-1): - if state: - if state == self.conn.get_status2(entry): - results.append(entry.name()) - else: - results.append(entry.name()) - return results - - def state(self): - results = [] - for entry in self.list_pools(): - state_blurb = self.conn.get_status(entry) - results.append("%s %s" % (entry, state_blurb)) - return results - - def autostart(self, entryid): - return self.conn.set_autostart(entryid, True) - - def get_autostart(self, entryid): - return self.conn.get_autostart2(entryid) - - def set_autostart(self, entryid, state): - return self.conn.set_autostart(entryid, state) - - def create(self, entryid): - return self.conn.create(entryid) - - def start(self, entryid): - return self.conn.create(entryid) - - def stop(self, entryid): - return self.conn.destroy(entryid) - - def destroy(self, entryid): - return self.conn.destroy(entryid) - - def undefine(self, entryid): - return self.conn.undefine(entryid) - - def status(self, entryid): - return self.conn.get_status(entryid) - - def get_xml(self, entryid): - return self.conn.get_xml(entryid) - - def define(self, entryid, xml): - return self.conn.define_from_xml(entryid, xml) - - def build(self, entryid, flags): - return self.conn.build(entryid, ENTRY_BUILD_FLAGS_MAP.get(flags, 0)) - - def delete(self, entryid, flags): - return self.conn.delete(entryid, ENTRY_DELETE_FLAGS_MAP.get(flags, 0)) - - def refresh(self, entryid): - return self.conn.refresh(entryid) - - def info(self): - return self.facts(facts_mode='info') - - def facts(self, facts_mode='facts'): - results = dict() - for entry in self.list_pools(): - results[entry] = dict() - if self.conn.find_entry(entry): - data = self.conn.get_info(entry) - # libvirt returns maxMem, memory, and cpuTime as long()'s, which - # xmlrpclib tries to convert to regular int's during serialization. - # This throws exceptions, so convert them to strings here and - # assume the other end of the xmlrpc connection can figure things - # out or doesn't care. - results[entry] = { - "status": ENTRY_STATE_INFO_MAP.get(data[0], "unknown"), - "size_total": str(data[1]), - "size_used": str(data[2]), - "size_available": str(data[3]), - } - results[entry]["autostart"] = self.conn.get_autostart(entry) - results[entry]["persistent"] = self.conn.get_persistent(entry) - results[entry]["state"] = self.conn.get_status(entry) - results[entry]["path"] = self.conn.get_path(entry) - results[entry]["type"] = self.conn.get_type(entry) - results[entry]["uuid"] = self.conn.get_uuid(entry) - if self.conn.find_entry(entry).isActive(): - results[entry]["volume_count"] = self.conn.get_volume_count(entry) - results[entry]["volumes"] = list() - for volume in self.conn.get_volume_names(entry): - results[entry]["volumes"].append(volume) - else: - results[entry]["volume_count"] = -1 - - try: - results[entry]["host"] = self.conn.get_host(entry) - except ValueError: - pass - - try: - results[entry]["source_path"] = self.conn.get_source_path(entry) - except ValueError: - pass - - try: - results[entry]["format"] = self.conn.get_format(entry) - except ValueError: - pass - - try: - devices = self.conn.get_devices(entry) - results[entry]["devices"] = devices - except ValueError: - pass - - else: - results[entry]["state"] = self.conn.get_status(entry) - - facts = dict() - if facts_mode == 'facts': - facts["ansible_facts"] = dict() - facts["ansible_facts"]["ansible_libvirt_pools"] = results - elif facts_mode == 'info': - facts['pools'] = results - return facts - - -def core(module): - - state = module.params.get('state', None) - name = module.params.get('name', None) - command = module.params.get('command', None) - uri = module.params.get('uri', None) - xml = module.params.get('xml', None) - autostart = module.params.get('autostart', None) - mode = module.params.get('mode', None) - - v = VirtStoragePool(uri, module) - res = {} - - if state and command == 'list_pools': - res = v.list_pools(state=state) - if not isinstance(res, dict): - res = {command: res} - return VIRT_SUCCESS, res - - if state: - if not name: - module.fail_json(msg="state change requires a specified name") - - res['changed'] = False - if state in ['active']: - if v.status(name) != 'active': - res['changed'] = True - res['msg'] = v.start(name) - elif state in ['present']: - try: - v.get_pool(name) - except EntryNotFound: - if not xml: - module.fail_json(msg="storage pool '" + name + "' not present, but xml not specified") - v.define(name, xml) - res = {'changed': True, 'created': name} - elif state in ['inactive']: - entries = v.list_pools() - if name in entries: - if v.status(name) != 'inactive': - res['changed'] = True - res['msg'] = v.destroy(name) - elif state in ['undefined', 'absent']: - entries = v.list_pools() - if name in entries: - if v.status(name) != 'inactive': - v.destroy(name) - res['changed'] = True - res['msg'] = v.undefine(name) - elif state in ['deleted']: - entries = v.list_pools() - if name in entries: - if v.status(name) != 'inactive': - v.destroy(name) - v.delete(name, mode) - res['changed'] = True - res['msg'] = v.undefine(name) - else: - module.fail_json(msg="unexpected state") - - return VIRT_SUCCESS, res - - if command: - if command in ENTRY_COMMANDS: - if not name: - module.fail_json(msg="%s requires 1 argument: name" % command) - if command == 'define': - if not xml: - module.fail_json(msg="define requires xml argument") - try: - v.get_pool(name) - except EntryNotFound: - v.define(name, xml) - res = {'changed': True, 'created': name} - return VIRT_SUCCESS, res - elif command == 'build': - res = v.build(name, mode) - if not isinstance(res, dict): - res = {'changed': True, command: res} - return VIRT_SUCCESS, res - elif command == 'delete': - res = v.delete(name, mode) - if not isinstance(res, dict): - res = {'changed': True, command: res} - return VIRT_SUCCESS, res - res = getattr(v, command)(name) - if not isinstance(res, dict): - res = {command: res} - return VIRT_SUCCESS, res - - elif hasattr(v, command): - res = getattr(v, command)() - if not isinstance(res, dict): - res = {command: res} - return VIRT_SUCCESS, res - - else: - module.fail_json(msg="Command %s not recognized" % command) - - if autostart is not None: - if not name: - module.fail_json(msg="state change requires a specified name") - - res['changed'] = False - if autostart: - if not v.get_autostart(name): - res['changed'] = True - res['msg'] = v.set_autostart(name, True) - else: - if v.get_autostart(name): - res['changed'] = True - res['msg'] = v.set_autostart(name, False) - - return VIRT_SUCCESS, res - - module.fail_json(msg="expected state or command parameter to be specified") - - -def main(): - - module = AnsibleModule( - argument_spec=dict( - name=dict(aliases=['pool']), - state=dict(choices=['active', 'inactive', 'present', 'absent', 'undefined', 'deleted']), - command=dict(choices=ALL_COMMANDS), - uri=dict(default='qemu:///system'), - xml=dict(), - autostart=dict(type='bool'), - mode=dict(choices=ALL_MODES), - ), - supports_check_mode=True - ) - - if not HAS_VIRT: - module.fail_json( - msg='The `libvirt` module is not importable. Check the requirements.' - ) - - if not HAS_XML: - module.fail_json( - msg='The `lxml` module is not importable. Check the requirements.' - ) - - rc = VIRT_SUCCESS - try: - rc, result = core(module) - except Exception as e: - module.fail_json(msg=str(e)) - - if rc != 0: # something went wrong emit the msg - module.fail_json(rc=rc, msg=result) - else: - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/virt.py b/plugins/modules/virt.py deleted file mode 120000 index 1f83883653..0000000000 --- a/plugins/modules/virt.py +++ /dev/null @@ -1 +0,0 @@ -./cloud/misc/virt.py \ No newline at end of file diff --git a/plugins/modules/virt_net.py b/plugins/modules/virt_net.py deleted file mode 120000 index 78bf2f5afa..0000000000 --- a/plugins/modules/virt_net.py +++ /dev/null @@ -1 +0,0 @@ -./cloud/misc/virt_net.py \ No newline at end of file diff --git a/plugins/modules/virt_pool.py b/plugins/modules/virt_pool.py deleted file mode 120000 index eff2f69f88..0000000000 --- a/plugins/modules/virt_pool.py +++ /dev/null @@ -1 +0,0 @@ -./cloud/misc/virt_pool.py \ No newline at end of file diff --git a/scripts/inventory/libvirt_lxc.py b/scripts/inventory/libvirt_lxc.py deleted file mode 100644 index c0d84dbaaf..0000000000 --- a/scripts/inventory/libvirt_lxc.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -# (c) 2013, Michael Scherer -# -# 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 . - -from subprocess import Popen, PIPE -import sys -import json - -result = {} -result['all'] = {} - -pipe = Popen(['virsh', '-q', '-c', 'lxc:///', 'list', '--name', '--all'], stdout=PIPE, universal_newlines=True) -result['all']['hosts'] = [x[:-1] for x in pipe.stdout.readlines()] -result['all']['vars'] = {} -result['all']['vars']['ansible_connection'] = 'libvirt_lxc' - -if len(sys.argv) == 2 and sys.argv[1] == '--list': - print(json.dumps(result)) -elif len(sys.argv) == 3 and sys.argv[1] == '--host': - print(json.dumps({'ansible_connection': 'libvirt_lxc'})) -else: - sys.stderr.write("Need an argument, either --list or --host \n") diff --git a/tests/integration/targets/virt_net/aliases b/tests/integration/targets/virt_net/aliases deleted file mode 100644 index e2a1952cbc..0000000000 --- a/tests/integration/targets/virt_net/aliases +++ /dev/null @@ -1,6 +0,0 @@ -shippable/posix/group1 -skip/aix -skip/freebsd -skip/osx -needs/privileged -destructive diff --git a/tests/integration/targets/virt_net/files/foobar.xml b/tests/integration/targets/virt_net/files/foobar.xml deleted file mode 100644 index 768e10eaa8..0000000000 --- a/tests/integration/targets/virt_net/files/foobar.xml +++ /dev/null @@ -1,9 +0,0 @@ - - foobar - - - - - - - diff --git a/tests/integration/targets/virt_net/tasks/main.yml b/tests/integration/targets/virt_net/tasks/main.yml deleted file mode 100644 index b6bd241906..0000000000 --- a/tests/integration/targets/virt_net/tasks/main.yml +++ /dev/null @@ -1,85 +0,0 @@ ---- -- include_vars: '{{ item }}' - with_first_found: - - "{{ ansible_distribution }}-{{ ansible_distribution_version}}.yml" - - "{{ ansible_distribution }}-{{ ansible_distribution_major_version}}.yml" - - "{{ ansible_distribution }}.yml" - - "default.yml" - -- block: - - name: Install libvirt packages - package: - name: "{{ virt_net_packages }}" - - - name: Start libvirt - service: - name: libvirtd - state: started - - - name: Define the foobar network - virt_net: - command: define - name: foobar - xml: '{{ lookup("file", "foobar.xml") }}' - - - name: Define the foobar network (again) - virt_net: - command: define - name: foobar - xml: '{{ lookup("file", "foobar.xml") }}' - register: second_virt_net_define - - - name: Start the default network - virt_net: - uri: qemu:///system - command: start - name: foobar - - - name: Start the default network (again) - virt_net: - uri: qemu:///system - command: start - name: foobar - register: second_virt_net_start - - - name: Get facts for default network - virt_net: - uri: qemu:///system - command: facts - name: foobar - register: virt_net_facts - - - name: Destroy the foobar network - virt_net: - command: destroy - name: foobar - - - name: Undefine the foobar network - virt_net: - command: undefine - name: foobar - register: second_virt_net_define - - - name: Undefine the foobar network (again) - virt_net: - command: undefine - name: foobar - register: second_virt_net_undefine - - - name: Ensure the second calls return "unchanged" - assert: - that: - - "second_virt_net_start is not changed" - - "second_virt_net_define is not changed" - - "second_virt_net_undefine is not changed" - - always: - - name: Stop libvirt - service: - name: libvirtd - state: stopped - - - name: Remove only the libvirt packages - package: - name: "{{ virt_net_packages|select('match', '.*libvirt.*')|list }}" - state: absent diff --git a/tests/integration/targets/virt_net/vars/Debian.yml b/tests/integration/targets/virt_net/vars/Debian.yml deleted file mode 100644 index 7d06d77cd6..0000000000 --- a/tests/integration/targets/virt_net/vars/Debian.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -virt_net_packages: - - libvirt-daemon - - libvirt-daemon-system - - python-libvirt - - python-lxml diff --git a/tests/integration/targets/virt_net/vars/Fedora-29.yml b/tests/integration/targets/virt_net/vars/Fedora-29.yml deleted file mode 100644 index dcf754dc9b..0000000000 --- a/tests/integration/targets/virt_net/vars/Fedora-29.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -virt_net_packages: - - libvirt - - libvirt-daemon - - python3-libvirt - - python3-lxml diff --git a/tests/integration/targets/virt_net/vars/RedHat-7.yml b/tests/integration/targets/virt_net/vars/RedHat-7.yml deleted file mode 100644 index 263281fec8..0000000000 --- a/tests/integration/targets/virt_net/vars/RedHat-7.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -virt_net_packages: - - libvirt - - libvirt-daemon - - libvirt-python - - python-lxml diff --git a/tests/integration/targets/virt_net/vars/RedHat-8.yml b/tests/integration/targets/virt_net/vars/RedHat-8.yml deleted file mode 100644 index dcf754dc9b..0000000000 --- a/tests/integration/targets/virt_net/vars/RedHat-8.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -virt_net_packages: - - libvirt - - libvirt-daemon - - python3-libvirt - - python3-lxml diff --git a/tests/integration/targets/virt_net/vars/Ubuntu-16.04.yml b/tests/integration/targets/virt_net/vars/Ubuntu-16.04.yml deleted file mode 100644 index b9ad4aa66e..0000000000 --- a/tests/integration/targets/virt_net/vars/Ubuntu-16.04.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -virt_net_packages: - - libvirt-daemon - - python-libvirt - - python-lxml diff --git a/tests/integration/targets/virt_net/vars/Ubuntu-18.04.yml b/tests/integration/targets/virt_net/vars/Ubuntu-18.04.yml deleted file mode 100644 index b9ad4aa66e..0000000000 --- a/tests/integration/targets/virt_net/vars/Ubuntu-18.04.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -virt_net_packages: - - libvirt-daemon - - python-libvirt - - python-lxml diff --git a/tests/integration/targets/virt_net/vars/Ubuntu-18.10.yml b/tests/integration/targets/virt_net/vars/Ubuntu-18.10.yml deleted file mode 100644 index 7d06d77cd6..0000000000 --- a/tests/integration/targets/virt_net/vars/Ubuntu-18.10.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -virt_net_packages: - - libvirt-daemon - - libvirt-daemon-system - - python-libvirt - - python-lxml diff --git a/tests/integration/targets/virt_net/vars/default.yml b/tests/integration/targets/virt_net/vars/default.yml deleted file mode 100644 index b9ad4aa66e..0000000000 --- a/tests/integration/targets/virt_net/vars/default.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -virt_net_packages: - - libvirt-daemon - - python-libvirt - - python-lxml diff --git a/tests/unit/modules/cloud/misc/virt_net/__init__.py b/tests/unit/modules/cloud/misc/virt_net/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/unit/modules/cloud/misc/virt_net/conftest.py b/tests/unit/modules/cloud/misc/virt_net/conftest.py deleted file mode 100644 index 1b86bf339e..0000000000 --- a/tests/unit/modules/cloud/misc/virt_net/conftest.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- 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) -import pytest - -from ansible_collections.community.general.plugins.modules.cloud.misc import virt_net - -from ansible_collections.community.general.tests.unit.compat import mock - - -virt_net.libvirt = None -virt_net.HAS_VIRT = True - - -class DummyNetwork(): - def __init__(self, name, isActive=True): - self._name = name - self._isActive = isActive - - def name(self): - return self._name - - def isActive(self): - return self._isActive - - -class DummyLibvirtConn(): - def __init__(self): - self._network = [ - DummyNetwork("inactive_net", isActive=False), - DummyNetwork("active_net", isActive=True)] - - def listNetworks(self): - return [i.name() for i in self._network] - - def networkLookupByName(self, name): - for i in self._network: - if i.name() == name: - return i - - def listDefinedNetworks(self): - return [] - - -class DummyLibvirt(): - VIR_ERR_NETWORK_EXIST = 'VIR_ERR_NETWORK_EXIST' - - @classmethod - def open(cls, uri): - return DummyLibvirtConn() - - class libvirtError(Exception): - def __init__(self): - self.error_code - - def get_error_code(self): - return self.error_code - - -@pytest.fixture -def dummy_libvirt(monkeypatch): - monkeypatch.setattr(virt_net, 'libvirt', DummyLibvirt) - return DummyLibvirt - - -@pytest.fixture -def virt_net_obj(dummy_libvirt): - return virt_net.VirtNetwork('qemu:///nowhere', mock.MagicMock()) diff --git a/tests/unit/modules/cloud/misc/virt_net/test_virt_net.py b/tests/unit/modules/cloud/misc/virt_net/test_virt_net.py deleted file mode 100644 index f073362fef..0000000000 --- a/tests/unit/modules/cloud/misc/virt_net/test_virt_net.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- 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 ansible_collections.community.general.tests.unit.compat import mock - - -def test_virt_net_create_already_active(virt_net_obj, dummy_libvirt): - virt_net_obj.conn.create = mock.Mock() - assert virt_net_obj.create("active_net") is None - virt_net_obj.conn.create.assert_not_called() - - -def test_virt_net_recreate(virt_net_obj, dummy_libvirt): - virt_net_obj.conn.create = mock.Mock() - dummy_libvirt.libvirtError.error_code = 'VIR_ERR_NETWORK_EXIST' - virt_net_obj.conn.create.side_effect = dummy_libvirt.libvirtError - assert virt_net_obj.create("active_net") is None - - -def test_virt_stop_ignore_inactive(virt_net_obj): - virt_net_obj.conn.destroy = mock.Mock() - virt_net_obj.stop('inactive_net') - virt_net_obj.conn.destroy.assert_not_called() - - -def test_virt_stop_active(virt_net_obj, monkeypatch): - virt_net_obj.conn.destroy = mock.Mock() - virt_net_obj.stop('active_net') - virt_net_obj.conn.destroy.assert_called_with('active_net')