_modules/vm_create.py
author Paul Tonelli <paul.tonelli@logilab.fr>
Wed, 06 Mar 2013 10:07:08 +0100
changeset 28 25e5edde5370
parent 27 1ac7131c81bd
child 41 a72cbdd66053
permissions -rw-r--r--
update to pre state : add files variation.sls and top.sls (and removed from vm_create.variate image)

#!/usr/bin/env python
'''
translation of a bash script to create a flavored virtual machine
'''

PROXY_ADDRESS = 'http://proxy.logilab.priv:3142/'
DEFAULT_PATH = '/mnt'
APT_PROXY_FILENAME = '/etc/apt/apt.conf.d/01proxy'
POLICY_FILENAME = '/usr/sbin/policy-rc.d'
POLICY_CONTENT =  'exit 101'
DEFAULT_VARIATION_NAME = 'variation.sls'
DEFAULT_MOUNT_DIR = 'variation'
DEFAULT_IMAGE_NAME = DEFAULT_MOUNT_DIR + '.img'

from os import listdir, remove, stat, environ
import shutil
import salt.utils
import logging

log = logging.getLogger(__name__)

def umount_images(path):
    '''
    umounts all images in path

    CLI Example::

        salt '*' vm_create.umount_images /mnt
    '''
    mounts = sorted([mount for mount in __salt__['mount.active']()
        if path in mount], reverse = True)
    for mount in mounts:
        print 'umounting ' + mount
        print __salt__['mount.umount'](mount)

def clean_images(path, image_dirname):
    _create_apt_proxy(path + '/' + image_dirname
            + '/etc/apt/apt.conf.d/01proxy', revert=True)
    _create_policy_ubuntu(path + '/' + image_dirname, revert=True)

def remove_images(path):
    '''
    delete all images in path

    CLI Example::

        salt '*' vm_create.remove_images /mnt
    '''
    for image in _get_images(path):
        print 'removing ' + path + '/' + image
        remove(path + '/'+ image)

def revert_all(path, files_to_delete=()):
    '''
    umounts and delete all images in path

    CLI Example::

        salt '*' vm_create.revert /mnt
    '''
    umount_images(path)
    remove_images(path)
    for file_to_delete in files_to_delete:
        remove(path + '/' + file_to_delete)

def mount_image(path, archive_name, new_size = 4):
    '''
    unrar, resize, mount image in path, add /proc and console

    CLI Example::

        salt '*' vm_create.mount_image /mnt raring_image.tar.gz
    '''
    log.info(__salt__['file.makedirs'](path + '/'))
    assert archive_name.split('.')[-1] == 'gz'
    remove_images(path)
    assert len(_get_images(path)) == 0
    print __salt__['cmd.run']('tar xzf ' + path + '/' + archive_name
            + ' --wildcards -C ' + path + ' "*.img" ')
    images = _get_images(path)
    assert len(images) == 1
    image_name = images[0]
    log.info(shutil.move(path + '/' + image_name, path + '/' + DEFAULT_IMAGE_NAME))
    image_name = DEFAULT_IMAGE_NAME
    resize_and_check(path, image_name, new_size * 1024)
    image_dirname = DEFAULT_MOUNT_DIR
    #log.info(__salt__['file.makedirs'](path + '/' + image_dirname + '/'))
    #log.info(__salt__['mount.mount'](path + '/' + image_dirname, path + '/'
    #        + image_name, opts='loop'))
    #log.info(__salt__['mount.mount'](path + '/' + image_dirname + '/proc', 'none',
    #        fstype='proc'))
    #log.info(__salt__['mount.mount'](path + '/' + image_dirname + '/dev/pts',
    #        'none', fstype='devpts'))
    #return image_name

def update_image(chroot_dirname):
    '''
    update image using apt-get. Doing so, it creates a few files.
    In order to be executed in chroot, you should use is with
    create_proxy=True unless you have /run mounted

    CLI Example::

        salt '*' vm_create.update_image /mnt/variation
    '''
    files_to_delete = []
    #_create_apt_proxy(chroot_dirname + '/etc/apt/apt.conf.d/01proxy')
    #_create_policy_ubuntu(chroot_dirname)
    #log.info(__salt__['file.remove'](chroot_dirname + "/etc/resolv.conf"))
    #log.info(salt.utils.copyfile('/etc/resolv.conf', chroot_dirname + '/etc/resolv.conf'))
    _print_if_error(_chroot_exec(chroot_dirname, 'apt-get --force-yes -y update && apt-get upgrade'))
    _print_if_error(_chroot_exec(chroot_dirname, 'apt-get --force-yes -y install salt-minion'))
    return files_to_delete

def variate_image(chroot_dirname, variation_filename=DEFAULT_VARIATION_NAME):
    '''
    do all code relevant to personalizing the machine here
    salt-minion should be available
    '''
    file_origin = 'salt://' + variation_filename

    #salt.utils.copyfile('/etc/salt/srv/' + variation_filename, chroot_dirname + '/etc/salt/srv')
    #__salt__['file.patch'](chroot_dirname + '/etc/salt/minion', '/etc/salt/srv/minion.patch')
    log.info(_chroot_exec(chroot_dirname, 'salt-call  --local state.highstate'))


def get_initrd_kernel(path, image_dirname):
    '''
    copy the kernel and ramdisk from a mounted image to path

    CLI Example::

        salt '*' vm_create.get_initrd_kernel /mnt raring_image
    '''
    return_files = []
    for filetype in ('vmlinuz','initrd'):
        myfiles = [myfile for myfile in listdir(path + '/' + image_dirname
            + '/boot/')
                if filetype in myfile]
        assert len(myfiles) == 1
        myfile = myfiles[0]
        log.info(__salt__['cmd.run']('cp ' + path + '/' + image_dirname + '/boot/'
                + myfile + ' ' + path + '/' + filetype))
        return_files.append(myfile)
    return tuple(return_files)

def create_variation(archive_name, variation_filename, path=DEFAULT_PATH,
        upload_to_glance=None):
    '''
    complete function to create the new variation from a virtual machine

    CLI Example::

        salt '*' vm_create.create_variation raring_image.tar.gz vanilla
    '''
    mount_image(path, archive_name)
    image_dirname = DEFAULT_MOUNT_DIR
    ### Exec pre state here ###
    #update_image(path + '/' + image_dirname)
    log.info(__salt__['state.sls']('ami_creator_pre'))
    variate_image(path + '/' + image_dirname, variation_filename)
    kernel_name, ramdisk_name = get_initrd_kernel(path, image_dirname)
    kernel_name = "vmlinuz_" + DEFAULT_MOUNT_DIR
    ramdisk_name = "ramdisk_" + DEFAULT_MOUNT_DIR
    #clean_images(path,image_dirname)
    #umount_images(path)
    #resize_and_check(path, image_name)
    log.info(__salt__['state.sls']('ami_creator_post'))
    if bool(upload_to_glance):
        log.info('will now upload to glance server')
        id_kernel = __salt__['openstack_client.upload_to_glance'](
                path, kernel_name, 'aki', '', image_dirname)
        id_ramdisk = __salt__['openstack_client.upload_to_glance'](
                path, ramdisk_name, 'ari', '', image_dirname)
        id_image = __salt__['openstack_client.upload_to_glance'](
                path, image_name, 'ami', 'kernel_id='
                + id_kernel + ' ramdisk_id=' + id_ramdisk)
        log.info("Reverting to initial state")
        revert_all(path, (kernel_name, ramdisk_name))
    else:
        log.info('Not uploading to glance')
        log.info("kernel name " + kernel_name)
        log.info("ramdisk name " + ramdisk_name)
    return True

def _get_images(path):
    '''
    find .img files in path
    '''
    images = [element for element in listdir(path)
            if element.endswith('img')]
    return images

def _create_apt_proxy(file_path=APT_PROXY_FILENAME,
        proxy=PROXY_ADDRESS, revert=False):
    if revert:
        return __salt__['file.remove'](file_path)
    else:
        proxy_file = open(file_path,'w')
        proxy_file.write("Acquire::http::Proxy \"" + proxy + "\";\n")
        proxy_file.close()
        return True

def _create_policy_debian(file_path=POLICY_FILENAME,
        content=POLICY_CONTENT, revert=False):
    if revert:
        return __salt__['file.remove'](file_path)
    else:
        policy_file = open(file_path,'w')
        policy_file.write('#!/bin/sh\n')
        policy_file.write(content + '\n')
        policy_file.close()
        __salt__['file.check_perms'](file_path, {}, 'root', 'root', '755')
        return True

def _create_policy_ubuntu(image_path, revert=False):
    if revert:
        print __salt__['file.remove'](image_path + "/sbin/initctl")
        log.info(_chroot_exec(image_path, "dpkg-divert --local --rename --remove /sbin/initctl" ))
    else:
        log.info(_chroot_exec(image_path, "dpkg-divert --local --rename --add /sbin/initctl" ))
        print __salt__['file.remove'](image_path + "/sbin/initctl")
        log.info(_chroot_exec(image_path, "ln -s /bin/true /sbin/initctl"))


def _chroot_exec(chroot_path, command):
    return __salt__['cmd.run_all']('chroot ' + chroot_path + ' ' + command)

def _print_if_error(ret):
    if ret['retcode'] != 0:
        log.error(ret['stderr'])