Merge pull request #40746 from dis-xcom/develop

Add possibility to specify multiple disks in 'virt' module
This commit is contained in:
Mike Place 2017-04-28 06:57:00 -06:00 committed by GitHub
commit 637be4e287

View File

@ -353,6 +353,115 @@ def _qemu_image_info(path):
return ret
def _qemu_image_create(vm_name,
disk_file_name,
disk_image=None,
disk_size=None,
disk_type='qcow2',
enable_qcow=False,
saltenv='base'):
'''
Create the image file using specified disk_size or/and disk_image
Return path to the created image file
'''
if not disk_size and not disk_image:
raise CommandExecutionError(
'Unable to create new disk {0}, please specify'
' disk size and/or disk image argument'
.format(disk_file_name)
)
img_dir = __salt__['config.option']('virt.images')
log.debug('Image directory from config option `virt.images`'
' is {0}'.format(img_dir))
img_dest = os.path.join(
img_dir,
vm_name,
disk_file_name
)
log.debug('Image destination will be {0}'.format(img_dest))
img_dir = os.path.dirname(img_dest)
log.debug('Image destination directory is {0}'
.format(img_dir))
try:
os.makedirs(img_dir)
except OSError:
pass
if disk_image:
log.debug('Create disk from specified image {0}'
.format(disk_image))
sfn = __salt__['cp.cache_file'](disk_image, saltenv)
qcow2 = False
if salt.utils.which('qemu-img'):
res = __salt__['cmd.run']('qemu-img info {}'.format(sfn))
imageinfo = yaml.load(res)
qcow2 = imageinfo['file format'] == 'qcow2'
try:
if enable_qcow and qcow2:
log.info('Cloning qcow2 image {0} using copy on write'
.format(sfn))
__salt__['cmd.run'](
'qemu-img create -f qcow2 -o backing_file={0} {1}'
.format(sfn, img_dest).split())
else:
log.debug('Copying {0} to {1}'.format(sfn, img_dest))
salt.utils.files.copyfile(sfn, img_dest)
mask = os.umask(0)
os.umask(mask)
if disk_size and qcow2:
log.debug('Resize qcow2 image to {0}M'.format(disk_size))
__salt__['cmd.run'](
'qemu-img resize {0} {1}M'
.format(img_dest, str(disk_size))
)
log.debug('Apply umask and remove exec bit')
mode = (0o0777 ^ mask) & 0o0666
os.chmod(img_dest, mode)
except (IOError, OSError) as e:
raise CommandExecutionError(
'Problem while copying image. {0} - {1}'
.format(disk_image, e)
)
else:
# Create empty disk
try:
mask = os.umask(0)
os.umask(mask)
if disk_size:
log.debug('Create empty image with size {0}M'.format(disk_size))
__salt__['cmd.run'](
'qemu-img create -f {0} {1} {2}M'
.format(disk_type, img_dest, str(disk_size))
)
else:
raise CommandExecutionError(
'Unable to create new disk {0},'
' please specify <size> argument'
.format(img_dest)
)
log.debug('Apply umask and remove exec bit')
mode = (0o0777 ^ mask) & 0o0666
os.chmod(img_dest, mode)
except (IOError, OSError) as e:
raise CommandExecutionError(
'Problem while creating volume {0} - {1}'
.format(img_dest, e)
)
return img_dest
# TODO: this function is deprecated, should be replaced with
# _qemu_image_info()
def _image_type(vda):
@ -403,6 +512,24 @@ def _disk_profile(profile, hypervisor, **kwargs):
format: qcow2
model: virtio
Example profile for KVM/QEMU with two disks, first is created
from specified image, the second is empty:
.. code-block:: yaml
virt:
disk:
two_disks:
- system:
size: 8192
format: qcow2
model: virtio
image: http://path/to/image.qcow2
- lvm:
size: 32768
format: qcow2
model: virtio
The ``format`` and ``model`` parameters are optional, and will
default to whatever is best suitable for the active hypervisor.
'''
@ -580,90 +707,34 @@ def init(name,
nicp = _nic_profile(nic, hypervisor, **kwargs)
log.debug('NIC profile is {0}'.format(nicp))
diskp = None
seedable = False
if image: # with disk template image
log.debug('Image {0} will be used'.format(image))
# if image was used, assume only one disk, i.e. the
# 'default' disk profile
# TODO: make it possible to use disk profiles and use the
# template image as the system disk
diskp = _disk_profile('default', hypervisor, **kwargs)
log.debug('Disk profile is {0}'.format(diskp))
diskp = _disk_profile(disk, hypervisor, **kwargs)
# When using a disk profile extract the sole dict key of the first
# array element as the filename for disk
if image:
# If image is specified in module arguments, then it will be used
# for the first disk instead of the image from the disk profile
disk_name = next(six.iterkeys(diskp[0]))
disk_type = diskp[0][disk_name]['format']
disk_file_name = '{0}.{1}'.format(disk_name, disk_type)
log.debug('{0} image from module arguments will be used for disk "{1}"'
' instead of {2}'
.format(image,
disk_name,
diskp[0][disk_name].get('image', None)))
diskp[0][disk_name]['image'] = image
if hypervisor in ['esxi', 'vmware']:
# TODO: we should be copying the image file onto the ESX host
raise SaltInvocationError(
'virt.init does not support image template template in '
'conjunction with esxi hypervisor'
)
elif hypervisor in ['qemu', 'kvm']:
img_dir = __salt__['config.option']('virt.images')
log.debug('Image directory from config option `virt.images` is {0}'
.format(img_dir))
img_dest = os.path.join(
img_dir,
name,
disk_file_name
)
log.debug('Image destination will be {0}'.format(img_dest))
img_dir = os.path.dirname(img_dest)
log.debug('Image destination directory is {0}'.format(img_dir))
sfn = __salt__['cp.cache_file'](image, saltenv)
# Create multiple disks, empty or from specified images.
for disk in diskp:
log.debug("Creating disk for VM [ {0} ]: {1}".format(name, disk))
try:
os.makedirs(img_dir)
except OSError:
pass
for disk_name, args in six.iteritems(disk):
qcow2 = False
if salt.utils.which('qemu-img'):
res = __salt__['cmd.run']('qemu-img info {}'.format(sfn))
imageinfo = yaml.load(res)
qcow2 = imageinfo['file format'] == 'qcow2'
try:
if enable_qcow and qcow2:
log.info('Cloning qcow2 image {} using copy on write'
.format(sfn))
__salt__['cmd.run'](
'qemu-img create -f qcow2 -o backing_file={} {}'
.format(sfn, img_dest).split())
if hypervisor in ['esxi', 'vmware']:
if 'image' in args:
# TODO: we should be copying the image file onto the ESX host
raise SaltInvocationError(
'virt.init does not support image '
'template in conjunction with esxi hypervisor'
)
else:
log.debug('Copying {0} to {1}'.format(sfn, img_dest))
salt.utils.files.copyfile(sfn, img_dest)
mask = os.umask(0)
os.umask(mask)
# Apply umask and remove exec bit
mode = (0o0777 ^ mask) & 0o0666
os.chmod(img_dest, mode)
except (IOError, OSError) as e:
raise CommandExecutionError('problem copying image. {0} - {1}'.format(image, e))
seedable = True
else:
log.error('Unsupported hypervisor when handling disk image')
else:
# no disk template image specified, create disks based on disk profile
diskp = _disk_profile(disk, hypervisor, **kwargs)
log.debug('No image specified, disk profile will be used: {0}'.format(diskp))
if hypervisor in ['qemu', 'kvm']:
# TODO: we should be creating disks in the local filesystem with
# qemu-img
raise SaltInvocationError(
'virt.init does not support disk profiles in conjunction with '
'qemu/kvm at this time, use image template instead'
)
else:
# assume libvirt manages disks for us
for disk in diskp:
for disk_name, args in six.iteritems(disk):
# assume libvirt manages disks for us
log.debug('Generating libvirt XML for {0}'.format(disk))
xml = _gen_vol_xml(
name,
@ -673,6 +744,42 @@ def init(name,
)
define_vol_xml_str(xml)
elif hypervisor in ['qemu', 'kvm']:
disk_type = args.get('format', 'qcow2')
disk_image = args.get('image', None)
disk_size = args.get('size', None)
disk_file_name = '{0}.{1}'.format(disk_name, disk_type)
img_dest = _qemu_image_create(
vm_name=name,
disk_file_name=disk_file_name,
disk_image=disk_image,
disk_size=disk_size,
disk_type=disk_type,
enable_qcow=enable_qcow,
saltenv=saltenv,
)
# Seed only if there is an image specified
if seed and disk_image:
log.debug('Seed command is {0}'.format(seed_cmd))
__salt__[seed_cmd](
img_dest,
id_=name,
config=kwargs.get('config'),
install=install,
pub_key=pub_key,
priv_key=priv_key,
)
else:
# Unknown hypervisor
raise SaltInvocationError(
'Unsupported hypervisor when handling disk image: {0}'
.format(hypervisor)
)
log.debug('Generating VM XML')
kwargs['enable_vnc'] = enable_vnc
xml = _gen_xml(name, cpu, mem, diskp, nicp, hypervisor, **kwargs)
@ -686,18 +793,8 @@ def init(name,
else:
raise err # a real error we should report upwards
if seed and seedable:
log.debug('Seed command is {0}'.format(seed_cmd))
__salt__[seed_cmd](
img_dest,
id_=name,
config=kwargs.get('config'),
install=install,
pub_key=pub_key,
priv_key=priv_key,
)
if start:
log.debug('Creating {0}'.format(name))
log.debug('Starting VM {0}'.format(name))
_get_domain(name).create()
return True