Deprecate kvm_hyper in favor of virt

This commit is contained in:
Thomas S Hatch 2013-05-28 16:37:40 -06:00
parent e80d2e2c4e
commit 94b82106ac

View File

@ -1,504 +0,0 @@
'''
Provide the hyper module for kvm hypervisors. This is the interface used to
interact with kvm on behalf of the salt-virt interface
:depends: - libvirt Python module
'''
# This is a test interface for the salt-virt system. The API in this file is
# VERY likely to change.
# Import python libs
import logging
import os
import shutil
import string
import subprocess
from xml.dom import minidom
# Import third party libs
import yaml
try:
import libvirt
HAS_LIBVIRT = True
except ImportError:
HAS_LIBVIRT = False
# Import salt libs
import salt.utils
from salt._compat import StringIO
log = logging.getLogger(__name__)
VIRT_STATE_NAME_MAP = {0: "running",
1: "running",
2: "running",
3: "paused",
4: "shutdown",
5: "shutdown",
6: "crashed"}
def __virtual__():
'''
Apply this module as the hyper module if the minion is a kvm hypervisor
'''
if 'virtual' not in __grains__:
return False
if __grains__['virtual'] != 'physical':
return False
if __grains__['kernel'] != 'Linux':
return False
if not os.path.exists('/proc/modules'):
return False
try:
if 'kvm_' not in salt.utils.fopen('/proc/modules').read():
return False
except IOError:
return False
if not HAS_LIBVIRT:
return False
#Libvirt is very noisy. This will quiet it down
def quiet_errors(ignored, err):
log.debug(err[2])
libvirt.registerErrorHandler(quiet_errors, None)
try:
libvirt_conn = libvirt.open('qemu:///system')
libvirt_conn.close()
return 'hyper'
except libvirt.libvirtError:
return False
def __get_conn():
'''
Connects to the libvirt daemon for qemu/kvm
'''
return libvirt.open("qemu:///system")
def _get_dom(vm_):
'''
Return a domain object for the named vm
'''
conn = __get_conn()
if vm_ not in list_virts():
raise Exception('The specified vm is not present')
return conn.lookupByName(vm_)
# Define tier 1 Virt functions, all hyper interfaces have these:
# hyper_type
# list_virts
# hyper_info
# get_conf
def hyper_type():
'''
Return that type of hypervisor this is
CLI Example::
salt '*' hyper.hyper_type
'''
return 'kvm'
def freemem():
'''
Return an int representing the amount of memory that has not been given
to virtual machines on this node
CLI Example::
salt '*' hyper.freemem
'''
conn = __get_conn()
mem = conn.getInfo()[1]
# Take off just enough to sustain the hypervisor
mem -= 256
for vm_ in list_virts():
dom = _get_dom(vm_)
if dom.ID() > 0:
mem -= dom.info()[2] / 1024
return mem
def freecpu():
'''
Return an int representing the number of unallocated cpus on this
hypervisor
CLI Example::
salt '*' hyper.freecpu
'''
conn = __get_conn()
cpus = conn.getInfo()[2]
for vm_ in list_virts():
dom = _get_dom(vm_)
if dom.ID() > 0:
cpus -= dom.info()[3]
return cpus
def list_virts():
'''
Return a list of virtual machine names on the minion
CLI Example::
salt '*' hyper.list_virts
'''
# Expand to include down vms
conn = __get_conn()
vms = []
for id_ in conn.listDomainsID():
vms.append(conn.lookupByID(id_).name())
return vms
def virt_info():
'''
Return detailed information about the vms on this hyper in a dict::
{'cpu': <int>,
'maxMem': <int>,
'mem': <int>,
'state': '<state>',
'cputime' <int>}
CLI Example::
salt '*' hyper.virt_info
'''
info = {}
for vm_ in list_virts():
dom = _get_dom(vm_)
raw = dom.info()
info[vm_] = {
'state': VIRT_STATE_NAME_MAP.get(raw[0], 'unknown'),
'maxMem': int(raw[1]),
'mem': int(raw[2]),
'cpu': raw[3],
'cputime': int(raw[4]),
'disks': get_disks(vm_),
}
return info
def hyper_info():
'''
Return a dict with information about this hypervisor
CLI Example::
salt '*' hyper.hyper_info
'''
conn = __get_conn()
raw = conn.getInfo()
info = {
'phymemory': raw[1],
'cpus': raw[2],
'cpumhz': raw[3],
'cpucores': raw[6],
'cputhreads': raw[7],
'type': hyper_type(),
'freecpu': freecpu(),
'freemem': freemem(),
'virt_info': virt_info(),
}
return info
# Level 2 - vm class specific
# init - Create a vm from options
# start - Start a down vm
# halt
# purge
# pause
# resume
# set_autostart
# get_disks
# get_conf
def _get_image(image, vda):
'''
Copy the image into place
'''
if ':' in image:
if not os.path.isabs(image) or not image.startswith('file://'):
# The image is on a network resource
env = 'base'
if image.rindex(':') != 4:
env = image.split(':')[-1]
image = image[:image.rindex(':')]
__salt__['cp.get_url'](image, vda, env)
if os.path.isabs(image) or image.startswith('file://'):
# This is a local file, copy it into place
if image.startswith('file://'):
# Condition this into a standard path
for ind in range(6, len(image)):
if image[ind].isalpha():
image = os.path.join('/', image[ind:])
break
shutil.copy2(image, vda)
def _gen_xml(name,
cpus,
mem,
vmdir,
disks,
network,
desc,
opts):
'''
Generate the XML used for the libvirt configuration
'''
# Don't generate the libvirt config if it already exists
vda = os.path.join(vmdir, 'vda')
data = '''
<domain type='kvm'>
<name>%%NAME%%</name>
<vcpu>%%CPU%%</vcpu>
<memory>%%MEM%%</memory>
<os>
<type>hvm</type>
<boot dev='hd'/>
</os>
<devices>
<emulator>/usr/bin/kvm</emulator>
<disk type='file' device='disk'>
<source file='%%VDA%%'/>
<target dev='vda' bus='virtio'/>
<driver name='qemu' cache='writeback' io='native'/>
</disk>
%%DISK%%
%%NICS%%
<graphics type='vnc' listen='0.0.0.0' autoport='yes'/>
</devices>
<features>
<acpi/>
</features>
</domain>
'''
data = data.replace('%%NAME%%', name)
data = data.replace('%%CPU%%', str(cpus))
data = data.replace('%%MEM%%', str(int(mem) * 1024))
data = data.replace('%%VDA%%', vda)
nics = ''
for interface, data in network.items():
for bridge, mac in data.items():
if not mac:
# Generate this interface's mac addr, use the qemu default
# prefix, 52:54
mac = salt.utils.gen_mac('52:54:')
nic = '''
<interface type='bridge'>
<source bridge='%%BRIDGE%%'/>
<mac address='%%MAC%%'/>
<model type='virtio'/>
</interface>\n'''
nic = nic.replace('%%BRIDGE%%', bridge)
nic = nic.replace('%%MAC%%', mac)
nics += nic
data = data.replace('%%NICS%%', nics)
if disks:
letters = string.ascii_lowercase
disk_str = ''
for ind in range(0, len(disks)):
disk = disks[ind]
disk_d = '''
<disk type='file' device='disk'>
<source file='%%DISK_PATH%%'/>
<target dev='%%VD%%' bus='virtio'/>
<driver name='qemu' type='%%TYPE%%' cache='writeback' io='native'/>
</disk>
'''
disk_d = disk_d.replace('%%DISK_PATH%%', disk['path'])
disk_d = disk_d.replace('%%TYPE%%', disk['format'])
disk_d = disk_d.replace('%%VD%%', 'vd' + letters[ind + 1])
disk_str += disk_d
data = data.replace('%%DISK%%', disk_str)
else:
data = data.replace('%%DISK%%', '')
return data
def init(
name,
cpus,
mem,
image,
storage_dir,
network=None,
desc='',
opts=None):
'''
Create a KVM virtual machine based on these passed options, the virtual
machine will be started upon creation
CLI Example:
salt '*' hyper.init webserver 2 2048 salt://fedora/f16.img:virt /srv/vm/images
'''
if network is None:
network = {'eth0': {'bridge': 'br0', 'mac': ''}}
vmdir = os.path.join(storage_dir, name)
if not os.path.exists(vmdir):
os.makedirs(vmdir)
vda = os.path.join(vmdir, 'vda')
_get_image(image, vda)
# The image is in place
xml = _gen_xml(name, cpus, mem, vmdir, network, desc, opts or {})
config = os.path.join(vmdir, 'config.xml')
salt.utils.fopen(config, 'w+').write(xml)
return start(config)
def start(config):
'''
Start an already defined virtual machine that has been shut down
CLI Example::
salt '*' hyper.start webserver
'''
# change this to use the libvirt API and add more logging and a verbose
# return
cmd = 'virsh create {0}'.format(config)
return not __salt__['cmd.retcode'](cmd)
def halt(name):
'''
Hard power down a virtual machine
CLI Example::
salt '*' hyper.halt webserver
'''
try:
dom = _get_dom(name)
dom.destroy()
except Exception:
return False
return True
def purge(name):
'''
Hard power down and purge a virtual machine, this will destroy a vm and
all associated vm data
CLI Example::
salt '*' hyper.purge webserver
'''
disks = get_disks(name)
halt(name)
directories = set()
for disk in disks:
os.remove(disks[disk]['file'])
directories.add(os.path.dirname(disks[disk]['file']))
if directories:
for dir_ in directories:
shutil.rmtree(dir_)
return True
def pause(name):
'''
Pause the named virtual machine
CLI Example::
salt '*' hyper.pause webserver
'''
dom = _get_dom(name)
dom.suspend()
return True
def resume(name):
'''
Resume the named virtual machine
CLI Example::
salt '*' hyper.resume webserver
'''
dom = _get_dom(name)
dom.resume()
return True
def set_autostart(name, state='on'):
'''
Set the named virtual machine to autostart when the hypervisor boots
CLI Example::
salt '*' hyper.set_autostart webserver
'''
dom = _get_dom(name)
if state == 'on':
return dom.setAutostart(1) == 0
elif state == 'off':
return dom.setAutostart(0) == 0
else:
# return False if state is set to something other then on or off
return False
def get_disks(name):
'''
Return the disks of a named virt
CLI Example::
salt '*' hyper.get_disks <vm name>
'''
disks = {}
doc = minidom.parse(StringIO(get_conf(name)))
for elem in doc.getElementsByTagName('disk'):
sources = elem.getElementsByTagName('source')
targets = elem.getElementsByTagName('target')
if len(sources) > 0:
source = sources[0]
else:
continue
if len(targets) > 0:
target = targets[0]
else:
continue
if 'dev' in list(target.attributes) and 'file' in list(source.attributes):
disks[target.getAttribute('dev')] = {'file': source.getAttribute('file')}
for dev in disks:
disks[dev].update(yaml.safe_load(subprocess.Popen(
'qemu-img info ' + disks[dev]['file'],
shell=True,
stdout=subprocess.PIPE).communicate()[0]))
return disks
def get_conf(name):
'''
Returns the XML for a given vm
CLI Example::
salt '*' hyper.get_conf <vm name>
'''
dom = _get_dom(name)
return dom.XMLDesc(0)