Ejemplo n.º 1
0
class BuildCommand(Command):
    """
    Builds a kit.

    """
    arguments = [
        Argument('-v',
                 '--version',
                 dest='kit_version',
                 default=None,
                 help='Override the x.y.z version in kit.json'),
        Argument('-i',
                 '--ignore-directory-version',
                 dest='ignore_directory_version',
                 action='store_true',
                 default=False,
                 help='Do not rename package to tortuga_kits/<name>_<version>')
    ]
    name = 'build'
    help = 'Builds the kit in the current directory'

    def execute(self, args: argparse.Namespace):
        builder = KitBuilder(
            version=args.kit_version,
            ignore_directory_version=args.ignore_directory_version)
        builder.build()
Ejemplo n.º 2
0
class InstallCommand(Command):
    """
    Install an extension.

    """
    name = 'install'
    help = 'Install an extension'

    arguments = [
        Argument('name', help='The name of the extension'),
        Argument('--upgrade',
                 action='store_true',
                 default=False,
                 help='Upgrade the extension to the latest version')
    ]

    def execute(self, args: argparse.Namespace):
        available_extensions = get_available_extensions(args)
        if args.name not in available_extensions:
            raise Exception('{} is not a valid extension name'.format(
                args.name))

        installer = get_installer(args)

        pip_cmd = [
            'pip', 'install', '--extra-index-url',
            get_python_package_repo(installer), '--trusted-host', installer
        ]

        if args.upgrade:
            pip_cmd.append('--upgrade')

        pip_cmd.append(args.name)

        subprocess.Popen(pip_cmd).wait()
Ejemplo n.º 3
0
class ListCommand(Command):
    """
    This command lists all items on an API endpoint.

    """
    name = 'list'
    help = 'List all {}'

    arguments = [
        Argument(
            '-q', '--query',
            type=str,
            nargs='*',
            help='List query parameters'
        )
    ]

    def get_help(self):
        #
        # Since this class can be used for multiple endpoints, we want to
        # customize the help text by inserting the endpoint name as
        # required
        #
        endpoint: str = self.parent.name

        return super().get_help().format(endpoint)

    def execute(self, args: argparse.Namespace):
        config: TortugaScriptConfig = self.get_config()
        ws_client = get_client(config, self.parent.name)

        query = args.query
        if not query:
            query = []

        params = self._parse_params(query)

        pretty_print(ws_client.list(**params), args.fmt)

    def _parse_params(self, query: List[str]) -> Dict[str, str]:
        """
        Takes a list of --query parameters and turns them into a dict
        suitable for passing to a funtion as **kwargs

        :param List[str] query: a list of "key=value" query arguments to
                                to parse

        :return Dict[str, str]: a dictionary of {key: value} pairs

        """
        params = {}
        for q in query:
            parts = q.split('=')
            params[parts[0]] = parts[1]

        return params
Ejemplo n.º 4
0
class ExtensionsCommand(RootCommand):
    """
    Command for managing Tortuga CLI extensions.

    """
    name = 'extensions'
    help = 'Manage Tortuga CLI extensions'

    sub_commands = [ListCommand(), InstallCommand()]

    arguments = [Argument('--all', action='store_true', default=False)]
Ejemplo n.º 5
0
class TortugaScript(Cli):
    """
    The Tortuga Cli implementation.

    """
    command_package = 'tortuga.scripts.tortuga.commands'

    arguments = [
        Argument('-v',
                 '--version',
                 action='store_true',
                 dest='version',
                 default=False,
                 help='print version and exit'),
        Argument('-d',
                 '--debug',
                 dest='debug',
                 help='set debug level; valid values are: critical, error, '
                 'warning, info, debug'),
        Argument('--config',
                 dest='config',
                 help='Path to config file (defaults to ~/.tortuga/config)'),
        Argument('--url', help='Web service URL'),
        Argument('--username', dest='username', help='Web service user name'),
        Argument('--password', dest='password', help='Web service password'),
        Argument('--token', dest='token', help='Web service token'),
        Argument('--no-verify',
                 dest='verify',
                 default=True,
                 action='store_false',
                 help="Don't verify the API SSL certificate"),
        Argument(
            '--json',
            dest='fmt',
            default='yaml',
            action='store_const',
            const='json',
            help='Output as JSON',
        )
    ]

    def __init__(self):
        super().__init__()
        self._config: Optional[TortugaScriptConfig] = None

    def get_command_package(self):
        return 'tortuga.cli.commands'

    def pre_execute(self, args: argparse.Namespace):
        self._version(args)
        self._set_log_level(args)
        self._load_config(args)

    def _version(self, args: argparse.Namespace):
        """
        Implements the --version argument.

        """
        if args.version:
            cm = ConfigManager()

            print('{0} version: {1}'.format(os.path.basename(sys.argv[0]),
                                            cm.getTortugaRelease()))
            sys.exit(0)

    def _set_log_level(self, args: argparse.Namespace):
        """
        Implements the --debug argument.

        """
        if args.debug:
            root_logger = logging.getLogger(ROOT_NAMESPACE)
            root_logger.setLevel(logging.DEBUG)

            ch = logging.StreamHandler()
            ch.setLevel(logging.DEBUG)
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
            ch.setFormatter(formatter)
            root_logger.addHandler(ch)

    def _load_config(self, args: argparse.Namespace):
        """
        Implements the --config argument.

        """
        #
        # Load a config, filename may or may-not be provided...
        #
        try:
            self._config = TortugaScriptConfig.load(args.config)

        except ConfigException as ex:
            print(str(ex))
            sys.exit(0)

        #
        # Override the config with any provided argument values
        #
        if args.url:
            self._config.url = args.url
        if args.username:
            self._config.username = args.username
        if args.password:
            self._config.password = args.password
        if args.token:
            self._config.token = args.token
        self._config.verify = args.verify

    def get_config(self) -> Optional[TortugaScriptConfig]:
        return self._config
Ejemplo n.º 6
0
class TortugaScript(Cli):
    """
    The Tortuga Cli implementation.

    """
    command_package = 'tortuga.scripts.tortuga.commands'

    arguments = [
        Argument('-v',
                 '--version',
                 action='store_true',
                 dest='version',
                 default=False,
                 help='print version and exit'),
        Argument('-d',
                 '--debug',
                 dest='debug',
                 help='set debug level; valid values are: critical, error, '
                 'warning, info, debug'),
        Argument('--url', help='Web service URL'),
        Argument('--username', dest='username', help='Web service user name'),
        Argument('--password', dest='password', help='Web service password'),
        Argument('--no-verify',
                 dest='verify',
                 default=True,
                 action='store_false',
                 help="Don't verify the API SSL certificate"),
        Argument(
            '--json',
            dest='fmt',
            default='yaml',
            action='store_const',
            const='json',
            help='Output as JSON',
        )
    ]

    def get_command_package(self):
        return 'tortuga.cli.commands'

    def pre_execute(self, args: argparse.Namespace):
        self._version(args)
        self._set_log_level(args)

    def _version(self, args: argparse.Namespace):
        """
        Implements the --version argument.

        """
        if args.version:
            cm = ConfigManager()

            print('{0} version: {1}'.format(os.path.basename(sys.argv[0]),
                                            cm.getTortugaRelease()))
            sys.exit(0)

    def _set_log_level(self, args: argparse.Namespace):
        """
        Implements the --debug argument.

        """
        if args.debug:
            root_logger = logging.getLogger(ROOT_NAMESPACE)
            root_logger.setLevel(logging.DEBUG)

            ch = logging.StreamHandler()
            ch.setLevel(logging.DEBUG)
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
            ch.setFormatter(formatter)
            root_logger.addHandler(ch)
Ejemplo n.º 7
0
class LicenseUsageCommand(Command):
    """
    Grab the usage between 2 dates
    and write to CSV file.

    IAM permission 'ce:GetCostAndUsage'
    is required to gather the data.
    """
    name = 'usage'
    help = 'Create usage report'

    arguments = [
        Argument('--start', help='YYYY-MM-DD', required=True),
        Argument('--end', help='YYYY-MM-DD', required=True),
        Argument('-o',
                 '--output',
                 help='Specify output file path',
                 type=str,
                 default=None)
    ]

    def __init__(self) -> None:
        """
        Initialise client and logger.

        :returns: None
        """
        super(LicenseUsageCommand, self).__init__()
        self._args = None
        self._client = boto3.client('ce')
        self._cm = ConfigManager()

    def execute(self, args: argparse.Namespace) -> None:
        """
        Iterate over each row and write to file.

        :returns: None
        """
        self._args = args

        start, end = self._string_to_date()

        if self._args.output:
            path: str = os.path.abspath(self._args.output)
            writer = PathWriter(path)
        else:
            writer = StreamWriter()

        with writer as f:
            for row in self._get_data(start, end):
                f.write(json.dumps(row))

    def _string_to_date(self) -> Tuple[date]:
        """
        Convert the arguments to date
        objects.

        :returns: Tuple Date Date
        """
        try:
            start: date = date.fromisoformat(self._args.start)
            end: date = date.fromisoformat(self._args.end)
        except AttributeError:  # Above method only in 3.7.
            split_start: Tuple[str] = self._args.start.split('-')
            split_end: Tuple[str] = self._args.end.split('-')

            split_start: Tuple[int] = map(int, split_start)
            split_end: Tuple[int] = map(int, split_end)

            start: date = date(*split_start)
            end: date = date(*split_end)

        return start, end

    def _get_data(self, start: date,
                  end: date) -> Generator[Dict[Any, Any], None, None]:
        """
        Get the usage data from AWS.

        :param start: Date
        :param end: Date
        :returns: Generator Dictionary
        """
        page_token: Optional[str] = None

        while True:
            if page_token:
                kwargs: dict = {'NextPageToken': page_token}
            else:
                kwargs: dict = {}

            response: Dict[Any, Any] = self._client.get_cost_and_usage(
                TimePeriod={
                    'Start': start.strftime('%Y-%m-%d'),
                    'End': end.strftime('%Y-%m-%d')
                },
                Granularity='MONTHLY',
                Metrics=['UsageQuantity'],
                GroupBy=[{
                    'Type': 'DIMENSION',
                    'Key': 'SERVICE',
                }, {
                    'Type': 'DIMENSION',
                    'Key': 'INSTANCE_TYPE'
                }],
                Filter={
                    'And': [{
                        'Tags': {
                            'Key': 'tortuga:installer_hostname',
                            'Values': [self._cm.getHost()]
                        }
                    }, {
                        'Dimensions': {
                            'Key': 'USAGE_TYPE_GROUP',
                            'Values': ['EC2: Running Hours']
                        }
                    }]
                },
                **kwargs)

            for result in response['ResultsByTime']:
                yield result

            page_token = response.get('NextPageToken', None)
            if not page_token:
                break