Server IP : 85.214.239.14 / Your IP : 18.226.187.224 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/3/cwd/lib/python3/dist-packages/ansible_collections/community/libvirt/plugins/modules/ |
Upload File : |
#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright: (c) 2007, 2012 Red Hat, Inc # Michael DeHaan <michael.dehaan@gmail.com> # Seth Vidal <skvidal@fedoraproject.org> # 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 = ''' --- module: virt short_description: Manages virtual machines supported by libvirt description: - Manages virtual machines supported by I(libvirt). extends_documentation_fragment: - community.libvirt.virt.options_uri - community.libvirt.virt.options_xml - community.libvirt.virt.options_guest - community.libvirt.virt.options_autostart - community.libvirt.virt.options_state - community.libvirt.virt.options_command - community.libvirt.requirements author: - Ansible Core Team - Michael DeHaan - Seth Vidal (@skvidal) ''' EXAMPLES = ''' # a playbook task line: - name: Start a VM community.libvirt.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 a VM community.libvirt.virt: command: define xml: "{{ lookup('template', 'container-template.xml.j2') }}" uri: 'lxc:///' - name: start vm community.libvirt.virt: name: foo state: running uri: 'lxc:///' # setting autostart on a qemu VM (default uri) - name: Set autostart for a VM community.libvirt.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 community.libvirt.virt: command: define xml: "{{ lookup('template', 'vm_template.xml.j2') }}" autostart: yes # Listing VMs - name: List all VMs community.libvirt.virt: command: list_vms register: all_vms - name: List only running VMs community.libvirt.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 """ vms = self.conn.listAllDomains() 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'") try: domain_name = re.search('<name>(.*)</name>', xml).groups()[0] except AttributeError: 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 '<name>' already exists with <uuid> # # In case a domain would be indeed overwritten, we should protect idempotency: try: existing_domain_xml = v.get_vm(domain_name).XMLDesc( libvirt.VIR_DOMAIN_XML_INACTIVE ) except VMNotFound: existing_domain_xml = None try: domain = v.define(xml) if existing_domain_xml: # if we are here, then libvirt redefined existing domain as the doc promised if existing_domain_xml != domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE): 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.get_error_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', 'paused', '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()