a method to import an image from another AWS region

            https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/CopyingAMIs.html

            REQUIRED: aws credentials must have valid access to both regions

        :param image_id: string with AWS id of source image
        :param region_name: string with AWS region of source image
        :return: string with AWS id of new image

def import_image(self, image_id, region_name):

        '''
            a method to import an image from another AWS region

            https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/CopyingAMIs.html

            REQUIRED: aws credentials must have valid access to both regions

        :param image_id: string with AWS id of source image
        :param region_name: string with AWS region of source image
        :return: string with AWS id of new image
        '''

        title = '%s.import_image' % self.__class__.__name__

    # validate inputs
        input_fields = {
            'image_id': image_id
        }
        for key, value in input_fields.items():
            object_title = '%s(%s=%s)' % (title, key, str(value))
            self.fields.validate(value, '.%s' % key, object_title)
        input_fields = {
            'region_name': region_name
        }
        for key, value in input_fields.items():
            object_title = '%s(%s=%s)' % (title, key, str(value))
            self.iam.fields.validate(value, '.%s' % key, object_title)
        if region_name == self.iam.region_name:
            raise ValueError('%s cannot import an image from the same region.' % title)
    
    # construct ec2 client connection for source region
        client_kwargs = {
            'service_name': 'ec2',
            'region_name': region_name,
            'aws_access_key_id': self.iam.access_id,
            'aws_secret_access_key': self.iam.secret_key
        }
        source_connection = boto3.client(**client_kwargs)

    # check existence of image
        try:
            response = source_connection.describe_images(
                ImageIds=[ image_id ]
            )
        except:
            raise ValueError('Image %s does not exist in AWS region %s.' % (image_id, region_name))
        if not 'Images' in response.keys():
            raise ValueError('Image %s does not exist in AWS region %s.' % (image_id, region_name))
        elif not response['Images'][0]:
            raise ValueError('Image %s does not exist in AWS region %s.' % (image_id, region_name))

    # check into state of image
        elif not 'State' in response['Images'][0].keys():
            from time import sleep
            from timeit import default_timer as timer
            self.iam.printer('Checking into the status of image %s in AWS region %s' % (image_id, region_name), flush=True)
            state_timeout = 0
            while not 'State' in response['Images'][0].keys():
                self.iam.printer('.', flush=True)
                sleep(3)
                state_timeout += 1
                response = source_connection.describe_images(
                    ImageIds=[ image_id ]
                )
                if state_timeout > 3:
                    raise Exception('Failure to determine status of image %s.' % image_id)
            self.iam.printer(' done.')
        image_state = response['Images'][0]['State']

    # raise error if image is deregistered or otherwise invalid
        if image_state == 'deregistered' or image_state == 'invalid' or image_state == 'transient' or image_state == 'failed':
            raise Exception('Image %s in AWS region %s is %s.' % (image_id, region_name, image_state))

    # wait while image is pending
        elif image_state == 'pending':
            from time import sleep
            from timeit import default_timer as timer
            self.iam.printer('Image %s is %s' % (image_id, image_state), flush=True)
            delay = 3
            state_timeout = 0
            while image_state != 'available':
                self.iam.printer('.', flush=True)
                sleep(delay)
                t3 = timer()
                response = source_connection.describe_images(
                    ImageIds=[ image_id ]
                )
                t4 = timer()
                state_timeout += 1
                response_time = t4 - t3
                if 3 - response_time > 0:
                    delay = 3 - response_time
                else:
                    delay = 0
                if state_timeout > 300:
                    raise Exception('Timeout. Failure initializing image %s in region %s in less than 15min' % (image_id, region_name))
                image_state = response['Images'][0]['State']
            self.iam.printer(' done.')

    # discover tags and name associated with source image
        try:
            response = source_connection.describe_images(
                ImageIds=[ image_id ]
            )
        except:
            raise AWSConnectionError(title)
        image_info = response['Images'][0]

    # construct image details from response
        image_name = image_info['Name']
        tag_list = self.iam.ingest(image_info['Tags'])

    # copy image over to current region
        self.iam.printer('Copying image %s from region %s.' % (image_id, region_name))
        try:
            response = self.connection.copy_image(
                SourceRegion=region_name,
                SourceImageId=image_id,
                Name=image_name
            )
        except:
            raise AWSConnectionError
        new_id = response['ImageId']

    # check into state of new image
        self.check_image_state(new_id, wait=False)

    # add tags from source image to new image
        self.tag_image(new_id, tag_list)
        self.iam.printer('Tags from image %s have been added to image %s.' % (image_id, new_id))

        return new_id