virt 11.4 KB
Newer Older
1
#!/usr/bin/python
2 3
# -*- coding: utf-8 -*-

4 5 6 7
"""
Virt management features

Copyright 2007, 2012 Red Hat, Inc
8
Michael DeHaan <michael.dehaan@gmail.com>
9 10 11 12 13 14
Seth Vidal <skvidal@fedoraproject.org>

This software may be freely redistributed under the terms of the GNU
general public license.

You should have received a copy of the GNU General Public License
15
along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17
"""

Jan-Piet Mens committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
DOCUMENTATION = '''
---
module: virt
short_description: Manages virtual machines supported by libvirt
description:
     - Manages virtual machines supported by I(libvirt).
version_added: "0.2"
options:
  name:
    description:
      - name of the guest VM being managed
    required: true
    default: null
    aliases: []
  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.
    required: false
    choices: [ "running", "shutdown", "destroyed", "undefined" ]
    default: "no"
  command:
    description:
      - in addition to state management, various non-idempotent commands are available. See examples
    required: false
examples:
45
   - code: "virt: guest=alpha state=running"
Jan-Piet Mens committed
46 47 48 49 50 51 52 53
     description: "Example from Ansible Playbooks"
   - code: ansible host -m virt -a "guest=alpha command=status"
     description: "Example guest management with C(/usr/bin/ansible)"
notes:
   - "Other non-idempotent commands are: C(status), C(pause), C(unpause),
     C(get_xml), C(autostart), C(freemem), C(list_vms), C(info), C(nodeinfo),
     C(virttype)"
requirements: [ "libvirt" ]
54
author: Michael DeHaan, Seth Vidal
Jan-Piet Mens committed
55 56
'''

57 58 59 60
VIRT_FAILED = 1
VIRT_SUCCESS = 0
VIRT_UNAVAILABLE=2

61 62
import sys

63 64 65
try:
    import libvirt
except ImportError:
66
    print "failed=True msg='libvirt python module unavailable'"
67
    sys.exit(1)
68

69
ALL_COMMANDS = []
70
VM_COMMANDS = ['create','status', 'start', 'stop', 'pause', 'unpause',
71 72 73 74
                'shutdown', 'undefine', 'destroy', 'get_xml', 'autostart']
HOST_COMMANDS = ['freemem', 'list_vms', 'info', 'nodeinfo', 'virttype']
ALL_COMMANDS.extend(VM_COMMANDS)
ALL_COMMANDS.extend(HOST_COMMANDS)
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

VIRT_STATE_NAME_MAP = {
   0 : "running",
   1 : "running",
   2 : "running",
   3 : "paused",
   4 : "shutdown",
   5 : "shutdown",
   6 : "crashed"
}

class LibvirtConnection(object):

    def __init__(self):

        cmd = subprocess.Popen("uname -r", shell=True, stdout=subprocess.PIPE,
                                close_fds=True)
        output = cmd.communicate()[0]

        if output.find("xen") != -1:
            conn = libvirt.open(None)
        else:
            conn = libvirt.open("qemu:///system")

        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 Exception("virtual machine %s not found" % vmid)

    def shutdown(self, vmid):
        return self.find_vm(vmid).shutdown()

    def pause(self, vmid):
        return self.suspend(self.conn,vmid)

    def unpause(self, vmid):
        return self.resume(self.conn,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_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()
185

186 187 188
    def set_autostart(self, vmid, val):
        vm = self.conn.lookupByName(vmid)
        return vm.setAutostart(val)
189

190 191 192 193 194 195 196 197 198 199 200


class Virt(object):

    def __get_conn(self):
        self.conn = LibvirtConnection()
        return self.conn

    def get_vm(self, vmid):
        self.__get_conn()
        return self.conn.find_vm(vmid)
201

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    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] = {
                "state"     : VIRT_STATE_NAME_MAP.get(data[0],"unknown"),
                "maxMem"    : str(data[1]),
                "memory"    : str(data[2]),
                "nrVirtCpu" : data[3],
                "cpuTime"   : str(data[4]),
            }
227
            info[vm]["autostart"] = self.conn.get_autostart(vm)
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259

        return info

    def nodeinfo(self):
        self.__get_conn()
        info = dict()
        data = self.conn.nodeinfo()
        info = {
            "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):
        self.conn = self.__get_conn()
        vms = self.conn.find_vm(-1)
        results = []
        for x in vms:
            try:
                results.append(x.name())
            except:
                pass
        return results

    def virttype(self):
        return self.__get_conn().get_type()
260

261 262 263 264 265 266 267 268 269
    def autostart(self, vmid):
        self.conn = self.__get_conn()
        return self.conn.set_autostart(vmid, True)

    def freemem(self):
        self.conn = self.__get_conn()
        return self.conn.getFreeMemory()

    def shutdown(self, vmid):
270
        """ Make the machine with the given vmid stop running.  Whatever that takes.  """
271 272 273 274 275 276
        self.__get_conn()
        self.conn.shutdown(vmid)
        return 0


    def pause(self, vmid):
277
        """ Pause the machine with the given vmid.  """
278 279 280 281 282

        self.__get_conn()
        return self.conn.suspend(vmid)

    def unpause(self, vmid):
283
        """ Unpause the machine with the given vmid.  """
284 285 286 287 288

        self.__get_conn()
        return self.conn.resume(vmid)

    def create(self, vmid):
289
        """ Start the machine via the given vmid """
290 291 292 293 294

        self.__get_conn()
        return self.conn.create(vmid)

    def start(self, vmid):
295
        """ Start the machine via the given id/name """
296 297 298 299 300

        self.__get_conn()
        return self.conn.create(vmid)

    def destroy(self, vmid):
301
        """ Pull the virtual power from the virtual domain, giving it virtually no time to virtually shut down.  """
302 303 304 305
        self.__get_conn()
        return self.conn.destroy(vmid)

    def undefine(self, vmid):
306
        """ Stop a domain, and then wipe it from the face of the earth.  (delete disk/config file) """
307 308 309 310 311 312 313 314 315 316 317 318 319

        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):
        """
Martijn Koster committed
320
        Receive a Vm id as input
321 322
        Return an xml describing vm config returned by a libvirt call
        """
323

324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
        conn = libvirt.openReadOnly(None)
        if conn == None:
            return (-1,'Failed to open connection to the hypervisor')
        try:
            domV = conn.lookupByName(vmid)
        except:
            return (-1,'Failed to find the main domain')
        return domV.XMLDesc(0)

    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)

349
def core(module):
350

351 352 353
    state      = module.params.get('state', None)
    guest      = module.params.get('name', None)
    command    = module.params.get('command', None)
354 355 356

    v = Virt()
    res = {}
357 358


359 360
    if state:
        if not guest:
361
            module.fail_json(msg = "state change requires a guest specified")
362

363 364 365
        res['changed'] = False
        if state == 'running':
            if v.status(guest) is not 'running':
366
                res['changed'] = True
367 368 369 370 371
                res['msg'] = v.start(guest)
        elif state == 'shutdown':
            if v.status(guest) is not 'shutdown':
                res['changed'] = True
                res['msg'] = v.shutdown(guest)
372
        else:
373 374
            module.fail_json(msg="unexpected state")

375
        return VIRT_SUCCESS, res
376

377
    if command:
378
        if command in VM_COMMANDS:
379
            if not guest:
380
                module.fail_json(msg = "%s requires 1 argument: guest" % command)
381 382 383
            res = getattr(v, command)(guest)
            if type(res) != dict:
                res = { command: res }
384
            return VIRT_SUCCESS, res
385

386 387 388 389
        elif hasattr(v, command):
            res = getattr(v, command)()
            if type(res) != dict:
                res = { command: res }
390
            return VIRT_SUCCESS, res
391

392
        else:
393
            module.fail_json(msg="Command %s not recognized" % basecmd)
394 395 396

    module.fail_json(msg="expected state or command parameter to be specified")

397
def main():
398

399 400 401 402 403 404 405
    module = AnsibleModule(argument_spec=dict(
        name = dict(aliases=['guest']),
        state = dict(choices=['running', 'shutdown']),
        command = dict(choices=ALL_COMMANDS),
    ))

    rc = VIRT_SUCCESS
406
    try:
407
        rc, result = core(module)
408
    except Exception, e:
409
        module.fail_json(msg=str(e))
410 411

    if rc != 0: # something went wrong emit the msg
412
        module.fail_json(rc=rc, msg=result)
413
    else:
414 415 416 417 418
        module.exit_json(**result)


# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
419
main()