#   Copyright 2012-2013 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.
#

"""Compute v2 Server action implementations"""

import getpass
import logging
import os
import six
import sys

from cliff import command
from cliff import lister
from cliff import show

from novaclient.v1_1 import servers
from openstackclient.common import exceptions
from openstackclient.common import parseractions
from openstackclient.common import utils


def _format_servers_list_networks(networks):
    """Return a formatted string of a server's networks

    :param server: a Server.networks field
    :rtype: a string of formatted network addresses
    """
    output = []
    for (network, addresses) in networks.items():
        if not addresses:
            continue
        addresses_csv = ', '.join(addresses)
        group = "%s=%s" % (network, addresses_csv)
        output.append(group)
    return '; '.join(output)


def _prep_server_detail(compute_client, server):
    """Prepare the detailed server dict for printing

    :param compute_client: a compute client instance
    :param server: a Server resource
    :rtype: a dict of server details
    """
    info = server._info.copy()

    # Call .get() to retrieve all of the server information
    # as findall(name=blah) and REST /details are not the same
    # and do not return flavor and image information.
    server = compute_client.servers.get(info['id'])
    info.update(server._info)

    # Convert the image blob to a name
    image_info = info.get('image', {})
    image_id = image_info.get('id', '')
    image = utils.find_resource(compute_client.images, image_id)
    info['image'] = "%s (%s)" % (image.name, image_id)

    # Convert the flavor blob to a name
    flavor_info = info.get('flavor', {})
    flavor_id = flavor_info.get('id', '')
    flavor = utils.find_resource(compute_client.flavors, flavor_id)
    info['flavor'] = "%s (%s)" % (flavor.name, flavor_id)

    # NOTE(dtroyer): novaclient splits these into separate entries...
    # Format addresses in a useful way
    info['addresses'] = _format_servers_list_networks(server.networks)

    # Map 'metadata' field to 'properties'
    info.update(
        {'properties': utils.format_dict(info.pop('metadata'))}
    )

    # Remove values that are long and not too useful
    info.pop('links', None)

    return info


def _show_progress(progress):
    if progress:
        sys.stdout.write('\rProgress: %s' % progress)
        sys.stdout.flush()


class AddServerVolume(command.Command):
    """Add volume to server"""

    log = logging.getLogger(__name__ + '.AddServerVolume')

    def get_parser(self, prog_name):
        parser = super(AddServerVolume, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        parser.add_argument(
            'volume',
            metavar='<volume>',
            help='Volume to add (name or ID)',
        )
        parser.add_argument(
            '--device',
            metavar='<device>',
            help='Server internal device name for volume',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)" % parsed_args)

        compute_client = self.app.client_manager.compute
        volume_client = self.app.client_manager.volume

        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )
        volume = utils.find_resource(
            volume_client.volumes,
            parsed_args.volume,
        )

        compute_client.volumes.create_server_volume(
            server.id,
            volume.id,
            parsed_args.device,
        )


class AddServerSecurityGroup(command.Command):
    """Add security group to server"""

    log = logging.getLogger(__name__ + '.AddServerSecurityGroup')

    def get_parser(self, prog_name):
        parser = super(AddServerSecurityGroup, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Name or ID of server to use',
        )
        parser.add_argument(
            'group',
            metavar='<group>',
            help='Name or ID of security group to add to server',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)" % parsed_args)

        compute_client = self.app.client_manager.compute

        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )
        security_group = utils.find_resource(
            compute_client.security_groups,
            parsed_args.group,
        )

        server.add_security_group(security_group)
        return


class CreateServer(show.ShowOne):
    """Create a new server"""

    log = logging.getLogger(__name__ + '.CreateServer')

    def get_parser(self, prog_name):
        parser = super(CreateServer, self).get_parser(prog_name)
        parser.add_argument(
            'server_name',
            metavar='<server-name>',
            help='New server name')
        parser.add_argument(
            '--image',
            metavar='<image>',
            required=True,
            help='Create server from this image')
        parser.add_argument(
            '--flavor',
            metavar='<flavor>',
            required=True,
            help='Create server with this flavor')
        parser.add_argument(
            '--security-group',
            metavar='<security-group-name>',
            action='append',
            default=[],
            help='Security group to assign to this server '
                 '(repeat for multiple groups)')
        parser.add_argument(
            '--key-name',
            metavar='<key-name>',
            help='Keypair to inject into this server (optional extension)')
        parser.add_argument(
            '--property',
            metavar='<key=value>',
            action=parseractions.KeyValueAction,
            help='Set a property on this server '
                 '(repeat for multiple values)')
        parser.add_argument(
            '--file',
            metavar='<dest-filename=source-filename>',
            action='append',
            default=[],
            help='File to inject into image before boot '
                 '(repeat for multiple files)')
        parser.add_argument(
            '--user-data',
            metavar='<user-data>',
            help='User data file to serve from the metadata server')
        parser.add_argument(
            '--availability-zone',
            metavar='<zone-name>',
            help='Select an availability zone for the server')
        parser.add_argument(
            '--block-device-mapping',
            metavar='<dev-name=mapping>',
            action='append',
            default=[],
            help='Map block devices; map is '
                 '<id>:<type>:<size(GB)>:<delete_on_terminate> '
                 '(optional extension)')
        parser.add_argument(
            '--nic',
            metavar='<nic-config-string>',
            action='append',
            default=[],
            help='Specify NIC configuration (optional extension)')
        parser.add_argument(
            '--hint',
            metavar='<key=value>',
            action='append',
            default=[],
            help='Hints for the scheduler (optional extension)')
        parser.add_argument(
            '--config-drive',
            metavar='<config-drive-volume>|True',
            default=False,
            help='Use specified volume as the config drive, '
                 'or \'True\' to use an ephemeral drive')
        parser.add_argument(
            '--min',
            metavar='<count>',
            type=int,
            default=1,
            help='Minimum number of servers to launch (default=1)')
        parser.add_argument(
            '--max',
            metavar='<count>',
            type=int,
            default=1,
            help='Maximum number of servers to launch (default=1)')
        parser.add_argument(
            '--wait',
            action='store_true',
            help='Wait for build to complete',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)
        compute_client = self.app.client_manager.compute

        # Lookup parsed_args.image
        image = utils.find_resource(compute_client.images,
                                    parsed_args.image)

        # Lookup parsed_args.flavor
        flavor = utils.find_resource(compute_client.flavors,
                                     parsed_args.flavor)

        boot_args = [parsed_args.server_name, image, flavor]

        files = {}
        for f in parsed_args.file:
            dst, src = f.split('=', 1)
            try:
                files[dst] = open(src)
            except IOError as e:
                raise exceptions.CommandError("Can't open '%s': %s" % (src, e))

        if parsed_args.min > parsed_args.max:
            raise exceptions.CommandError("min instances should be <= "
                                          "max instances")
        if parsed_args.min < 1:
            raise exceptions.CommandError("min instances should be > 0")
        if parsed_args.max < 1:
            raise exceptions.CommandError("max instances should be > 0")

        userdata = None
        if parsed_args.user_data:
            try:
                userdata = open(parsed_args.user_data)
            except IOError as e:
                raise exceptions.CommandError("Can't open '%s': %s" %
                                              (parsed_args.user_data, e))

        block_device_mapping = dict(v.split('=', 1)
                                    for v in parsed_args.block_device_mapping)

        nics = []
        for nic_str in parsed_args.nic:
            nic_info = {"net-id": "", "v4-fixed-ip": ""}
            nic_info.update(dict(kv_str.split("=", 1)
                            for kv_str in nic_str.split(",")))
            nics.append(nic_info)

        hints = {}
        for hint in parsed_args.hint:
            key, _sep, value = hint.partition('=')
            # NOTE(vish): multiple copies of the same hint will
            #             result in a list of values
            if key in hints:
                if isinstance(hints[key], basestring):
                    hints[key] = [hints[key]]
                hints[key] += [value]
            else:
                hints[key] = value

        # What does a non-boolean value for config-drive do?
        # --config-drive argument is either a volume id or
        # 'True' (or '1') to use an ephemeral volume
        if str(parsed_args.config_drive).lower() in ("true", "1"):
            config_drive = True
        elif str(parsed_args.config_drive).lower() in ("false", "0",
                                                       "", "none"):
            config_drive = None
        else:
            config_drive = parsed_args.config_drive

        boot_kwargs = dict(
            meta=parsed_args.property,
            files=files,
            reservation_id=None,
            min_count=parsed_args.min,
            max_count=parsed_args.max,
            security_groups=parsed_args.security_group,
            userdata=userdata,
            key_name=parsed_args.key_name,
            availability_zone=parsed_args.availability_zone,
            block_device_mapping=block_device_mapping,
            nics=nics,
            scheduler_hints=hints,
            config_drive=config_drive)

        self.log.debug('boot_args: %s' % boot_args)
        self.log.debug('boot_kwargs: %s' % boot_kwargs)
        server = compute_client.servers.create(*boot_args, **boot_kwargs)

        if parsed_args.wait:
            if utils.wait_for_status(
                compute_client.servers.get,
                server.id,
                callback=_show_progress,
            ):
                sys.stdout.write('\n')
            else:
                self.log.error('Error creating server: %s' %
                               parsed_args.server_name)
                sys.stdout.write('\nError creating server')
                raise SystemExit

        details = _prep_server_detail(compute_client, server)
        return zip(*sorted(details.iteritems()))


class DeleteServer(command.Command):
    """Delete server command"""

    log = logging.getLogger(__name__ + '.DeleteServer')

    def get_parser(self, prog_name):
        parser = super(DeleteServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Name or ID of server to delete')
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)
        compute_client = self.app.client_manager.compute
        server = utils.find_resource(
            compute_client.servers, parsed_args.server)
        compute_client.servers.delete(server.id)
        return


class ListServer(lister.Lister):
    """List servers"""

    log = logging.getLogger(__name__ + '.ListServer')

    def get_parser(self, prog_name):
        parser = super(ListServer, self).get_parser(prog_name)
        parser.add_argument(
            '--reservation-id',
            metavar='<reservation-id>',
            help='only return instances that match the reservation')
        parser.add_argument(
            '--ip',
            metavar='<ip-address-regex>',
            help='regular expression to match IP address')
        parser.add_argument(
            '--ip6',
            metavar='<ip-address-regex>',
            help='regular expression to match IPv6 address')
        parser.add_argument(
            '--name',
            metavar='<name>',
            help='regular expression to match name')
        parser.add_argument(
            '--status',
            metavar='<status>',
            # FIXME(dhellmann): Add choices?
            help='search by server status')
        parser.add_argument(
            '--flavor',
            metavar='<flavor>',
            help='search by flavor ID')
        parser.add_argument(
            '--image',
            metavar='<image>',
            help='search by image ID')
        parser.add_argument(
            '--host',
            metavar='<hostname>',
            help='search by hostname')
        parser.add_argument(
            '--instance-name',
            metavar='<server-name>',
            help='regular expression to match instance name (admin only)')
        parser.add_argument(
            '--all-tenants',
            action='store_true',
            default=bool(int(os.environ.get("ALL_TENANTS", 0))),
            help='display information from all tenants (admin only)')
        parser.add_argument(
            '--long',
            action='store_true',
            default=False,
            help='Additional fields are listed in output')
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)
        compute_client = self.app.client_manager.compute
        search_opts = {
            'reservation_id': parsed_args.reservation_id,
            'ip': parsed_args.ip,
            'ip6': parsed_args.ip6,
            'name': parsed_args.name,
            'instance_name': parsed_args.instance_name,
            'status': parsed_args.status,
            'flavor': parsed_args.flavor,
            'image': parsed_args.image,
            'host': parsed_args.host,
            'all_tenants': parsed_args.all_tenants,
        }
        self.log.debug('search options: %s', search_opts)

        if parsed_args.long:
            columns = (
                'ID',
                'Name',
                'Status',
                'Networks',
                'OS-EXT-AZ:availability_zone',
                'OS-EXT-SRV-ATTR:host',
                'Metadata',
            )
            column_headers = (
                'ID',
                'Name',
                'Status',
                'Networks',
                'Availability Zone',
                'Host',
                'Properties',
            )
            mixed_case_fields = [
                'OS-EXT-AZ:availability_zone',
                'OS-EXT-SRV-ATTR:host',
            ]
        else:
            columns = ('ID', 'Name', 'Status', 'Networks')
            column_headers = columns
            mixed_case_fields = []
        data = compute_client.servers.list(search_opts=search_opts)
        return (column_headers,
                (utils.get_item_properties(
                    s, columns,
                    mixed_case_fields=mixed_case_fields,
                    formatters={
                        'Networks': _format_servers_list_networks,
                        'Metadata': utils.format_dict,
                    },
                ) for s in data))


class LockServer(command.Command):
    """Lock server"""

    log = logging.getLogger(__name__ + '.LockServer')

    def get_parser(self, prog_name):
        parser = super(LockServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        ).lock()


# FIXME(dtroyer): Here is what I want, how with argparse/cliff?
# server migrate [--wait] \
#   [--live <hostname>
#     [--shared-migration | --block-migration]
#     [--disk-overcommit | --no-disk-overcommit]]
#   <server>
#
# live_parser = parser.add_argument_group(title='Live migration options')
# then adding the groups doesn't seem to work

class MigrateServer(command.Command):
    """Migrate server to different host"""

    log = logging.getLogger(__name__ + '.MigrateServer')

    def get_parser(self, prog_name):
        parser = super(MigrateServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server to migrate (name or ID)',
        )
        parser.add_argument(
            '--wait',
            action='store_true',
            help='Wait for resize to complete',
        )
        parser.add_argument(
            '--live',
            metavar='<hostname>',
            help='Target hostname',
        )
        migration_group = parser.add_mutually_exclusive_group()
        migration_group.add_argument(
            '--shared-migration',
            dest='shared_migration',
            action='store_true',
            default=True,
            help='Perform a shared live migration (default)',
        )
        migration_group.add_argument(
            '--block-migration',
            dest='shared_migration',
            action='store_false',
            help='Perform a block live migration',
        )
        disk_group = parser.add_mutually_exclusive_group()
        disk_group.add_argument(
            '--no-disk-overcommit',
            dest='disk_overcommit',
            action='store_false',
            default=False,
            help='Do not over-commit disk on the destination host (default)',
        )
        disk_group.add_argument(
            '--disk-overcommit',
            action='store_true',
            default=False,
            help='Allow disk over-commit on the destination host',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute

        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )
        if parsed_args.live:
            server.live_migrate(
                parsed_args.live,
                parsed_args.shared_migration,
                parsed_args.disk_overcommit,
            )
        else:
            server.migrate()

        if parsed_args.wait:
            if utils.wait_for_status(
                compute_client.servers.get,
                server.id,
                #callback=_show_progress,
            ):
                sys.stdout.write('Complete\n')
            else:
                sys.stdout.write('\nError migrating server')
                raise SystemExit


class PauseServer(command.Command):
    """Pause server"""

    log = logging.getLogger(__name__ + '.PauseServer')

    def get_parser(self, prog_name):
        parser = super(PauseServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        ).pause()


class RebootServer(command.Command):
    """Perform a hard or soft server reboot"""

    log = logging.getLogger(__name__ + '.RebootServer')

    def get_parser(self, prog_name):
        parser = super(RebootServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        group = parser.add_mutually_exclusive_group()
        group.add_argument(
            '--hard',
            dest='reboot_type',
            action='store_const',
            const=servers.REBOOT_HARD,
            default=servers.REBOOT_SOFT,
            help='Perform a hard reboot',
        )
        group.add_argument(
            '--soft',
            dest='reboot_type',
            action='store_const',
            const=servers.REBOOT_SOFT,
            default=servers.REBOOT_SOFT,
            help='Perform a soft reboot',
        )
        parser.add_argument(
            '--wait',
            action='store_true',
            help='Wait for reboot to complete',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)
        compute_client = self.app.client_manager.compute
        server = utils.find_resource(
            compute_client.servers, parsed_args.server)
        server.reboot(parsed_args.reboot_type)

        if parsed_args.wait:
            if utils.wait_for_status(
                compute_client.servers.get,
                server.id,
                callback=_show_progress,
            ):
                sys.stdout.write('\nReboot complete\n')
            else:
                sys.stdout.write('\nError rebooting server\n')
                raise SystemExit


class RebuildServer(show.ShowOne):
    """Rebuild server"""

    log = logging.getLogger(__name__ + '.RebuildServer')

    def get_parser(self, prog_name):
        parser = super(RebuildServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        parser.add_argument(
            '--image',
            metavar='<image>',
            required=True,
            help='Recreate server from this image',
        )
        parser.add_argument(
            '--password',
            metavar='<password>',
            help="Set the password on the rebuilt instance",
        )
        parser.add_argument(
            '--wait',
            action='store_true',
            help='Wait for rebuild to complete',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)
        compute_client = self.app.client_manager.compute

        # Lookup parsed_args.image
        image = utils.find_resource(compute_client.images, parsed_args.image)

        server = utils.find_resource(
            compute_client.servers, parsed_args.server)

        server = server.rebuild(image, parsed_args.password)
        if parsed_args.wait:
            if utils.wait_for_status(
                compute_client.servers.get,
                server.id,
                callback=_show_progress,
            ):
                sys.stdout.write('\nComplete\n')
            else:
                sys.stdout.write('\nError rebuilding server')
                raise SystemExit

        details = _prep_server_detail(compute_client, server)
        return zip(*sorted(details.iteritems()))


class RemoveServerSecurityGroup(command.Command):
    """Remove security group from server"""

    log = logging.getLogger(__name__ + '.RemoveServerSecurityGroup')

    def get_parser(self, prog_name):
        parser = super(RemoveServerSecurityGroup, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Name or ID of server to use',
        )
        parser.add_argument(
            'group',
            metavar='<group>',
            help='Name or ID of security group to remove from server',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)" % parsed_args)

        compute_client = self.app.client_manager.compute

        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )
        security_group = utils.find_resource(
            compute_client.security_groups,
            parsed_args.group,
        )

        server.remove_security_group(security_group)


class RemoveServerVolume(command.Command):
    """Remove volume from server"""

    log = logging.getLogger(__name__ + '.RemoveServerVolume')

    def get_parser(self, prog_name):
        parser = super(RemoveServerVolume, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        parser.add_argument(
            'volume',
            metavar='<volume>',
            help='Volume to remove (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)" % parsed_args)

        compute_client = self.app.client_manager.compute
        volume_client = self.app.client_manager.volume

        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )
        volume = utils.find_resource(
            volume_client.volumes,
            parsed_args.volume,
        )

        compute_client.volumes.delete_server_volume(
            server.id,
            volume.id,
        )


class RescueServer(show.ShowOne):
    """Put server in rescue mode"""

    log = logging.getLogger(__name__ + '.RescueServer')

    def get_parser(self, prog_name):
        parser = super(RescueServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        ).rescue()
        return zip(*sorted(six.iteritems(server._info)))


class ResizeServer(command.Command):
    """Convert server to a new flavor"""

    log = logging.getLogger(__name__ + '.ResizeServer')

    def get_parser(self, prog_name):
        parser = super(ResizeServer, self).get_parser(prog_name)
        phase_group = parser.add_mutually_exclusive_group()
        phase_group.add_argument(
            '--flavor',
            metavar='<flavor>',
            help='Resize server to this flavor',
        )
        phase_group.add_argument(
            '--verify',
            action="store_true",
            help='Verify previous server resize',
        )
        phase_group.add_argument(
            '--revert',
            action="store_true",
            help='Restore server before resize',
        )
        parser.add_argument(
            '--wait',
            action='store_true',
            help='Wait for resize to complete',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )
        if parsed_args.flavor:
            flavor = utils.find_resource(
                compute_client.flavors,
                parsed_args.flavor,
            )
            server.resize(flavor)
            if parsed_args.wait:
                if utils.wait_for_status(
                    compute_client.servers.get,
                    server.id,
                    success_status=['active', 'verify_resize'],
                    callback=_show_progress,
                ):
                    sys.stdout.write('Complete\n')
                else:
                    sys.stdout.write('\nError resizing server')
                    raise SystemExit
        elif parsed_args.verify:
            server.confirm_resize()
        elif parsed_args.revert:
            server.revert_resize()


class ResumeServer(command.Command):
    """Resume server"""

    log = logging.getLogger(__name__ + '.ResumeServer')

    def get_parser(self, prog_name):
        parser = super(ResumeServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        ) .resume()


class SetServer(command.Command):
    """Set server properties"""

    log = logging.getLogger(__name__ + '.SetServer')

    def get_parser(self, prog_name):
        parser = super(SetServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        parser.add_argument(
            '--name',
            metavar='<new-name>',
            help='New server name',
        )
        parser.add_argument(
            '--root-password',
            action="store_true",
            help='Set new root password (interactive only)',
        )
        parser.add_argument(
            "--property",
            metavar="<key=value>",
            action=parseractions.KeyValueAction,
            help='Property to add/change for this server '
                 '(repeat option to set multiple properties)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )

        if parsed_args.name:
            server.update(name=parsed_args.name)

        if parsed_args.property:
            compute_client.servers.set_meta(
                server,
                parsed_args.property,
            )

        if parsed_args.root_password:
            p1 = getpass.getpass('New password: ')
            p2 = getpass.getpass('Retype new password: ')
            if p1 == p2:
                server.change_password(p1)
            else:
                raise exceptions.CommandError(
                    "Passwords do not match, password unchanged")


class ShowServer(show.ShowOne):
    """Show server details"""

    log = logging.getLogger(__name__ + '.ShowServer')

    def get_parser(self, prog_name):
        parser = super(ShowServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server to show (name or ID)',
        )
        parser.add_argument(
            '--diagnostics',
            action='store_true',
            default=False,
            help='Display diagnostics information for a given server',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)
        compute_client = self.app.client_manager.compute
        server = utils.find_resource(compute_client.servers,
                                     parsed_args.server)

        if parsed_args.diagnostics:
            (resp, data) = server.diagnostics()
            if not resp.status_code == 200:
                sys.stderr.write("Error retrieving diagnostics data")
                return ({}, {})
        else:
            data = _prep_server_detail(compute_client, server)

        return zip(*sorted(data.iteritems()))


class SuspendServer(command.Command):
    """Suspend server"""

    log = logging.getLogger(__name__ + '.SuspendServer')

    def get_parser(self, prog_name):
        parser = super(SuspendServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        ).suspend()


class UnlockServer(command.Command):
    """Unlock server"""

    log = logging.getLogger(__name__ + '.UnlockServer')

    def get_parser(self, prog_name):
        parser = super(UnlockServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        ).unlock()


class UnpauseServer(command.Command):
    """Unpause server"""

    log = logging.getLogger(__name__ + '.UnpauseServer')

    def get_parser(self, prog_name):
        parser = super(UnpauseServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        ).unpause()


class UnrescueServer(command.Command):
    """Restore server from rescue mode"""

    log = logging.getLogger(__name__ + '.UnrescueServer')

    def get_parser(self, prog_name):
        parser = super(UnrescueServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)

        compute_client = self.app.client_manager.compute
        utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        ).unrescue()


class UnsetServer(command.Command):
    """Unset server properties"""

    log = logging.getLogger(__name__ + '.UnsetServer')

    def get_parser(self, prog_name):
        parser = super(UnsetServer, self).get_parser(prog_name)
        parser.add_argument(
            'server',
            metavar='<server>',
            help='Server (name or ID)',
        )
        parser.add_argument(
            '--property',
            metavar='<key>',
            action='append',
            default=[],
            help='Property key to remove from server '
                 '(repeat to set multiple values)',
        )
        return parser

    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)' % parsed_args)
        compute_client = self.app.client_manager.compute
        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )

        if parsed_args.property:
            compute_client.servers.delete_meta(
                server,
                parsed_args.property,
            )