mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 09:23:56 +00:00
Deprecate kvm_hyper in favor of virt
This commit is contained in:
parent
e80d2e2c4e
commit
94b82106ac
@ -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)
|
Loading…
Reference in New Issue
Block a user