Create a new Cloud Formation stack from the given Senza definition file

def create(definition: dict, version: str, parameter: tuple,
           region: str,
           disable_rollback: bool,
           dry_run: bool,
           force: bool,
           tag: List[str],
           timeout: int,
           keep_stacks: Optional[int],
           traffic: int,
           verbose: bool,
           remote: str,
           parameter_file: Optional[str]
           ):
    """
    Create a new Cloud Formation stack from the given Senza definition file
    """
    lizzy = setup_lizzy_client(remote)
    parameter = list(parameter) or []
    if parameter_file:
        parameter.extend(read_parameter_file(parameter_file))

    if not force:  # pragma: no cover
        # supporting artifact checking would imply copying a large amount of code
        # from senza, so it should be considered out of scope until senza
        # and lizzy client are merged
        warning("WARNING: "
                "Artifact checking is still not supported by lizzy-client.")

    with Action('Requesting new stack..') as action:
        new_stack, output = lizzy.new_stack(keep_stacks, traffic,
                                            definition, version,
                                            disable_rollback, parameter,
                                            region=region,
                                            dry_run=dry_run,
                                            tags=tag)

    stack_id = '{stack_name}-{version}'.format_map(new_stack)
    print(output)

    info('Stack ID: {}'.format(stack_id))

    if dry_run:
        info("Post deployment steps skipped")
        exit(0)

    with Action('Waiting for new stack...') as action:
        if verbose:
            print()  # ensure that new states will not be printed on the same line as the action

        last_state = None
        for state in lizzy.wait_for_deployment(stack_id, region=region):
            if state != last_state and verbose:
                click.echo(' {}'.format(state))
            else:
                action.progress()
            last_state = state

        # TODO be prepared to handle all final AWS CF states
        if last_state == 'ROLLBACK_COMPLETE':
            fatal_error(
                'Stack was rollback after deployment. Check your application log for possible reasons.')
        elif last_state != 'CREATE_COMPLETE':
            fatal_error('Deployment failed: {}'.format(last_state))

    info('Deployment Successful')

    if traffic is not None:
        with Action('Requesting traffic change..'):
            try:
                lizzy.traffic(stack_id, traffic, region=region)
            except requests.ConnectionError as e:
                connection_error(e, fatal=False)
            except requests.HTTPError as e:
                agent_error(e, fatal=False)

    # TODO unit test this
    if keep_stacks is not None:
        versions_to_keep = keep_stacks + 1
        stacks_to_remove_counter = 1
        end_time = datetime.datetime.utcnow() + datetime.timedelta(seconds=timeout)
        while stacks_to_remove_counter > 0 and datetime.datetime.utcnow() <= end_time:
            try:
                all_stacks = lizzy.get_stacks([new_stack['stack_name']],
                                              region=region)
            except requests.ConnectionError as e:
                connection_error(e, fatal=False)
                error("Failed to fetch old stacks. "
                      "Old stacks WILL NOT BE DELETED")
                exit(1)
            except requests.HTTPError as e:
                agent_error(e, fatal=False)
                error("Failed to fetch old stacks. "
                      "Old stacks WILL NOT BE DELETED")
                exit(1)
            else:
                sorted_stacks = sorted(all_stacks,
                                       key=lambda stack: stack['creation_time'])
                stacks_to_remove = sorted_stacks[:-versions_to_keep]
                stacks_to_remove_counter = len(stacks_to_remove)
                with Action('Deleting old stacks..'):
                    print()
                    for old_stack in stacks_to_remove:
                        old_stack_id = '{stack_name}-{version}'.format_map(
                            old_stack)
                        if old_stack['status'] in COMPLETE_STATES:
                            click.echo(' {}'.format(old_stack_id))
                            try:
                                lizzy.delete(old_stack_id, region=region)
                                stacks_to_remove_counter -= 1
                            except requests.ConnectionError as e:
                                connection_error(e, fatal=False)
                            except requests.HTTPError as e:
                                agent_error(e, fatal=False)
                        else:
                            click.echo(' > {} current status is {} trying '
                                       'again later'.format(old_stack_id,
                                                            old_stack['status']))
                if stacks_to_remove_counter > 0:
                    time.sleep(5)

        if datetime.datetime.utcnow() > end_time:
            click.echo('Timeout waiting for related stacks to be ready.')