_modules/vm_create.py
author Paul Tonelli <paul.tonelli@logilab.fr>
Thu, 21 Mar 2013 14:35:02 +0100
changeset 4 9b1b64ddeebd
parent 1 8b17affcf529
child 5 430383a302c7
permissions -rw-r--r--
clean code

#!/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"

from os import listdir, remove, stat, environ
def _get_images(path):
    '''
    find .img files in path
    '''
    images = [element for element in listdir(path) 
            if element.split(".")[-1] == "img"]
    return images

def _create_apt_proxy(path="/etc/apt/apt.conf.d/01proxy",
        proxy=PROXY_ADDRESS):
    proxy_file = open(path,'w')
    proxy_file.write(proxy)
    proxy_file.close()
    return path

def _create_policy_script(path="/usr/sbin/policy-rc.d",
        content="exit 101"):
    policy_file = open(path,'w')
    policy_file.write("#!/bin/sh")
    policy_file.write(content)
    __salt__['file.check_perms'](path, {}, 'root', 'root', '755')
    policy_file.close()
    return path

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

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 mount.find(path) != -1], reverse = True)
    for mount in mounts:
        print "umounting " + mount
        print __salt__['mount.umount'](mount)

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(path=DEFAULT_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 resize_and_check(path, image_name, new_size=0):
    '''
    resize partition and filesystem whenever possible (size in Mb) 
    and check the integrity (for ext* fs) 

    CLI Example::
      
        salt '*' vm_create.resize_and_check /mnt raring_image.img 4
        salt '*' vm_create.resize_and_check /mnt raring_image.img
    '''
    current_size = stat(path + "/" + image_name).st_size/pow(1024, 2)
    if new_size > current_size:
        print __salt__['cmd.run']("dd if=/dev/zero bs=1024k count=" 
                + str(new_size - current_size) + " >> " + path + "/" 
                + image_name)
        minimize = ""
    else:
        minimize = "-M "
    print __salt__['cmd.run']("e2fsck -n -f " + path + "/" + image_name)
    print __salt__['cmd.run']("resize2fs -f " + minimize + path + "/" 
            + image_name)

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

    CLI Example::
      
        salt '*' vm_create.resize_and_check /mnt raring_image.tar.gz
    '''
    print __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]
    resize_and_check(path, image_name, new_size * 1024)
    image_dirname = image_name.replace(".img","")
    print __salt__['file.makedirs'](path + "/" + image_dirname + "/") 
    print __salt__['mount.mount'](path + "/" + image_dirname, path + "/" 
            + image_name, opts="loop")
    print __salt__['mount.mount'](path + "/" + image_dirname + "/proc", "none",
            fstype="proc")
    print __salt__['mount.mount'](path + "/" + image_dirname + "/dev/pts", 
            "none", fstype="devpts")
    return image_dirname, image_name

def update_image(path, image_dirname, create_proxy=True, create_policy=True):
    '''
    update image using apt-get. Doing so, it creates a 

    ***WARNING***
    it also deletes whoopsie which crashed when being updated 
    return files added which must be deletes afterwards

    CLI Example::
      
        salt '*' vm_create.resize_and_check /mnt raring_image.tar.gz
    '''
    files_to_delete = []
    if create_proxy:
        files_to_delete.append(_create_apt_proxy(path + "/" + image_dirname 
            + "/etc/apt/apt.conf.d/01proxy"))
    if create_policy:
    files_to_delete.append(_create_policy_script(path + "/" + image_dirname 
        + "/usr/bin/policy-rc.d"))
    _chroot_exec(image_dirname, "apt-get remove whoopsie")
    _chroot_exec(image_dirname, "apt-get update && apt-get upgrade")
    _chroot_exec(image_dirname, "apt-get install salt-minion")
    return files_to_delete

def flavor_image(path, image_dirname, flavor_filename):
    '''
    do all code relevant to personalizing the machine here
    salt-minion should be available
    '''
    pass

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 myfile.find(filetype) != -1]
        assert len(myfiles) == 1
        myfile = myfiles[0]
        __salt__['cmd.run']("cp " + path + "/" + image_dirname + "/boot/" 
                + myfile + " " + path)
        return_files.append(myfile)
    return tuple(return_files)

def create_flavor(archive_name, flavor_filename, path=DEFAULT_PATH, upload_to_glance=True):
    '''
    complete function to create the new flavor

    CLI Example::
      
        salt '*' vm_create.create_flavor raring_image.tar.gz vanilla
    '''
    image_dirname, image_name =  mount_image(path, archive_name)
    update_image(path, image_dirname)
    flavor_image(path, image_dirname, flavor_filename)
    kernel_name, ramdisk_name = get_initrd_kernel(path, image_dirname)
    umount_images(image_dirname)
    resize_and_check(path, image_name)
    if upload_to_glance:
        print "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)
    else:
        print "Done, no upload to do"
    #clean
    revert(path, (kernel_name, ramdisk_name))
    return True