def _snapshot_root_volume(aws_svc, instance, image_id): """ Snapshot the root volume of the given AMI. :except SnapshotError if the snapshot goes into an error state """ log.info( 'Stopping instance %s in order to create snapshot', instance.id) aws_svc.stop_instance(instance.id) wait_for_instance(aws_svc, instance.id, state='stopped') # Snapshot root volume. instance = aws_svc.get_instance(instance.id) root_dev = instance.root_device_name bdm = instance.block_device_mapping if root_dev not in bdm: # try stripping partition id root_dev = string.rstrip(root_dev, string.digits) root_vol = bdm[root_dev] vol = aws_svc.get_volume(root_vol.volume_id) aws_svc.create_tags( root_vol.volume_id, name=NAME_ORIGINAL_VOLUME % {'image_id': image_id} ) snapshot = aws_svc.create_snapshot( vol.id, name=NAME_ORIGINAL_SNAPSHOT, description=DESCRIPTION_ORIGINAL_SNAPSHOT % {'image_id': image_id} ) log.info( 'Creating snapshot %s of root volume for instance %s', snapshot.id, instance.id ) try: wait_for_snapshots(aws_svc, snapshot.id) # Now try to detach the root volume. log.info('Detaching root volume %s from %s', root_vol.volume_id, instance.id) aws_svc.detach_volume( root_vol.volume_id, instance_id=instance.id, force=True ) aws_service.wait_for_volume(aws_svc, root_vol.volume_id) # And now delete it log.info('Deleting root volume %s', root_vol.volume_id) aws_svc.delete_volume(root_vol.volume_id) except: clean_up(aws_svc, snapshot_ids=[snapshot.id]) raise ret_values = ( snapshot.id, root_dev, vol.size, vol.type, root_vol.iops) log.debug('Returning %s', str(ret_values)) return ret_values
def test_wait_for_volume(self): aws_svc, encryptor_image, guest_image = build_aws_service() # Create a dummy volume. volume = Volume() volume.size = 8 volume.id = new_id() volume.status = 'detaching' aws_svc.volumes[volume.id] = volume def transition_to_available(callback_volume): self.num_calls += 1 self.assertEqual(volume, callback_volume) self.assertFalse(self.num_calls > 5) if self.num_calls == 5: volume.status = 'available' aws_svc.get_volume_callback = transition_to_available result = aws_service.wait_for_volume(aws_svc, volume.id) self.assertEqual(volume, result)
def register_ami(aws_svc, encryptor_instance, encryptor_image, name, description, mv_bdm=None, legacy=False, guest_instance=None, mv_root_id=None): if not mv_bdm: mv_bdm = BlockDeviceMapping() # Register the new AMI. if legacy: # The encryptor instance may modify its volume attachments while # running, so we update the encryptor instance's local attributes # before reading them. encryptor_instance = aws_svc.get_instance(encryptor_instance.id) guest_id = encryptor_instance.id # Explicitly detach/delete all but root drive bdm = encryptor_instance.block_device_mapping for d in ['/dev/sda2', '/dev/sda3', '/dev/sda4', '/dev/sda5', '/dev/sdf', '/dev/sdg']: if not bdm.get(d): continue aws_svc.detach_volume( bdm[d].volume_id, instance_id=encryptor_instance.id, force=True ) aws_service.wait_for_volume(aws_svc, bdm[d].volume_id) aws_svc.delete_volume(bdm[d].volume_id) else: guest_id = guest_instance.id root_device_name = guest_instance.root_device_name # Explicitly attach new mv root to guest instance log.info('Attaching %s to %s', mv_root_id, guest_instance.id) aws_svc.attach_volume( mv_root_id, guest_instance.id, root_device_name, ) instance = wait_for_volume_attached( aws_svc, guest_instance.id, root_device_name) bdm = instance.block_device_mapping mv_bdm[root_device_name] = bdm[root_device_name] mv_bdm[root_device_name].delete_on_termination = True # Legacy: # Create AMI from (stopped) MV instance # Non-legacy: # Create AMI from original (stopped) guest instance. This # preserves any billing information found in # the identity document (i.e. billingProduct) ami = aws_svc.create_image( guest_id, name, description=description, no_reboot=True, block_device_mapping=mv_bdm ) if not legacy: log.info("Deleting volume %s" % (mv_root_id,)) aws_svc.detach_volume( mv_root_id, instance_id=guest_instance.id, force=True ) aws_service.wait_for_volume(aws_svc, mv_root_id) aws_svc.delete_volume(mv_root_id) log.info('Registered AMI %s based on the snapshots.', ami) wait_for_image(aws_svc, ami) image = aws_svc.get_image(ami, retry=True) if encryptor_image.virtualization_type == 'paravirtual': name = NAME_METAVISOR_GRUB_SNAPSHOT else: name = NAME_METAVISOR_ROOT_SNAPSHOT snap = image.block_device_mapping[image.root_device_name] aws_svc.create_tags( snap.snapshot_id, name=name, description=description ) aws_svc.create_tags(ami) ami_info = {} ami_info['volume_device_map'] = [] result_image = aws_svc.get_image(ami, retry=True) for attach_point, bdt in result_image.block_device_mapping.iteritems(): if bdt.snapshot_id: bdt_snapshot = aws_svc.get_snapshot(bdt.snapshot_id) device_details = { 'attach_point': attach_point, 'description': bdt_snapshot.tags.get('Name', ''), 'size': bdt_snapshot.volume_size } ami_info['volume_device_map'].append(device_details) ami_info['ami'] = ami ami_info['name'] = name return ami_info
def snapshot_encrypted_instance(aws_svc, enc_svc_cls, encryptor_instance, encryptor_image, image_id=None, vol_type='', iops=None, legacy=False, save_encryptor_logs=True, status_port=encryptor_service.ENCRYPTOR_STATUS_PORT): # First wait for encryption to complete host_ips = [] if encryptor_instance.ip_address: host_ips.append(encryptor_instance.ip_address) if encryptor_instance.private_ip_address: host_ips.append(encryptor_instance.private_ip_address) log.info('Adding %s to NO_PROXY environment variable' % encryptor_instance.private_ip_address) if os.environ.get('NO_PROXY'): os.environ['NO_PROXY'] += "," + \ encryptor_instance.private_ip_address else: os.environ['NO_PROXY'] = encryptor_instance.private_ip_address enc_svc = enc_svc_cls(host_ips, port=status_port) try: log.info('Waiting for encryption service on %s (port %s on %s)', encryptor_instance.id, enc_svc.port, ', '.join(host_ips)) encryptor_service.wait_for_encryptor_up(enc_svc, Deadline(600)) log.info('Creating encrypted root drive.') encryptor_service.wait_for_encryption(enc_svc) except (BracketError, encryptor_service.EncryptionError) as e: # Stop the encryptor instance, to make the console log available. stop_and_wait(aws_svc, encryptor_instance.id) log_exception_console(aws_svc, e, encryptor_instance.id) if save_encryptor_logs: log.info('Saving logs from encryptor instance in snapshot') log_snapshot = snapshot_log_volume(aws_svc, encryptor_instance.id) log.info('Encryptor logs saved in snapshot %(snapshot_id)s. ' 'Run `brkt share-logs --region %(region)s ' '--snapshot-id %(snapshot_id)s` ' 'to share this snapshot with Bracket support' % {'snapshot_id': log_snapshot.id, 'region': aws_svc.region}) raise log.info('Encrypted root drive is ready.') # The encryptor instance may modify its volume attachments while running, # so we update the encryptor instance's local attributes before reading # them. encryptor_instance = aws_svc.get_instance(encryptor_instance.id) encryptor_bdm = encryptor_instance.block_device_mapping # Stop the encryptor instance. log.info('Stopping encryptor instance %s', encryptor_instance.id) aws_svc.stop_instance(encryptor_instance.id) wait_for_instance(aws_svc, encryptor_instance.id, state='stopped') description = DESCRIPTION_SNAPSHOT % {'image_id': image_id} # Set up new Block Device Mappings log.debug('Creating block device mapping') new_bdm = BlockDeviceMapping() if not vol_type or vol_type == '': vol_type = 'gp2' # Snapshot volumes. if encryptor_image.virtualization_type == 'paravirtual': snap_guest = aws_svc.create_snapshot( encryptor_bdm['/dev/sda5'].volume_id, name=NAME_ENCRYPTED_ROOT_SNAPSHOT, description=description ) snap_bsd = aws_svc.create_snapshot( encryptor_bdm['/dev/sda2'].volume_id, name=NAME_METAVISOR_ROOT_SNAPSHOT, description=description ) snap_log = aws_svc.create_snapshot( encryptor_bdm['/dev/sda3'].volume_id, name=NAME_METAVISOR_LOG_SNAPSHOT, description=description ) log.info( 'Creating snapshots for the new encrypted AMI: %s, %s, %s', snap_guest.id, snap_bsd.id, snap_log.id) wait_for_snapshots( aws_svc, snap_guest.id, snap_bsd.id, snap_log.id) if vol_type is None: vol_type = "gp2" dev_guest_root = EBSBlockDeviceType(volume_type=vol_type, snapshot_id=snap_guest.id, iops=iops, delete_on_termination=True) mv_root_id = encryptor_bdm['/dev/sda1'].volume_id dev_mv_root = EBSBlockDeviceType(volume_type='gp2', snapshot_id=snap_bsd.id, delete_on_termination=True) dev_log = EBSBlockDeviceType(volume_type='gp2', snapshot_id=snap_log.id, delete_on_termination=True) new_bdm['/dev/sda2'] = dev_mv_root new_bdm['/dev/sda3'] = dev_log new_bdm['/dev/sda5'] = dev_guest_root else: # HVM instance type snap_guest = aws_svc.create_snapshot( encryptor_bdm['/dev/sdg'].volume_id, name=NAME_ENCRYPTED_ROOT_SNAPSHOT, description=description ) log.info( 'Creating snapshots for the new encrypted AMI: %s' % ( snap_guest.id) ) wait_for_snapshots(aws_svc, snap_guest.id) dev_guest_root = EBSBlockDeviceType(volume_type=vol_type, snapshot_id=snap_guest.id, iops=iops, delete_on_termination=True) mv_root_id = encryptor_bdm['/dev/sda1'].volume_id new_bdm['/dev/sdf'] = dev_guest_root if not legacy: log.info("Detaching new guest root %s" % (mv_root_id,)) aws_svc.detach_volume( mv_root_id, instance_id=encryptor_instance.id, force=True ) aws_service.wait_for_volume(aws_svc, mv_root_id) aws_svc.create_tags( mv_root_id, name=NAME_METAVISOR_ROOT_VOLUME) if image_id: log.debug('Getting image %s', image_id) guest_image = aws_svc.get_image(image_id) if guest_image is None: raise BracketError("Can't find image %s" % image_id) # Propagate any ephemeral drive mappings to the soloized image guest_bdm = guest_image.block_device_mapping for key in guest_bdm.keys(): guest_vol = guest_bdm[key] if guest_vol.ephemeral_name: log.info('Propagating block device mapping for %s at %s' % (guest_vol.ephemeral_name, key)) new_bdm[key] = guest_vol return mv_root_id, new_bdm