#!/usr/bin/env python

#    Copyright 2011 OpenStack Foundation
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""
Trove Command line tool
"""

import os
import sys

from troveclient.compat import common

# If ../trove/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
                                                os.pardir,
                                                os.pardir))
if os.path.exists(os.path.join(possible_topdir, 'troveclient',
                               '__init__.py')):
    sys.path.insert(0, possible_topdir)


class InstanceCommands(common.AuthedCommandsBase):
    """Commands to perform various instance operations and actions."""

    params = [
        'flavor',
        'id',
        'limit',
        'marker',
        'name',
        'size',
        'backup',
        'availability_zone',
        'configuration_id',
    ]

    def _get_configuration_ref(self):
        configuration_ref = None
        if self.configuration_id is not None:
            if self.configuration_id == "":
                configuration_ref = self.configuration_id
            else:
                configuration_ref = "/".join(
                    [
                        self.dbaas.client.service_url,
                        self.configuration_id,
                    ]
                )
        return configuration_ref

    def create(self):
        """Create a new instance."""
        self._require('name', 'flavor')
        volume = None
        if self.size:
            volume = {"size": self.size}
        restorePoint = None
        if self.backup:
            restorePoint = {"backupRef": self.backup}
        self._pretty_print(self.dbaas.instances.create, self.name,
                           self.flavor, volume, restorePoint=restorePoint,
                           availability_zone=self.availability_zone,
                           configuration=self._get_configuration_ref())

    # TODO(pdmars): is this actually what this should be named?
    def modify(self):
        """Modify an instance."""
        self._require('id')
        self._pretty_print(self.dbaas.instances.modify, self.id,
                           configuration=self._get_configuration_ref())

    def delete(self):
        """Delete the specified instance."""
        self._require('id')
        print(self.dbaas.instances.delete(self.id))

    def get(self):
        """Get details for the specified instance."""
        self._require('id')
        self._pretty_print(self.dbaas.instances.get, self.id)

    def backups(self):
        """Get a list of backups for the specified instance."""
        self._require('id')
        self._pretty_list(self.dbaas.instances.backups, self.id)

    def list(self):
        """List all instances for account."""
        # limit and marker are not required.
        limit = self.limit or None
        if limit:
            limit = int(limit, 10)
        self._pretty_paged(self.dbaas.instances.list)

    def resize_volume(self):
        """Resize an instance volume."""
        self._require('id', 'size')
        self._pretty_print(self.dbaas.instances.resize_volume, self.id,
                           self.size)

    def resize_instance(self):
        """Resize an instance flavor"""
        self._require('id', 'flavor')
        self._pretty_print(self.dbaas.instances.resize_instance, self.id,
                           self.flavor)

    def restart(self):
        """Restart the database."""
        self._require('id')
        self._pretty_print(self.dbaas.instances.restart, self.id)

    def configuration(self):
        """Get configuration for the specified instance."""
        self._require('id')
        self._pretty_print(self.dbaas.instances.configuration, self.id)


class FlavorsCommands(common.AuthedCommandsBase):
    """Command for listing Flavors."""

    params = []

    def list(self):
        """List the available flavors."""
        self._pretty_list(self.dbaas.flavors.list)


class DatabaseCommands(common.AuthedCommandsBase):
    """Database CRUD operations on an instance."""

    params = [
        'name',
        'id',
        'limit',
        'marker',
    ]

    def create(self):
        """Create a database."""
        self._require('id', 'name')
        databases = [{'name': self.name}]
        print(self.dbaas.databases.create(self.id, databases))

    def delete(self):
        """Delete a database."""
        self._require('id', 'name')
        print(self.dbaas.databases.delete(self.id, self.name))

    def list(self):
        """List the databases."""
        self._require('id')
        self._pretty_paged(self.dbaas.databases.list, self.id)


class UserCommands(common.AuthedCommandsBase):
    """User CRUD operations on an instance."""
    params = [
        'id',
        'database',
        'databases',
        'hostname',
        'name',
        'password',
        'new_name',
        'new_host',
        'new_password',
    ]

    def create(self):
        """Create a user in instance, with access to one or more databases."""
        self._require('id', 'name', 'password', 'databases')
        self._make_list('databases')
        databases = [{'name': dbname} for dbname in self.databases]
        users = [{'name': self.name, 'password': self.password,
                  'databases': databases}]
        if self.hostname:
            users[0]['host'] = self.hostname
        self.dbaas.users.create(self.id, users)

    def delete(self):
        """Delete the specified user"""
        self._require('id', 'name')
        self.dbaas.users.delete(self.id, self.name, self.hostname)

    def get(self):
        """Get a single user."""
        self._require('id', 'name')
        self._pretty_print(self.dbaas.users.get, self.id,
                           self.name, self.hostname)

    def update_attributes(self):
        """Update attributes of a single user."""
        self._require('id', 'name')
        self._require_at_least_one_of('new_name', 'new_host', 'new_password')
        user_new = {}
        if self.new_name:
            user_new['name'] = self.new_name
        if self.new_host:
            user_new['host'] = self.new_host
        if self.new_password:
            user_new['password'] = self.new_password
        self.dbaas.users.update_attributes(self.id, self.name, user_new,
                                           self.hostname)

    def list(self):
        """List all the users for an instance."""
        self._require('id')
        self._pretty_paged(self.dbaas.users.list, self.id)

    def access(self):
        """Show all databases the user has access to."""
        self._require('id', 'name')
        self._pretty_list(self.dbaas.users.list_access, self.id,
                          self.name, self.hostname)

    def grant(self):
        """Allow an existing user permissions to access one or more
        databases.
        """
        self._require('id', 'name', 'databases')
        self._make_list('databases')
        self.dbaas.users.grant(self.id, self.name, self.databases,
                               self.hostname)

    def revoke(self):
        """Revoke from an existing user access permissions to a database."""
        self._require('id', 'name', 'database')
        self.dbaas.users.revoke(self.id, self.name, self.database,
                                self.hostname)

    def change_password(self):
        """Change the password of a single user."""
        self._require('id', 'name', 'password')
        users = [{'name': self.name,
                  'host': self.hostname,
                  'password': self.password}]
        self.dbaas.users.change_passwords(self.id, users)


class RootCommands(common.AuthedCommandsBase):
    """Root user related operations on an instance."""

    params = [
        'id',
    ]

    def create(self):
        """Enable the instance's root user."""
        self._require('id')
        try:
            user, password = self.dbaas.root.create(self.id)
            print("User:\t\t%s\nPassword:\t%s" % (user, password))
        except Exception:
            print(sys.exc_info()[1])

    def delete(self):
        """Disable the instance's root user."""
        self._require('id')
        print(self.dbaas.root.delete(self.id))

    def enabled(self):
        """Check the instance for root access."""
        self._require('id')
        self._pretty_print(self.dbaas.root.is_root_enabled, self.id)


class VersionCommands(common.AuthedCommandsBase):
    """List available versions."""

    params = [
        'url',
    ]

    def list(self):
        """List all the supported versions."""
        self._require('url')
        self._pretty_list(self.dbaas.versions.index, self.url)


class LimitsCommands(common.AuthedCommandsBase):
    """Show the rate limits and absolute limits."""

    def list(self):
        """List the rate limits and absolute limits."""
        self._pretty_list(self.dbaas.limits.list)


class BackupsCommands(common.AuthedCommandsBase):
    """Command to manage and show backups."""
    params = ['name', 'instance', 'description']

    def get(self):
        """Get details for the specified backup."""
        self._require('id')
        self._pretty_print(self.dbaas.backups.get, self.id)

    def list(self):
        """List backups."""
        self._pretty_list(self.dbaas.backups.list)

    def create(self):
        """Create a new backup."""
        self._require('name', 'instance')
        self._pretty_print(self.dbaas.backups.create, self.name,
                           self.instance, self.description)

    def delete(self):
        """Delete a backup."""
        self._require('id')
        self._pretty_print(self.dbaas.backups.delete, self.id)


class DatastoreConfigurationParameters(common.AuthedCommandsBase):
    """Command to show configuration parameters for a datastore."""
    params = ['datastore', 'parameter']

    def parameters(self):
        """List parameters that can be set."""
        self._require('datastore')
        self._pretty_print(self.dbaas.configuration_parameters.parameters,
                           self.datastore)

    def get_parameter(self):
        """List parameters that can be set."""
        self._require('datastore', 'parameter')
        self._pretty_print(self.dbaas.configuration_parameters.get_parameter,
                           self.datastore, self.parameter)


class ConfigurationsCommands(common.AuthedCommandsBase):
    """Command to manage and show configurations."""
    params = ['name', 'instances', 'values', 'description', 'parameter']

    def get(self):
        """Get details for the specified configuration."""
        self._require('id')
        self._pretty_print(self.dbaas.configurations.get, self.id)

    def list_instances(self):
        """Get details for the specified configuration."""
        self._require('id')
        self._pretty_list(self.dbaas.configurations.instances, self.id)

    def list(self):
        """List configurations."""
        self._pretty_list(self.dbaas.configurations.list)

    def create(self):
        """Create a new configuration."""
        self._require('name', 'values')
        self._pretty_print(self.dbaas.configurations.create, self.name,
                           self.values, self.description)

    def update(self):
        """Update an existing configuration."""
        self._require('id', 'values')
        self._pretty_print(self.dbaas.configurations.update, self.id,
                           self.values, self.name, self.description)

    def edit(self):
        """Edit an existing configuration values."""
        self._require('id', 'values')
        self._pretty_print(self.dbaas.configurations.edit, self.id,
                           self.values)

    def delete(self):
        """Delete a configuration."""
        self._require('id')
        self._pretty_print(self.dbaas.configurations.delete, self.id)


class SecurityGroupCommands(common.AuthedCommandsBase):
    """Commands to list and show Security Groups For an Instance and
    create and delete security group rules for them.
    """
    params = [
        'id',
        'secgroup_id',
        'protocol',
        'from_port',
        'to_port',
        'cidr'
    ]

    def get(self):
        """Get a security group associated with an instance."""
        self._require('id')
        self._pretty_print(self.dbaas.security_groups.get, self.id)

    def list(self):
        """List all the Security Groups and the rules."""
        self._pretty_paged(self.dbaas.security_groups.list)

    def add_rule(self):
        """Add a security group rule."""
        self._require('secgroup_id', 'protocol',
                      'from_port', 'to_port', 'cidr')
        self.dbaas.security_group_rules.create(self.secgroup_id, self.protocol,
                                               self.from_port, self.to_port,
                                               self.cidr)

    def delete_rule(self):
        """Delete a security group rule."""
        self._require('id')
        self.dbaas.security_group_rules.delete(self.id)


class MetadataCommands(common.AuthedCommandsBase):
    """Commands to create/update/replace/delete/show metadata for an instance
    """
    params = [
        'instance_id',
        'metadata'
    ]

    def show(self):
        """Show instance metadata."""
        self._require('instance_id')
        self._pretty_print(self.dbaas.metadata.show(self.instance_id))


COMMANDS = {
    'auth': common.Auth,
    'instance': InstanceCommands,
    'flavor': FlavorsCommands,
    'database': DatabaseCommands,
    'limit': LimitsCommands,
    'backup': BackupsCommands,
    'configuration': ConfigurationsCommands,
    'user': UserCommands,
    'root': RootCommands,
    'version': VersionCommands,
    'secgroup': SecurityGroupCommands,
    'metadata': MetadataCommands,
}


def main():
    # Parse arguments
    load_file = True
    for index, arg in enumerate(sys.argv):
        if (arg == "auth" and len(sys.argv) > (index + 1) and
                sys.argv[index + 1] == "login"):
            load_file = False

    oparser = common.CliOptions.create_optparser(load_file)
    for k, v in COMMANDS.items():
        v._prepare_parser(oparser)
    (options, args) = oparser.parse_args()

    if not args:
        common.print_commands(COMMANDS)

    if options.verbose:
        os.environ['RDC_PP'] = "True"
        os.environ['REDDWARFCLIENT_DEBUG'] = "True"

    # Pop the command and check if it's in the known commands
    cmd = args.pop(0)
    if cmd in COMMANDS:
        fn = COMMANDS.get(cmd)
        command_object = None
        try:
            command_object = fn(oparser)
        except Exception as ex:
            if options.debug:
                raise
            print(ex)

        # Get a list of supported actions for the command
        actions = common.methods_of(command_object)

        if len(args) < 1:
            common.print_actions(cmd, actions)

        # Check for a valid action and perform that action
        action = args.pop(0)
        if action in actions:
            if not options.debug:
                try:
                    getattr(command_object, action)()
                except Exception as ex:
                    if options.debug:
                        raise
                    print(ex)
            else:
                getattr(command_object, action)()
        else:
            common.print_actions(cmd, actions)
    else:
        common.print_commands(COMMANDS)


if __name__ == '__main__':
    main()
