def attach_snap_to_inst(inst, snap):
     """Cleanup volume(s)."""
     wait_for(inst, 'running')
     try:
         vol, volumes = force_snap_attach(inst, snap)
         if encr:
             mnt = None
         else:
             mnt = mount_volume(vol)
         yield vol, mnt
     except BaseException as err:
         logger.exception(str(err))
         raise
     finally:
         key_filename = config.get(inst.region.name, 'KEY_FILENAME')
         with settings(host_string=inst.public_dns_name,
                       key_filename=key_filename):
             if not encr:
                 try:
                     wait_for_sudo('umount {0}'.format(mnt))
                 except:
                     pass
         for vol in volumes:
             if vol.status != 'available':
                 vol.detach(force=True)
             wait_for(vol, 'available', limit=DETACH_TIME)
             logger.info('Deleting {vol} in {vol.region}.'.format(vol=vol))
             vol.delete()
def modify_kernel(region, instance_id):
    """
    Modify old kernel for stopped instance (needed for make pv-grub working)

    .. note:: install grub-legacy-ec2 and upgrades before run this.

    region
        specify instance region;
    instance_id
        specify instance id for kernel change
    Kernels list:
        ap-southeast-1      x86_64  aki-11d5aa43
        ap-southeast-1  i386    aki-13d5aa41
        eu-west-1       x86_64  aki-4feec43b
        eu-west-1       i386    aki-4deec439
        us-east-1       x86_64  aki-427d952b
        us-east-1       i386    aki-407d9529
        us-west-1       x86_64  aki-9ba0f1de
        us-west-1       i386    aki-99a0f1dc"""
    key_filename = config.get(region, 'KEY_FILENAME')
    conn = get_region_conn(region)
    instance = get_inst_by_id(conn.region.name, instance_id)
    env.update({
        'host_string': instance.public_dns_name,
        'key_filename': key_filename,
    })
    sudo('env DEBIAN_FRONTEND=noninteractive apt-get update && '
         'env DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade && '
         'env DEBIAN_FRONTEND=noninteractive apt-get install grub-legacy-ec2')
    kernel = config.get(conn.region.name,
                        'KERNEL' + instance.architecture.upper())
    instance.stop()
    wait_for(instance, 'stopped')
    instance.modify_attribute('kernel', kernel)
    instance.start()
Example #3
0
def create_temp_inst(region=None, zone=None, key_pair=None, security_groups='',
                     synchronously=False):
    if region and zone:
        assert zone in get_region_conn(region.name).get_all_zones(), (
            '{0} doesn\'t belong to {1}'.format(zone, region))

    def create_inst_in_zone(zone, key_pair, sec_grps):
        inst = create_instance(zone.region.name, zone.name, key_pair=key_pair,
                               security_groups=sec_grps)
        inst.add_tag(config.get('DEFAULT', 'TAG_NAME'), 'temporary')
        return inst

    if zone:
        inst = create_inst_in_zone(zone, key_pair, security_groups)
    else:
        for zone in get_region_conn(region.name).get_all_zones():
            try:
                inst = create_inst_in_zone(zone, key_pair, security_groups)
            except BotoServerError as err:
                logging.debug(format_exc())
                logging.error('{0} in {1}'.format(err, zone))
                continue
            else:
                break
    try:
        yield inst
    finally:
        logger.info('Terminating the {0} in {0.region}...'.format(inst))
        inst.terminate()
        if synchronously:
            wait_for(inst, 'terminated')
Example #4
0
 def attach_snap_to_inst(inst, snap):
     """Cleanup volume(s)."""
     wait_for(inst, 'running')
     try:
         vol, volumes = force_snap_attach(inst, snap)
         if encr:
             mnt = None
         else:
             mnt = mount_volume(vol)
         yield vol, mnt
     except BaseException as err:
         logger.exception(str(err))
         raise
     finally:
         key_filename = config.get(inst.region.name, 'KEY_FILENAME')
         with settings(host_string=inst.public_dns_name,
                       key_filename=key_filename):
             if not encr:
                 try:
                     wait_for_sudo('umount {0}'.format(mnt))
                 except:
                     pass
         for vol in volumes:
             if vol.status != 'available':
                 vol.detach(force=True)
             wait_for(vol, 'available', limit=DETACH_TIME)
             logger.info('Deleting {vol} in {vol.region}.'.format(vol=vol))
             vol.delete()
Example #5
0
def modify_kernel(region, instance_id):
    """
    Modify old kernel for stopped instance (needed for make pv-grub working)

    .. note:: install grub-legacy-ec2 and upgrades before run this.

    region
        specify instance region;
    instance_id
        specify instance id for kernel change
    Kernels list:
        ap-southeast-1      x86_64  aki-11d5aa43
        ap-southeast-1  i386    aki-13d5aa41
        eu-west-1       x86_64  aki-4feec43b
        eu-west-1       i386    aki-4deec439
        us-east-1       x86_64  aki-427d952b
        us-east-1       i386    aki-407d9529
        us-west-1       x86_64  aki-9ba0f1de
        us-west-1       i386    aki-99a0f1dc"""
    key_filename = config.get(region, 'KEY_FILENAME')
    conn = get_region_conn(region)
    instance = get_inst_by_id(conn.region.name, instance_id)
    env.update({
        'host_string': instance.public_dns_name,
        'key_filename': key_filename,
    })
    sudo('env DEBIAN_FRONTEND=noninteractive apt-get update && '
         'env DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade && '
         'env DEBIAN_FRONTEND=noninteractive apt-get install grub-legacy-ec2')
    kernel = config.get(conn.region.name,
                        'KERNEL' + instance.architecture.upper())
    instance.stop()
    wait_for(instance, 'stopped')
    instance.modify_attribute('kernel', kernel)
    instance.start()
def launch_instance_from_ami(region_name,
                             ami_id,
                             inst_type=None,
                             security_groups='',
                             key_pair=None,
                             zone_name=None,
                             user_data=None):
    """Create instance from specified AMI.

    region_name
        location of the AMI and new instance;
    ami_id
        "ami-..."
    inst_type
        by default will be fetched from AMI description or used
        't1.micro' if not mentioned in the description;
    security_groups
        list of AWS Security Groups names formatted as string separated
        with semicolon ';'
    key_pair
        name of key_pair to be granted access. Will be fetched from
        config by default, may be configured per region;
    zone_name
        in string format;
    user_data
        string with OS configuration commands."""
    conn = get_region_conn(region_name)
    image = conn.get_all_images([ami_id])[0]
    inst_type = inst_type or get_descr_attr(image, 'Type') or 't1.micro'
    security_groups = filter(None, security_groups.strip(';').split(';'))
    security_groups.append(new_security_group(conn.region))
    logger.info('Launching new instance in {reg} using {image}'.format(
        reg=conn.region, image=image))
    inst = image.run(key_name=key_pair
                     or config.get(conn.region.name, 'KEY_PAIR'),
                     security_groups=security_groups,
                     instance_type=inst_type,
                     user_data=user_data
                     or config.get('user_data', 'USER_DATA'),
                     placement=zone_name).instances[0]
    wait_for(inst, 'running', limit=10 * 60)
    groups = [grp.name for grp in inst.groups]
    inst.add_tag('Security Groups', dumps(groups, separators=(',', ':')))
    add_tags(inst, image.tags)
    modify_instance_termination(conn.region.name, inst.id)
    logger.info('{inst} created in {inst.placement}'.format(inst=inst))
    info = ('\nYou may now SSH into the {inst} server, using:'
            '\n ssh -i {key} {user}@{inst.public_dns_name}')
    key_file = config.get(conn.region.name, 'KEY_FILENAME')
    logger.info(info.format(inst=inst, user=env.user, key=key_file))
    return inst
Example #7
0
def create_empty_snapshot(region, size):
    """Format new filesystem."""
    with create_temp_inst(region) as inst:
        vol = get_region_conn(region.name).create_volume(size, inst.placement)
        earmarking_tag = config.get(region.name, 'TAG_NAME')
        vol.add_tag(earmarking_tag, 'temporary')
        vol.attach(inst.id, get_avail_dev(inst))
        mount_volume(vol, mkfs=True)
        snap = vol.create_snapshot()
        snap.add_tag(earmarking_tag, 'temporary')
        vol.detach(True)
        wait_for(vol, 'available')
        vol.delete()
        return snap
Example #8
0
def create_tmp_volume(region, size):
    """Format new filesystem."""
    with create_temp_inst(region) as inst:
        earmarking_tag = config.get(region.name, 'TAG_NAME')
        try:
            vol = get_region_conn(region.name).create_volume(size,
                                                             inst.placement)
            vol.add_tag(earmarking_tag, 'temporary')
            vol.attach(inst.id, get_avail_dev(inst))
            yield vol, mount_volume(vol, mkfs=True)
        finally:
            vol.detach(force=True)
            wait_for(vol, 'available', limit=DETACH_TIME)
            vol.delete()
Example #9
0
def create_tmp_volume(region, size):
    """Format new filesystem."""
    with create_temp_inst(region) as inst:
        earmarking_tag = config.get(region.name, 'TAG_NAME')
        try:
            vol = get_region_conn(region.name).create_volume(size,
                                                             inst.placement)
            vol.add_tag(earmarking_tag, 'temporary')
            vol.attach(inst.id, get_avail_dev(inst))
            yield vol, mount_volume(vol, mkfs=True)
        finally:
            vol.detach(force=True)
            wait_for(vol, 'available', limit=DETACH_TIME)
            vol.delete()
Example #10
0
def launch_instance_from_ami(
    region_name, ami_id, inst_type=None, security_groups='', key_pair=None,
    zone_name=None):
    """Create instance from specified AMI.

    region_name
        location of the AMI and new instance;
    ami_id
        "ami-..."
    inst_type
        by default will be fetched from AMI description or used
        't1.micro' if not mentioned in the description;
    security_groups
        list of AWS Security Groups names formatted as string separated
        with semicolon ';'
    key_pair
        name of key_pair to be granted access. Will be fetched from
        config by default, may be configured per region;
    zone_name
        in string format."""
    try:
        user_data = config.get('user_data', 'USER_DATA')
    except:
        user_data = None
    conn = get_region_conn(region_name)
    image = conn.get_all_images([ami_id])[0]
    inst_type = inst_type or get_descr_attr(image, 'Type') or 't1.micro'
    security_groups = filter(None, security_groups.strip(';').split(';'))
    security_groups.append(new_security_group(conn.region))
    logger.info('Launching new instance in {reg} using {image}'
                .format(reg=conn.region, image=image))
    inst = image.run(
        key_name=key_pair or config.get(conn.region.name, 'KEY_PAIR'),
        security_groups=security_groups,
        instance_type=inst_type,
        user_data=user_data,
        placement=zone_name).instances[0]
    wait_for(inst, 'running')
    groups = [grp.name for grp in inst.groups]
    inst.add_tag('Security Groups', dumps(groups, separators=(',', ':')))
    add_tags(inst, image.tags)
    modify_instance_termination(conn.region.name, inst.id)
    logger.info('{inst} created in {inst.placement}'.format(inst=inst))
    info = ('\nYou may now SSH into the {inst} server, using:'
            '\n ssh -i {key} {user}@{inst.public_dns_name}')
    key_file = config.get(conn.region.name, 'KEY_FILENAME')
    logger.info(info.format(inst=inst, user=env.user, key=key_file))
    return inst
Example #11
0
 def force_snap_attach(inst, snap):
     """Iterate over devices until successful attachment."""
     volumes_to_delete = []
     while get_avail_dev(inst):
         vol = inst.connection.create_volume(snap.volume_size,
                                             inst.placement, snap)
         add_tags(vol, snap.tags)
         vol.add_tag(config.get('DEFAULT', 'TAG_NAME'), 'temporary')
         volumes_to_delete.append(vol)
         dev_name = get_avail_dev(inst)
         logger.debug('Got avail {0} from {1}'.format(dev_name, inst))
         vol.attach(inst.id, dev_name)
         try:
             wait_for(vol, 'attached', ['attach_data', 'status'])
         except StateNotChangedError:
             logger.error('Attempt to attach as next device')
         else:
             break
     return vol, volumes_to_delete
Example #12
0
 def force_snap_attach(inst, snap):
     """Iterate over devices until successful attachment."""
     volumes_to_delete = []
     while get_avail_dev(inst):
         vol = inst.connection.create_volume(snap.volume_size,
                                             inst.placement, snap)
         add_tags(vol, snap.tags)
         vol.add_tag(config.get('DEFAULT', 'TAG_NAME'), 'temporary')
         volumes_to_delete.append(vol)
         dev_name = get_avail_dev(inst)
         logger.debug('Got avail {0} from {1}'.format(dev_name, inst))
         vol.attach(inst.id, dev_name)
         try:
             wait_for(vol, 'attached', ['attach_data', 'status'])
         except StateNotChangedError:
             logger.error('Attempt to attach as next device')
         else:
             break
     return vol, volumes_to_delete
Example #13
0
def mount_volume(vol, mkfs=False):
    """Mount the device by SSH. Return mountpoint on success.

    vol
        volume to be mounted on the instance it is attached to."""

    wait_for(vol, 'attached', ['attach_data', 'status'])
    inst = get_inst_by_id(vol.region.name, vol.attach_data.instance_id)
    key_filename = config.get(vol.region.name, 'KEY_FILENAME')
    with settings(host_string=inst.public_dns_name, key_filename=key_filename):
        dev = get_vol_dev(vol)
        mountpoint = dev.replace('/dev/', '/media/')
        wait_for_sudo('mkdir -p {0}'.format(mountpoint))
        if mkfs:
            sudo('mkfs.ext3 {dev}'.format(dev=dev))
        sudo('mount {dev} {mnt}'.format(dev=dev, mnt=mountpoint))
        if mkfs:
            sudo('chown -R {user}:{user} {mnt}'.format(user=env.user,
                                                       mnt=mountpoint))
    logger.debug('Mounted {0} to {1} at {2}'.format(vol, inst, mountpoint))
    return mountpoint
Example #14
0
def mount_volume(vol, mkfs=False):

    """Mount the device by SSH. Return mountpoint on success.

    vol
        volume to be mounted on the instance it is attached to."""

    wait_for(vol, 'attached', ['attach_data', 'status'])
    inst = get_inst_by_id(vol.region.name, vol.attach_data.instance_id)
    key_filename = config.get(vol.region.name, 'KEY_FILENAME')
    with settings(host_string=inst.public_dns_name, key_filename=key_filename):
        dev = get_vol_dev(vol)
        mountpoint = dev.replace('/dev/', '/media/')
        wait_for_sudo('mkdir -p {0}'.format(mountpoint))
        if mkfs:
            sudo('mkfs.ext3 {dev}'.format(dev=dev))
        sudo('mount {dev} {mnt}'.format(dev=dev, mnt=mountpoint))
        if mkfs:
            sudo('chown -R {user}:{user} {mnt}'.format(user=env.user,
                                                       mnt=mountpoint))
    logger.debug('Mounted {0} to {1} at {2}'.format(vol, inst, mountpoint))
    return mountpoint
Example #15
0
def create_temp_inst(region=None,
                     zone=None,
                     key_pair=None,
                     security_groups='',
                     synchronously=False):
    if region and zone:
        assert zone in get_region_conn(
            region.name).get_all_zones(), ('{0} doesn\'t belong to {1}'.format(
                zone, region))

    def create_inst_in_zone(zone, key_pair, sec_grps):
        inst = create_instance(zone.region.name,
                               zone.name,
                               key_pair=key_pair,
                               security_groups=sec_grps)
        inst.add_tag(config.get('DEFAULT', 'TAG_NAME'), 'temporary')
        return inst

    if zone:
        inst = create_inst_in_zone(zone, key_pair, security_groups)
    else:
        for zone in get_region_conn(region.name).get_all_zones():
            try:
                inst = create_inst_in_zone(zone, key_pair, security_groups)
            except BotoServerError as err:
                logging.debug(format_exc())
                logging.error('{0} in {1}'.format(err, zone))
                continue
            else:
                break
    try:
        yield inst
    finally:
        logger.info('Terminating the {0} in {0.region}...'.format(inst))
        inst.terminate()
        if synchronously:
            wait_for(inst, 'terminated')
Example #16
0
def create_encrypted_instance(
    region_name, release='lucid', volume_size='8', architecture=None,
    type='t1.micro', name='encr_root', pw1=None, pw2=None, security_groups=''):
    """
    Creates ubuntu instance with luks-encryted root volume.

    region_name
        Region where you want to create instance;
    release
        Ubuntu release name (lucid or natty). "lucid" by default;
    volume_size
        Size of volume in Gb (always remember, that script creates boot volume
        with size 1Gb, so minimal size of whole volume is 3Gb (1Gb for /boot
        2Gb for /)). 8 by default;
    architecture
        "i386" or "x86_64".
    type
        Type of instance. 't1.micro' by default;
    name
        Name of luks encrypted volume. 'encr_root' by default;
    pw1, pw2
        You can specify passwords in parameters to suppress password prompt;
    security_groups
        List of AWS Security Groups names formatted as string separated
        with semicolon ';'.

    To unlock go to https://ip_address_of_instance (only after reboot
    or shutdown).
    You can set up to 8 passwords. Defaut boot.key and boot.crt created
    for .amazonaws.com so must work for all instances. Process of
    creation is about 20 minutes long."""
    assert volume_size >= 3, '1 GiB for /boot and 2 GiB for /'
    conn = get_region_conn(region_name)

    with config_temp_ssh(conn) as key_filename:
        key_pair = os.path.splitext(os.path.split(key_filename)[1])[0]
        zn = conn.get_all_zones()[-1]
        with create_temp_inst(zone=zn, key_pair=key_pair) as inst:
            vol = conn.create_volume(size=volume_size, zone=zn)
            dev = get_avail_dev_encr(inst)
            vol.attach(inst.id, dev)
            arch = architecture or config.get('DEFAULT', 'ARCHITECTURE')
            ubuntu_arch = 'amd64' if arch == 'x86_64' else arch
            make_encrypted_ubuntu(inst.public_dns_name, key_filename, 'ubuntu',
                                  ubuntu_arch, dev, name, release, pw1, pw2)
            description = dumps({
                'Volume': vol.id,
                'Region': vol.region.name,
                'Device': '/dev/sda',
                'Type': type,
                'Arch': arch,
                'Root_dev_name': '/dev/sda1',
                'Time': timestamp(),
            })
            snap = vol.create_snapshot(description)
            wait_for(snap, '100%', limit=SNAP_TIME)
            vol.detach(force=True)
            wait_for(vol, 'available', limit=DETACH_TIME)
            vol.delete()
            HTTPS_SG = config.get('DEFAULT', 'HTTPS_SECURITY_GROUP')
            security_groups = ';'.join([security_groups, HTTPS_SG])
            img, new_instance = create_ami(region_name, snap.id, 'RUN',
                                           security_groups=security_groups)
            logger.info('\nTo unlock go to:\n   https://{0}\n'
                        .format(new_instance.public_dns_name))
            img.deregister()
            snap.delete()
Example #17
0
def create_ami(region, snap_id, force=None, root_dev='/dev/sda1', zone_name=None,
               default_arch=None, default_type='t1.micro', security_groups=''):
    """
    Creates AMI image from given snapshot.

    Force option removes prompt request and creates new instance from
    created ami image.

    region, snap_id
        specify snapshot to be processed. Snapshot description in json
        format will be used to restore instance with same parameters.
        Will automaticaly process snapshots for same instance with near
        time (10 minutes or shorter), but for other devices (/dev/sdb,
        /dev/sdc, etc);
    force
        Run instance from ami after creation without confirmation. To
        enable set value to "RUN";
    default_arch
        architecture to use if not mentioned in snapshot description;
    default_type
        instance type to use if not mentioned in snapshot description.
        Used only if ``force`` is "RUN";
    security_groups
        list of AWS Security Groups names formatted as string separated
        with semicolon ';'. Used only if ``force`` is "RUN".
    """
    conn = get_region_conn(region)
    snap = conn.get_all_snapshots(snapshot_ids=[snap_id, ])[0]
    instance_id = get_snap_instance(snap)
    _device = get_snap_device(snap)
    snaps = conn.get_all_snapshots(owner='self')
    snapshots = [snp for snp in snaps if
        get_snap_instance(snp) == instance_id and
        get_snap_device(snp) != _device and
        abs(get_snap_time(snap) - get_snap_time(snp)) <= timedelta(minutes=10)]
    snapshot = sorted(snapshots, key=get_snap_time,
                      reverse=True) if snapshots else None
    # setup for building an EBS boot snapshot
    default_arch = default_arch or config.get('DEFAULT', 'ARCHITECTURE')
    arch = get_descr_attr(snap, 'Arch') or default_arch
    kernel = config.get(conn.region.name, 'KERNEL' + arch.upper())
    dev = re.match(r'^/dev/sda$', _device)  # if our instance encrypted
    if dev:
        kernel = config.get(conn.region.name, 'KERNEL_ENCR_' + arch.upper())
    ebs = EBSBlockDeviceType()
    ebs.snapshot_id = snap_id
    ebs.delete_on_termination = True
    block_map = BlockDeviceMapping()
    block_map[_device] = ebs
    sdb = BlockDeviceType()
    sdb.ephemeral_name = 'ephemeral0'
    block_map['/dev/sdb'] = sdb

    if snapshot:
        for s in snapshot:
            s_dev = get_snap_device(s)
            s_ebs = EBSBlockDeviceType()
            s_ebs.delete_on_termination = True
            s_ebs.snapshot_id = s.id
            block_map[s_dev] = s_ebs

    name = 'Created {0} using access key {1}'.format(timestamp(),
                                                     conn.access_key)
    name = name.replace(":", ".").replace(" ", "_")

    # create the new AMI all options from snap JSON description:
    wait_for(snap, '100%', limit=SNAP_TIME)
    result = conn.register_image(
        name=name,
        description=snap.description,
        architecture=get_descr_attr(snap, 'Arch') or default_arch,
        root_device_name=get_descr_attr(snap, 'Root_dev_name') or root_dev,
        block_device_map=block_map, kernel_id=kernel)
    sleep(2)
    image = conn.get_all_images(image_ids=[result, ])[0]
    wait_for(image, 'available', limit=10 * 60)
    add_tags(image, snap.tags)

    logger.info('The new AMI ID = {0}'.format(result))

    new_instance = None
    if force == 'RUN':
        instance_type = get_descr_attr(snap, 'Type') or default_type
        new_instance = launch_instance_from_ami(
            region, image.id, inst_type=instance_type,
            security_groups=security_groups, zone_name=zone_name)
    return image, new_instance
Example #18
0
def attach_snapshot(snap,
                    key_pair=None,
                    security_groups='',
                    inst=None,
                    encr=None):
    """Attach `snap` to `inst` or to new temporary instance.

    security_groups
        list of AWS Security Groups names formatted as string separated
        with semicolon ';'

    Yield volume, created from the `snap` and its mountpoint.

    Create temporary instance if `inst` not provided. Provide access to
    newly created temporary instance for `key_pair` and with
    `security_groups`."""

    wait_for(snap, '100%', limit=SNAP_TIME)
    assert snap.status == 'completed'

    def force_snap_attach(inst, snap):
        """Iterate over devices until successful attachment."""
        volumes_to_delete = []
        while get_avail_dev(inst):
            vol = inst.connection.create_volume(snap.volume_size,
                                                inst.placement, snap)
            add_tags(vol, snap.tags)
            vol.add_tag(config.get('DEFAULT', 'TAG_NAME'), 'temporary')
            volumes_to_delete.append(vol)
            dev_name = get_avail_dev(inst)
            logger.debug('Got avail {0} from {1}'.format(dev_name, inst))
            vol.attach(inst.id, dev_name)
            try:
                wait_for(vol, 'attached', ['attach_data', 'status'])
            except StateNotChangedError:
                logger.error('Attempt to attach as next device')
            else:
                break
        return vol, volumes_to_delete

    @contextmanager
    def attach_snap_to_inst(inst, snap):
        """Cleanup volume(s)."""
        wait_for(inst, 'running')
        try:
            vol, volumes = force_snap_attach(inst, snap)
            if encr:
                mnt = None
            else:
                mnt = mount_volume(vol)
            yield vol, mnt
        except BaseException as err:
            logger.exception(str(err))
            raise
        finally:
            key_filename = config.get(inst.region.name, 'KEY_FILENAME')
            with settings(host_string=inst.public_dns_name,
                          key_filename=key_filename):
                if not encr:
                    try:
                        wait_for_sudo('umount {0}'.format(mnt))
                    except:
                        pass
            for vol in volumes:
                if vol.status != 'available':
                    vol.detach(force=True)
                wait_for(vol, 'available', limit=DETACH_TIME)
                logger.info('Deleting {vol} in {vol.region}.'.format(vol=vol))
                vol.delete()

    if inst:
        with attach_snap_to_inst(inst, snap) as (vol, mountpoint):
            yield vol, mountpoint
    else:
        with create_temp_inst(snap.region,
                              key_pair=key_pair,
                              security_groups=security_groups) as inst:
            with attach_snap_to_inst(inst, snap) as (vol, mountpoint):
                yield vol, mountpoint
Example #19
0
def attach_snapshot(snap, key_pair=None, security_groups='', inst=None,
                    encr=None):

    """Attach `snap` to `inst` or to new temporary instance.

    security_groups
        list of AWS Security Groups names formatted as string separated
        with semicolon ';'

    Yield volume, created from the `snap` and its mountpoint.

    Create temporary instance if `inst` not provided. Provide access to
    newly created temporary instance for `key_pair` and with
    `security_groups`."""

    wait_for(snap, '100%', limit=SNAP_TIME)
    assert snap.status == 'completed'

    def force_snap_attach(inst, snap):
        """Iterate over devices until successful attachment."""
        volumes_to_delete = []
        while get_avail_dev(inst):
            vol = inst.connection.create_volume(snap.volume_size,
                                                inst.placement, snap)
            add_tags(vol, snap.tags)
            vol.add_tag(config.get('DEFAULT', 'TAG_NAME'), 'temporary')
            volumes_to_delete.append(vol)
            dev_name = get_avail_dev(inst)
            logger.debug('Got avail {0} from {1}'.format(dev_name, inst))
            wait_for(vol, 'available', ['status'])
            vol.attach(inst.id, dev_name)
            try:
                wait_for(vol, 'attached', ['attach_data', 'status'])
            except StateNotChangedError:
                logger.error('Attempt to attach as next device')
            else:
                break
        return vol, volumes_to_delete

    @contextmanager
    def attach_snap_to_inst(inst, snap):
        """Cleanup volume(s)."""
        wait_for(inst, 'running')
        try:
            vol, volumes = force_snap_attach(inst, snap)
            if encr:
                mnt = None
            else:
                mnt = mount_volume(vol)
            yield vol, mnt
        except BaseException as err:
            logger.exception(str(err))
            raise
        finally:
            key_filename = config.get(inst.region.name, 'KEY_FILENAME')
            with settings(host_string=inst.public_dns_name,
                          key_filename=key_filename):
                if not encr:
                    try:
                        wait_for_sudo('umount {0}'.format(mnt))
                    except:
                        pass
            for vol in volumes:
                if vol.status != 'available':
                    vol.detach(force=True)
                wait_for(vol, 'available', limit=DETACH_TIME)
                logger.info('Deleting {vol} in {vol.region}.'.format(vol=vol))
                vol.delete()

    if inst:
        with attach_snap_to_inst(inst, snap) as (vol, mountpoint):
            yield vol, mountpoint
    else:
        with create_temp_inst(snap.region, key_pair=key_pair,
                              security_groups=security_groups) as inst:
            with attach_snap_to_inst(inst, snap) as (vol, mountpoint):
                yield vol, mountpoint
Example #20
0
def create_ami(region,
               snap_id,
               force=None,
               root_dev='/dev/sda1',
               zone_name=None,
               default_arch=None,
               default_type='t1.micro',
               security_groups=''):
    """
    Creates AMI image from given snapshot.

    Force option removes prompt request and creates new instance from
    created ami image.

    region, snap_id
        specify snapshot to be processed. Snapshot description in json
        format will be used to restore instance with same parameters.
        Will automaticaly process snapshots for same instance with near
        time (10 minutes or shorter), but for other devices (/dev/sdb,
        /dev/sdc, etc);
    force
        Run instance from ami after creation without confirmation. To
        enable set value to "RUN";
    default_arch
        architecture to use if not mentioned in snapshot description;
    default_type
        instance type to use if not mentioned in snapshot description.
        Used only if ``force`` is "RUN";
    security_groups
        list of AWS Security Groups names formatted as string separated
        with semicolon ';'. Used only if ``force`` is "RUN".
    """
    conn = get_region_conn(region)
    snap = conn.get_all_snapshots(snapshot_ids=[
        snap_id,
    ])[0]
    instance_id = get_snap_instance(snap)
    _device = get_snap_device(snap)
    snaps = conn.get_all_snapshots(owner='self')
    snapshots = [
        snp for snp in snaps if get_snap_instance(snp) == instance_id
        and get_snap_device(snp) != _device and
        abs(get_snap_time(snap) - get_snap_time(snp)) <= timedelta(minutes=10)
    ]
    snapshot = sorted(snapshots, key=get_snap_time,
                      reverse=True) if snapshots else None
    # setup for building an EBS boot snapshot
    default_arch = default_arch or config.get('DEFAULT', 'ARCHITECTURE')
    arch = get_descr_attr(snap, 'Arch') or default_arch
    kernel = config.get(conn.region.name, 'KERNEL' + arch.upper())
    dev = re.match(r'^/dev/sda$', _device)  # if our instance encrypted
    if dev:
        kernel = config.get(conn.region.name, 'KERNEL_ENCR_' + arch.upper())
    ebs = EBSBlockDeviceType()
    ebs.snapshot_id = snap_id
    ebs.delete_on_termination = True
    block_map = BlockDeviceMapping()
    block_map[_device] = ebs
    sdb = BlockDeviceType()
    sdb.ephemeral_name = 'ephemeral0'
    block_map['/dev/sdb'] = sdb

    if snapshot:
        for s in snapshot:
            s_dev = get_snap_device(s)
            s_ebs = EBSBlockDeviceType()
            s_ebs.delete_on_termination = True
            s_ebs.snapshot_id = s.id
            block_map[s_dev] = s_ebs

    name = 'Created {0} using access key {1}'.format(timestamp(),
                                                     conn.access_key)
    name = name.replace(":", ".").replace(" ", "_")

    # create the new AMI all options from snap JSON description:
    wait_for(snap, '100%', limit=SNAP_TIME)
    result = conn.register_image(
        name=name,
        description=snap.description,
        architecture=get_descr_attr(snap, 'Arch') or default_arch,
        root_device_name=get_descr_attr(snap, 'Root_dev_name') or root_dev,
        block_device_map=block_map,
        kernel_id=kernel)
    sleep(2)
    image = conn.get_all_images(image_ids=[
        result,
    ])[0]
    wait_for(image, 'available', limit=10 * 60)
    add_tags(image, snap.tags)

    logger.info('The new AMI ID = {0}'.format(result))

    info = ('\nEnter RUN if you want to launch instance using '
            'just created {0}: '.format(image))
    new_instance = None
    if force == 'RUN' or raw_input(info).strip() == 'RUN':
        instance_type = get_descr_attr(snap, 'Type') or default_type
        new_instance = launch_instance_from_ami(
            region,
            image.id,
            inst_type=instance_type,
            security_groups=security_groups,
            zone_name=zone_name)
    return image, new_instance
Example #21
0
def create_encrypted_instance(region_name,
                              release='lucid',
                              volume_size='8',
                              architecture=None,
                              type='t1.micro',
                              name='encr_root',
                              pw1=None,
                              pw2=None,
                              security_groups=''):
    """
    Creates ubuntu instance with luks-encryted root volume.

    region_name
        Region where you want to create instance;
    release
        Ubuntu release name (lucid or natty). "lucid" by default;
    volume_size
        Size of volume in Gb (always remember, that script creates boot volume
        with size 1Gb, so minimal size of whole volume is 3Gb (1Gb for /boot
        2Gb for /)). 8 by default;
    architecture
        "i386" or "x86_64".
    type
        Type of instance. 't1.micro' by default;
    name
        Name of luks encrypted volume. 'encr_root' by default;
    pw1, pw2
        You can specify passwords in parameters to suppress password prompt;
    security_groups
        List of AWS Security Groups names formatted as string separated
        with semicolon ';'.

    To unlock go to https://ip_address_of_instance (only after reboot
    or shutdown).
    You can set up to 8 passwords. Defaut boot.key and boot.crt created
    for .amazonaws.com so must work for all instances. Process of
    creation is about 20 minutes long."""
    assert volume_size >= 3, '1 GiB for /boot and 2 GiB for /'
    conn = get_region_conn(region_name)

    with config_temp_ssh(conn) as key_filename:
        key_pair = os.path.splitext(os.path.split(key_filename)[1])[0]
        zn = conn.get_all_zones()[-1]
        with create_temp_inst(zone=zn, key_pair=key_pair) as inst:
            vol = conn.create_volume(size=volume_size, zone=zn)
            dev = get_avail_dev_encr(inst)
            vol.attach(inst.id, dev)
            arch = architecture or config.get('DEFAULT', 'ARCHITECTURE')
            ubuntu_arch = 'amd64' if arch == 'x86_64' else arch
            make_encrypted_ubuntu(inst.public_dns_name, key_filename, 'ubuntu',
                                  ubuntu_arch, dev, name, release, pw1, pw2)
            description = dumps({
                'Volume': vol.id,
                'Region': vol.region.name,
                'Device': '/dev/sda',
                'Type': type,
                'Arch': arch,
                'Root_dev_name': '/dev/sda1',
                'Time': timestamp(),
            })
            snap = vol.create_snapshot(description)
            wait_for(snap, '100%', limit=SNAP_TIME)
            vol.detach(force=True)
            wait_for(vol, 'available', limit=DETACH_TIME)
            vol.delete()
            HTTPS_SG = config.get('DEFAULT', 'HTTPS_SECURITY_GROUP')
            security_groups = ';'.join([security_groups, HTTPS_SG])
            img, new_instance = create_ami(region_name,
                                           snap.id,
                                           'RUN',
                                           security_groups=security_groups)
            logger.info('\nTo unlock go to:\n   https://{0}\n'.format(
                new_instance.public_dns_name))
            img.deregister()
            snap.delete()
Example #22
0
def create_snapshot(vol, description='', tags=None, synchronously=True,
                    consistent=False):
    """Return new snapshot for the volume.

    vol
        volume to snapshot;
    synchronously
        wait for successful completion;
    description
        description for snapshot. Will be compiled from instnace
        parameters by default;
    tags
        tags to be added to snapshot. Will be cloned from volume and from
        instance by default.
    consistent
        if consistent True, script will try to freeze fs mountpoint and create
        snapshot while it's freezed with all buffers dumped to disk.
    """
    if vol.attach_data:
        inst = get_inst_by_id(vol.region.name, vol.attach_data.instance_id)
    else:
        inst = None
    if not description and inst:
        description = dumps({
            'Volume': vol.id,
            'Region': vol.region.name,
            'Device': vol.attach_data.device,
            'Instance': inst.id,
            'Type': inst.instance_type,
            'Arch': inst.architecture,
            'Root_dev_name': inst.root_device_name,
            'Time': timestamp(),
            })

    def freeze_volume():
        key_filename = config.get(inst.region.name, 'KEY_FILENAME')
        try:
            _user = config.get('SYNC', 'USERNAME')
        except:
            _user = USERNAME
        with settings(host_string=inst.public_dns_name,
                      key_filename=key_filename, user=_user):
            run('sync', shell=False)
            run('for i in {1..20}; do sync; sleep 1; done &')

    def initiate_snapshot():
        if consistent:
            if inst.state == 'running':
                try:
                    freeze_volume()
                except:
                    logger.info('FS NOT FREEZED! '
                                'Do you have access to this server?')
        snapshot = vol.create_snapshot(description)
        if tags:
            add_tags(snapshot, tags)
        else:
            add_tags(snapshot, vol.tags)
            if inst:
                add_tags(snapshot, inst.tags)
        logger.info('{0} started from {1} in {0.region}'.format(snapshot, vol))
        return snapshot

    if synchronously:
        while True:     # Iterate unless success and delete failed snapshots.
            snapshot = initiate_snapshot()
            try:
                wait_for(snapshot, '100%', limit=SNAP_TIME)
                assert snapshot.status == 'completed', (
                    'completed with wrong status {0}'.format(snapshot.status))
            except (StateNotChangedError, AssertionError) as err:
                logger.error(str(err) + ' - deleting')
                snapshot.delete()
            else:
                break
    else:
        snapshot = initiate_snapshot()
    return snapshot
Example #23
0
def create_snapshot(vol, description='', tags=None, synchronously=True,
                    consistent=False):
    """Return new snapshot for the volume.

    vol
        volume to snapshot;
    synchronously
        wait for successful completion;
    description
        description for snapshot. Will be compiled from instnace
        parameters by default;
    tags
        tags to be added to snapshot. Will be cloned from volume and from
        instance by default.
    consistent
        if consistent True, script will try to freeze fs mountpoint and create
        snapshot while it's freezed with all buffers dumped to disk.
    """
    if vol.attach_data:
        inst = get_inst_by_id(vol.region.name, vol.attach_data.instance_id)
    else:
        inst = None
    if not description and inst:
        description = dumps({
            'Volume': vol.id,
            'Region': vol.region.name,
            'Device': vol.attach_data.device,
            'Instance': inst.id,
            'Type': inst.instance_type,
            'Arch': inst.architecture,
            'Root_dev_name': inst.root_device_name,
            'Time': timestamp(),
            })

    def freeze_volume():
        key_filename = config.get(inst.region.name, 'KEY_FILENAME')
        try:
            _user = config.get('SYNC', 'USERNAME')
        except:
            _user = USERNAME
        with settings(host_string=inst.public_dns_name,
                      key_filename=key_filename, user=_user):
            wait_for_sudo('sync', shell=False)
            run('for i in {1..20}; do sudo sync; sleep 1; done &')

    def initiate_snapshot():
        if consistent:
            if inst.state == 'running':
                try:
                    freeze_volume()
                except:
                    logger.info('FS NOT FREEZED! '
                                'Do you have access to this server?')
        snapshot = vol.create_snapshot(description)
        if tags:
            add_tags(snapshot, tags)
        else:
            add_tags(snapshot, vol.tags)
            if inst:
                add_tags(snapshot, inst.tags)
        logger.info('{0} started from {1} in {0.region}'.format(snapshot, vol))
        return snapshot

    if synchronously:
        while True:     # Iterate unless success and delete failed snapshots.
            snapshot = initiate_snapshot()
            try:
                wait_for(snapshot, '100%', limit=SNAP_TIME)
                assert snapshot.status == 'completed', (
                    'completed with wrong status {0}'.format(snapshot.status))
            except (StateNotChangedError, AssertionError) as err:
                logger.error(str(err) + ' - deleting')
                snapshot.delete()
            else:
                break
    else:
        snapshot = initiate_snapshot()
    return snapshot