Example #1
0
	def run(self):
		# Find, parse and validate configs
		with print_scope('Checking configuration:', 'Done.\n'):
			config = get_config(self._args)

		# Create AWS client
		aws = AwsClient(config['aws_access_key'], config['aws_secret_key'], config['aws_region'])

		# Call corresponding actor to handle selected subcommand
		self._args.actor(self, aws, self._args)
Example #2
0
	def run(self):
		# Find, parse and validate configs
		with print_scope('Checking configuration:', 'Done.\n'):
			config = get_config(self._args)
			portal_spec, portal_name = get_portal_spec(self._args)

		# Create AWS client
		aws = AwsClient(config['aws_access_key'], config['aws_secret_key'], config['aws_region'])

		with print_scope('Retrieving data from AWS:', 'Done.\n'):
			# Get current user
			with step('Get user identity'):
				user = aws.get_user_identity()

			# Get spot instance
			with step('Get spot instance', error_message='Portal `{}` does not seem to be opened'.format(portal_name),
					  catch=[RuntimeError]):
				spot_instance = common.get_spot_instance(aws, portal_name, user['Arn'])

			spot_fleet_request_id = \
				filter(lambda tag: tag['Key'] == 'aws:ec2spot:fleet-request-id', spot_instance['Tags'])[0]['Value']

			# Get spot instance
			with step('Get spot request', error_message='Portal `{}` does not seem to be opened'.format(portal_name),
					  catch=[RuntimeError]):
				spot_fleet_request = common.get_spot_fleet_request(aws, spot_fleet_request_id)

		# TODO: print fleet and instance statistics

		# Cancel spot instance request
		aws.cancel_spot_fleet_request(spot_fleet_request_id)

		# Clean up volumes' tags
		volume_ids = [volume['Ebs']['VolumeId']
					  for volume in spot_instance['BlockDeviceMappings']
					  if not volume['Ebs']['DeleteOnTermination']]
		aws.remove_tags(volume_ids, 'mount-point')

		print('Portal `{}` has been closed.'.format(portal_name))
Example #3
0
	def delete_volume(self, aws, args):
		with print_scope('Retrieving data from AWS:', 'Done.\n'):
			# Get current user
			with step('Get user identity'):
				user = aws.get_user_identity()

			# Ensure that instance does not yet exist
			with step('Get volume details'):
				volume = aws.get_volumes_by_id(args.volume_id)[0]

		if not self.is_proper_volume(volume, user) and not args.force:
			raise CommandError('Volume {} is not owned by you. Use -f flag to force deletion.'.format(args.volume_id))

		aws.delete_volume(args.volume_id)

		print('Volume {} is deleted.'.format(args.volume_id))
Example #4
0
	def list_volumes(self, aws, args):
		with print_scope('Retrieving data from AWS:', 'Done.\n'):
			if not args.all:
				# Get current user
				with step('Get user identity'):
					user = aws.get_user_identity()

				# Get list of volumes owned by user
				with step('Get list of proper volumes'):
					volumes = aws.get_volumes(self.get_proper_volume_filter(user))
			else:
				# Get list of all volumes
				with step('Get list of volumes'):
					volumes = aws.get_volumes()

		# Filter tags of every volume
		volumes = (self.filter_tags(volume) for volume in volumes)

		# Pretty print list of volumes
		map(print_volume, volumes)
Example #5
0
    def run(self):
        # Find, parse and validate configs
        with print_scope('Checking configuration:', 'Done.\n'):
            config = get_config(self._args)
            portal_spec, portal_name = get_portal_spec(self._args)

            # Ensure there is at least one channel spec
            with step('Check specifications for channels',
                      error_message=
                      'Portal specification does not contain any channel'):
                channels = portal_spec['channels']
                if len(channels) == 0:
                    raise Exception()

        # Create AWS client
        aws = AwsClient(config['aws_access_key'], config['aws_secret_key'],
                        config['aws_region'])

        with print_scope('Retrieving data from AWS:', 'Done.\n'):
            # Get current user
            with step('Get user identity'):
                user = aws.get_user_identity()

            # Get spot instance
            with step('Get spot instance',
                      error_message='Portal `{}` does not seem to be opened'.
                      format(portal_name),
                      catch=[RuntimeError]):
                spot_instance = common.get_spot_instance(
                    aws, portal_name, user['Arn'])

        host_name = spot_instance['PublicDnsName']

        # Print information about the channels
        with print_scope(
                'Channels defined for portal `{}`:'.format(portal_name), ''):
            for i in range(len(channels)):
                channel = channels[i]
                with print_scope('Channel #{} ({}):'.format(
                        i, channel['direction'].upper())):
                    print('Local:   {}'.format(channel['local_path']))
                    print('Remote:  {}'.format(channel['remote_path']))

        # Specify remote host for ssh
        env.user = portal_spec['spot_instance']['remote_user']
        env.key_filename = [portal_spec['spot_instance']['identity_file']]
        env.hosts = [host_name]

        # Periodically sync files across all channels
        print('Syncing... (press ctrl+C to interrupt)')
        for channel in channels:
            is_upload = channel['direction'] == 'out'
            is_recursive = channel[
                'recursive'] if 'recursive' in channel else False
            delay = 1.0
            if 'delay' in channel:
                delay = channel['delay']
            run_periodically(sync_files, [
                channel['local_path'], channel['remote_path'], is_upload,
                is_recursive
            ], delay)
Example #6
0
    def run(self):
        # Find, parse and validate configs
        with print_scope('Checking configuration:', 'Done.\n'):
            config = get_config(self._args)
            portal_spec, portal_name = get_portal_spec(self._args)

        instance_spec = portal_spec['spot_instance']

        # Create AWS client
        aws = AwsClient(config['aws_access_key'], config['aws_secret_key'],
                        config['aws_region'])

        with print_scope('Retrieving data from AWS:', 'Done.\n'):
            # Get current user
            with step('Get user identity'):
                user = aws.get_user_identity()

            # Ensure that instance does not yet exist
            with step('Check already running instances',
                      error_message='Portal `{}` seems to be already opened'.
                      format(portal_name),
                      catch=[RuntimeError]):
                common.check_instance_not_exists(aws, portal_name, user['Arn'])

            # Ensure persistent volumes are available
            with step('Check volumes availability', catch=[RuntimeError]):
                volume_ids = [
                    volume_spec['volume_id']
                    for volume_spec in portal_spec['persistent_volumes']
                ]
                common.check_volumes_availability(aws, volume_ids)

            # If subnet Id is not provided, pick the default subnet of the availability zone
            if 'subnet_id' not in instance_spec or not instance_spec[
                    'subnet_id']:
                with step('Get subnet id', catch=[IndexError, KeyError]):
                    subnets = aws.get_subnets(
                        instance_spec['availability_zone'])
                    instance_spec['subnet_id'] = subnets[0]['SubnetId']

        # Make request for Spot instance
        instance_type = instance_spec['instance_type']
        with print_scope('Requesting a Spot instance of type {}:'.format(
                instance_type)):
            request_config = aws_helpers.single_instance_spot_fleet_request(
                portal_spec, portal_name, user['Arn'])
            response = aws.request_spot_fleet(request_config)
            spot_fleet_request_id = response['SpotFleetRequestId']

            # Wait for spot fleet request to be fulfilled
            print('Waiting for the Spot instance to be created...')
            print(
                '(usually it takes around a minute, but might take much longer)'
            )
            begin_time = datetime.datetime.now()
            next_time = begin_time
            try:
                while True:
                    # Repeat status request every N seconds
                    if datetime.datetime.now() > next_time:
                        spot_fleet_request = aws.get_spot_fleet_request(
                            spot_fleet_request_id)
                        next_time += datetime.timedelta(seconds=5)

                    # Compute time spend in waiting
                    elapsed = datetime.datetime.now() - begin_time

                    # Check request state and activity status
                    request_state = spot_fleet_request['SpotFleetRequestState']
                    if request_state == 'active':
                        spot_request_status = spot_fleet_request[
                            'ActivityStatus']
                        if spot_request_status == 'fulfilled':
                            break
                        else:
                            print(
                                'Elapsed {}s. Spot request is {} and has status `{}`'
                                .format(elapsed.seconds, request_state,
                                        spot_request_status),
                                end='\r')
                    else:
                        print('Elapsed {}s. Spot request is {}'.format(
                            elapsed.seconds, request_state),
                              end='\r')

                    sys.stdout.flush()  # ensure stdout is flushed immediately.
                    time.sleep(0.5)
            except KeyboardInterrupt:
                print('\n')
                print('Interrupting...')

                # Cancel spot instance request
                aws.cancel_spot_fleet_request(spot_fleet_request_id)

                raise CommandError('Spot request has been cancelled.')
        print('\nSpot instance is created in {} seconds.\n'.format(
            (datetime.datetime.now() - begin_time).seconds))

        # Get id of the created instance
        spot_fleet_instances = aws.get_spot_fleet_instances(
            spot_fleet_request_id)
        instance_id = spot_fleet_instances[0]['InstanceId']

        # Get information about the created instance
        instance_info = aws.get_instance(instance_id)

        # Make requests to attach persistent volumes
        with print_scope('Attaching persistent volumes:'):
            for volume_spec in portal_spec['persistent_volumes']:
                response = aws.attach_volume(instance_id,
                                             volume_spec['volume_id'],
                                             volume_spec['device'])

                # Check status code
                if response['State'] not in ['attaching', 'attached']:
                    raise CommandError(
                        'Could not attach persistent volume `{}`'.format(
                            volume_spec['volume_id']))

            # Wait for persistent volumes to be attached
            print('Waiting for the persistent volumes to be attached...')
            begin_time = datetime.datetime.now()
            next_time = begin_time
            while True:
                # Repeat status request every N seconds
                if datetime.datetime.now() > next_time:
                    volumes = aws.get_volumes_by_id(volume_ids)
                    next_time += datetime.timedelta(seconds=1)

                # Compute time spend in waiting
                elapsed = datetime.datetime.now() - begin_time

                if all([
                        volume['Attachments'][0]['State'] == 'attached'
                        for volume in volumes
                ]):
                    break
                else:
                    states = [
                        '{} - `{}`'.format(volume['VolumeId'],
                                           volume['Attachments'][0]['State'])
                        for volume in volumes
                    ]
                    print('Elapsed {}s. States: {}'.format(
                        elapsed.seconds, ', '.join(states)),
                          end='\r')

                sys.stdout.flush()  # ensure stdout is flushed immediately.
                time.sleep(0.5)
        print('\nPersistent volumes are attached in {} seconds.\n'.format(
            (datetime.datetime.now() - begin_time).seconds))

        # Configure ssh connection via fabric
        env.user = instance_spec['remote_user']
        env.key_filename = [instance_spec['identity_file']]
        env.hosts = instance_info['PublicDnsName']
        env.connection_attempts = self._fabric_retry_limit

        with print_scope('Preparing the instance:', 'Instance is ready.\n'):
            # Mount persistent volumes
            for i in range(len(portal_spec['persistent_volumes'])):
                with step('Mount volume #{}'.format(i),
                          error_message='Could not mount volume',
                          catch=[RuntimeError]):
                    volume_spec = portal_spec['persistent_volumes'][i]

                    # Mount volume
                    with hide('running', 'stdout'):
                        execute(self.mount_volume, volume_spec['device'],
                                volume_spec['mount_point'],
                                instance_spec['remote_group'],
                                instance_spec['remote_user'])

                    # Store extra information in volume's tags
                    aws.add_tags(volume_spec['volume_id'],
                                 {'mount-point': volume_spec['mount_point']})

            # TODO: consider importing and executing custom fab tasks instead
            # Install extra python packages, if needed
            if 'extra_python_packages' in instance_spec and len(
                    instance_spec['extra_python_packages']) > 0:
                with step('Install extra python packages',
                          error_message='Could not install python packages',
                          catch=[RuntimeError]):
                    python_packages = instance_spec['extra_python_packages']
                    virtual_env = instance_spec['python_virtual_env']
                    with hide('running', 'stdout'):
                        execute(self.install_python_packages, python_packages,
                                virtual_env)

        # Print summary
        print('Portal `{}` is now opened.'.format(portal_name))
        with print_scope('Summary:', ''):
            with print_scope('Instance:'):
                print('Id:              {}'.format(instance_id))
                print('Type:            {}'.format(
                    instance_info['InstanceType']))
                print('Public IP:       {}'.format(
                    instance_info['PublicIpAddress']))
                print('Public DNS name: {}'.format(
                    instance_info['PublicDnsName']))
            with print_scope('Persistent volumes:'):
                for volume_spec in portal_spec['persistent_volumes']:
                    print('{}: {}'.format(volume_spec['device'],
                                          volume_spec['mount_point']))

        # Print ssh command
        print('Use the following command to connect to the remote machine:')
        print('ssh -i "{}" {}@{}'.format(instance_spec['identity_file'],
                                         instance_spec['remote_user'],
                                         instance_info['PublicDnsName']))
Example #7
0
	def create_volume(self, aws, args):
		with print_scope('Retrieving data from AWS:', 'Done.\n'):
			# Get current user
			with step('Get user identity'):
				user = aws.get_user_identity()

			# Ensure that instance does not yet exist
			with step('Get Availability Zones'):
				availability_zones = aws.get_availability_zones()

		print('Creating new persistent volume.')

		# Get properties of the new volume
		name = args.name
		size = args.size
		availability_zone = args.zone
		snapshot_id = args.snapshot

		# Ask for name, if not provided
		if name is None:
			print('Enter name for the new volume (no name by default): ', end='')
			name = raw_input() or None

		# Ask for size, if not provide
		if args.size is None:
			print('Enter size of the new volume in Gb ({}): '.format(self._default_size), end='')
			size = raw_input() or self._default_size
			try:
				size = int(size)
			except ValueError as e:
				raise CommandError('Size has to be an integer.')

		# Check size parameter
		if size < self._min_size:
			raise CommandError('Specified size {}Gb is smaller than the lower limit of {}Gb.'
							   .format(size, self._min_size))
		elif size > self._max_size:
			raise CommandError('Specified size {}Gb is bigger than the upper limit of {}Gb.'
							   .format(size, self._max_size))

		# Ask for availability zone, if not provided
		if availability_zone is None:
			print('Enter availability zone for the new volume ({}): '.format(availability_zones[0]), end='')
			availability_zone = raw_input() or availability_zones[0]

		# Check availability zone
		if availability_zone not in availability_zones:
			raise CommandError('Unexpected availability zone "{}". Available zones are: {}.'
							   .format(availability_zone, ', '.join(availability_zones)))

		# Set tags
		tags = {'Name': name, 'created-by': user['Arn'], self._proper_tag_key: self._proper_tag_value}

		# Add user-specified tags, if provided
		if args.tags is not None:
			tags.update(self.parse_tags(args.tags))

		# Create volume
		volume_id = aws.create_volume(size, availability_zone, tags, snapshot_id)

		print('New persistent volume has been created.\nVolume id: {}'.format(volume_id))
    def show_full_info(self):
        # Find, parse and validate configs
        with print_scope('Checking configuration:', 'Done.\n'):
            config = get_config(self._args)
            portal_spec, portal_name = get_portal_spec(self._args)

        # Create AWS client
        aws = AwsClient(config['aws_access_key'], config['aws_secret_key'],
                        config['aws_region'])

        volumes = []
        with print_scope('Retrieving data from AWS:', 'Done.\n'):
            # Get current user
            with step('Get user identity'):
                aws_user = aws.get_user_identity()

            # Get spot instance
            with step('Get spot instance',
                      error_message='Portal `{}` does not seem to be opened'.
                      format(portal_name),
                      catch=[RuntimeError]):
                instance_info = aws.find_spot_instance(portal_name,
                                                       aws_user['Arn'])

            # Get persistent volumes, if portal is opened
            if instance_info is not None:
                with step('Get volumes'):
                    volume_ids = [
                        volume['Ebs']['VolumeId']
                        for volume in instance_info['BlockDeviceMappings']
                        if not volume['Ebs']['DeleteOnTermination']
                    ]
                    volumes = aws.get_volumes_by_id(volume_ids)

        # Print status
        if instance_info is not None:
            with print_scope('Summary:', ''):
                print('Name:              {}'.format(portal_name))
                print('Status:            open')

            with print_scope('Instance:', ''):
                print('Id:                {}'.format(
                    instance_info['InstanceId']))
                print('Type:              {}'.format(
                    instance_info['InstanceType']))
                print('Public IP:         {}'.format(
                    instance_info['PublicIpAddress']))
                print('Public DNS name:   {}'.format(
                    instance_info['PublicDnsName']))
                print('User:              {}'.format(
                    portal_spec['spot_instance']['remote_user']))

            with print_scope('Persistent volumes:', ''):
                for i in range(len(volumes)):
                    volume = volumes[i]
                    with print_scope('Volume #{}:'.format(i), ''):
                        self.print_volume_info(volume)

            # Print ssh command
            with print_scope(
                    'Use the following command to connect to the remote machine:'
            ):
                print('ssh -i "{}" {}@{}'.format(
                    portal_spec['spot_instance']['identity_file'],
                    portal_spec['spot_instance']['remote_user'],
                    instance_info['PublicDnsName']))
        else:
            with print_scope('Summary:'):
                print('Name:              {}'.format(portal_name))
                print('Status:            close')