#!/usr/bin/python3

# Copyright 2015 SUSE LLC, Robert Schweikert
#
# This file is part of ec2deprecateimg.
#
# ec2deprecateimg is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ec2deprecateimg is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ec2deprecateimg. If not, see <http://www.gnu.org/licenses/>.

import boto3
import argparse
import os
import sys

import ec2utils.ec2utilsutils as utils
import ec2utils.ec2deprecateimg as ec2depimg
from ec2utils.ec2UtilsExceptions import *


# Set up command line argument parsing
argparse = argparse.ArgumentParser(description='Deprecate images in EC2')
argparse.add_argument(
    '-a', '--account',
    dest='accountName',
    help='Account to use',
    metavar='ACCOUNT_NAME',
)
argparse.add_argument(
    '--access-id',
    dest='accessKey',
    help='AWS access key (Optional)',
    metavar='AWS_ACCESS_KEY'
)
help_msg = 'The deprecation period, image will be tagged for removal '
help_msg += 'on "now + deprecation perion", specified in months, default '
help_msg += ' is 6 month (Optional)'
argparse.add_argument(
    '-d', '--deprecation-period',
    default=6,
    dest='depTime',
    help=help_msg,
    metavar='NUMBER_OF_MONTHS',
    type=int
)
help_msg = 'Do not perform any action, print information about actions that '
help_msg += 'would be performed instared (Optional)'
argparse.add_argument(
    '-n', '--dry-run',
    action='store_true',
    default=False,
    dest='dryRun',
    help=help_msg
)
argparse.add_argument(
    '-f', '--file',
    default=os.path.expanduser('~') + os.sep + '.ec2utils.conf',
    dest='configFilePath',
    help='Path to configuration file, default ~/.ec2utils.conf (Optional)',
    metavar='CONFIG_FILE'
)
argparse.add_argument(
    '--force',
    action='store_true',
    default=False,
    dest='force',
    help='Overwrite existing deprecation tags (Optional)',
)
# Note, one of the arguments in the group is required if --version is
# not specified. However setting this behavior through the parser
# also requiers one argument to be specified even if --version is specified
# This parser behavior is true even if --version and the group are part of the
# same subgroup
deprecation_image_id = argparse.add_mutually_exclusive_group()
deprecation_image_id.add_argument(
    '--image-id',
    dest='depImgID',
    help='The AMI ID of the image to be deprecated (Optional)',
    metavar='AMI_ID'
)
deprecation_image_id.add_argument(
    '--image-name',
    dest='depImgName',
    help='The image name of the image to be deprecated (Optional)',
    metavar='IMAGE_NAME'
)
help_msg = 'An image name fragment to match the image name of the image to be '
help_msg += 'deprecated (Optional)'
deprecation_image_id.add_argument(
    '--image-name-frag',
    dest='depImgNameFrag',
    help=help_msg,
    metavar='IMAGE_NAME_FRAGMENT'
)
help_msg = 'A regular expression to match the image name of the image to be '
help_msg += 'deprecated (Optional)'
deprecation_image_id.add_argument(
    '--image-name-match',
    dest='depImgNameMatch',
    help=help_msg,
    metavar='REGEX'
)
argparse.add_argument(
    '--image-virt-type',
    choices=['hvm', 'para'],
    dest='virtType',
    help='The virtualization type of the image to be deprecated (Optional)',
    metavar='VIRT_TYPE'
)
argparse.add_argument(
    '--public-only',
    action='store_true',
    default=False,
    dest='publicOnly',
    help='Only consider images that are public (Optional)'
)
# Note, one of the arguments in the group is required if --version is
# not specified. However setting this behavior through the parser
# also requiers one argument to be specified even if --version is specified
# This parser behavior is true even if --version and the group are part of the
# same subgroup
replacement_image_id = argparse.add_mutually_exclusive_group()
help_msg = 'The AMI ID of the image used as a replacement for the image(s) '
help_msg += 'being deprecated (Optional), one of --replacement-id or '
help_msg += '--replacement-name is required'
replacement_image_id.add_argument(
    '--replacement-id',
    dest='replImgID',
    help=help_msg,
    metavar='AMI_ID'
)
help_msg = 'The name of the image used as a replacement for the image(s) '
help_msg += 'being deprecated (Optional), one of --replacement-id or '
help_msg += '--replacement-name is required'
replacement_image_id.add_argument(
    '--replacement-name',
    dest='replImgName',
    help=help_msg,
    metavar='IMAGE_NAME'
)
help_msg = 'An image name fragment to match the image name of the image to be '
help_msg += 'deprecated (Optional)'
replacement_image_id.add_argument(
    '--replacement-name-frag',
    dest='replImgNameFrag',
    help=help_msg,
    metavar='IMAGE_NAME_FRAGMENT'
)
help_msg = 'A regular expression to match the image name of the image to be '
help_msg += 'deprecated (Optional)'
replacement_image_id.add_argument(
    '--replacement-name-match',
    dest='replImgNameMatch',
    help=help_msg,
    metavar='REGEX'
)
help_msg = 'Comma separated list of regions for publishing, all integrated '
help_msg += 'region sif not given (Optional)'
argparse.add_argument(
    '-r', '--regions',
    dest='regions',
    help=help_msg,
    metavar='EC2_REGIONS'
)
argparse.add_argument(
    '-s', '--secret-key',
    dest='secretKey',
    help='AWS secret access key (Optional)',
    metavar='AWS_SECRET_KEY'
)
argparse.add_argument(
    '--verbose',
    action='store_true',
    default=False,
    dest='verbose',
    help='Enable on verbose output'
)
argparse.add_argument(
    '--version',
    action='store_true',
    default=False,
    dest='version',
    help='Program version'
)

args = argparse.parse_args()

if args.version:
    import ec2utils
    version_file_name = 'deprecate_VERSION'
    base_path = os.path.dirname(ec2utils.__file__)
    version = open(base_path + os.sep + version_file_name, 'r').read()
    print(version)
    sys.exit(0)

# Explicit check required to to the group issue, see comment above
if (
        not args.depImgID and not
        args.depImgName and not
        args.depImgNameFrag and not
        args.depImgNameMatch):
    error_msg = 'ec2deprecateimg: error: one of the arguments '
    error_msg += '--image-id --image-name --image-name-frag '
    error_msg += '--image-name-match is required'
    print(error_msg)
    sys.exit(1)

# Explicit check required to to the group issue, see comment above
if (
        not args.replImgID and not
        args.replImgName and not
        args.replImgNameFrag and not
        args.replImgNameMatch):
    error_msg = 'ec2deprecateimg: error: one of the arguments '
    error_msg += '--replacement-id --replacement-name '
    error_msg += '--replacement-name-frag --replacement-name-match is required'
    print(error_msg)
    sys.exit(1)


config_file = args.configFilePath
config = None
if not os.path.isfile(config_file):
    print('Configuration file "%s" not found.' % config_file)
    sys.exit(1)
try:
    config = utils.get_config(config_file)
except Exception as e:
    print(format(e), file=sys.stderr)
    sys.exit(1)

access_key = args.accessKey
if not access_key:
    try:
        access_key = utils.get_from_config(args.accountName,
                                           config,
                                           None,
                                           'access_key_id',
                                           '--access-id')
    except EC2AccountException as e:
        print(format(e), file=sys.stderr)
        sys.exit(1)

if not access_key:
    print('Could not determine account access key', file=sys.stderr)
    sys.exit(1)

secret_key = args.secretKey
if not secret_key:
    try:
        secret_key = utils.get_from_config(args.accountName,
                                           config,
                                           None,
                                           'secret_access_key',
                                           '--secret-key')
    except EC2AccountException as e:
        print(format(e), file=sys.stderr)
        sys.exit(1)

if not secret_key:
    print('Could not determine account secret access key', file=sys.stderr)
    sys.exit(1)

regions = utils.get_regions(args, access_key, secret_key)

# Collect all the errors to be displayed later
errors = {}

deprecator = ec2depimg.EC2DeprecateImg(
        access_key=access_key,
        deprecation_period=args.depTime,
        deprecation_image_id=args.depImgID,
        deprecation_image_name=args.depImgName,
        deprecation_image_name_fragment=args.depImgNameFrag,
        deprecation_image_name_match=args.depImgNameMatch,
        force=args.force,
        image_virt_type=args.virtType,
        public_only=args.publicOnly,
        replacement_image_id=args.replImgID,
        replacement_image_name=args.replImgName,
        replacement_image_name_fragment=args.replImgNameFrag,
        replacement_image_name_match=args.replImgNameMatch,
        secret_key=secret_key,
        verbose=args.verbose)

for region in regions:
    deprecator.set_region(region)
    try:
        if args.dryRun:
            print('Dry run, image attributes will not be modified')
            deprecator.print_deprecation_info()
        else:
            deprecator.deprecate_images()
    except EC2DeprecateImgException as e:
        errors[region] = format(e)
    except Exception as e:
        if errors:
            print('Collected errors:', file=sys.stderr)
            for region, error in list(errors.items()):
                print('Region: %s -> %s' % (region, error), file=sys.stderr)
        print(format(e), file=sys.stderr)
        sys.exit(1)

if errors:
    print('Errors encountered:', file=sys.stderr)
    for region, error in list(errors.items()):
        print('Region: %s -> %s' % (region, error), file=sys.stderr)
    sys.exit(1)
