# Copyright (c) 2016 Red Hat Inc.
# All Rights Reserved.
#
#    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.

from cinder.api import microversions as mv
from cinder.api.openstack import wsgi
from cinder.api.schemas import clusters as schema
from cinder.api.v3.views import clusters as clusters_view
from cinder.api import validation
from cinder.common import constants
from cinder import exception
from cinder.i18n import _
from cinder import objects
from cinder.policies import clusters as policy
from cinder import utils


class ClusterController(wsgi.Controller):
    allowed_list_keys = {'name', 'binary', 'is_up', 'disabled', 'num_hosts',
                         'num_down_hosts', 'binary', 'replication_status',
                         'frozen', 'active_backend_id'}
    replication_fields = {'replication_status', 'frozen', 'active_backend_id'}

    @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
    def show(self, req, id, binary=constants.VOLUME_BINARY):
        """Return data for a given cluster name with optional binary."""
        # Let the wsgi middleware convert NotAuthorized exceptions
        context = req.environ['cinder.context']
        context.authorize(policy.GET_POLICY)
        # Let the wsgi middleware convert NotFound exceptions
        cluster = objects.Cluster.get_by_id(context, None, binary=binary,
                                            name=id, services_summary=True)
        replication_data = req.api_version_request.matches(
            mv.REPLICATION_CLUSTER)
        return clusters_view.ViewBuilder.detail(cluster, replication_data)

    @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
    def index(self, req):
        """Return a non detailed list of all existing clusters.

        Filter by is_up, disabled, num_hosts, and num_down_hosts.
        """
        return self._get_clusters(req, detail=False)

    @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
    def detail(self, req):
        """Return a detailed list of all existing clusters.

        Filter by is_up, disabled, num_hosts, and num_down_hosts.
        """
        return self._get_clusters(req, detail=True)

    def _get_clusters(self, req, detail):
        # Let the wsgi middleware convert NotAuthorized exceptions
        context = req.environ['cinder.context']
        context.authorize(policy.GET_ALL_POLICY)
        replication_data = req.api_version_request.matches(
            mv.REPLICATION_CLUSTER)
        filters = dict(req.GET)
        allowed = self.allowed_list_keys
        if not replication_data:
            allowed = allowed.difference(self.replication_fields)

        # Check filters are valid
        if not allowed.issuperset(filters):
            invalid_keys = set(filters).difference(allowed)
            msg = _('Invalid filter keys: %s') % ', '.join(invalid_keys)
            raise exception.InvalidInput(reason=msg)

        # Check boolean values
        for bool_key in ('disabled', 'is_up'):
            if bool_key in filters:
                filters[bool_key] = utils.get_bool_param(bool_key, req.GET)

        # For detailed view we need the services summary information
        filters['services_summary'] = detail

        clusters = objects.ClusterList.get_all(context, **filters)
        return clusters_view.ViewBuilder.list(clusters, detail,
                                              replication_data)

    def _update(
        self, context, name, binary, disabled, disabled_reason=None,
        show_replication_data=False,
    ):
        """Enable/Disable scheduling for a cluster."""
        # NOTE(geguileo): This method tries to be consistent with services
        # update endpoint API.

        # Let wsgi handle NotFound exception
        cluster = objects.Cluster.get_by_id(context, None, binary=binary,
                                            name=name)
        cluster.disabled = disabled
        cluster.disabled_reason = disabled_reason
        cluster.save()

        # We return summary data plus the disabled reason
        ret_val = clusters_view.ViewBuilder.summary(
            cluster, show_replication_data,
        )
        ret_val['cluster']['disabled_reason'] = disabled_reason

        return ret_val

    @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
    @validation.schema(schema.disable_cluster)
    def disable(self, req, body):
        # NOTE(geguileo): This method tries to be consistent with services
        # disable endpoint API.

        # Let the wsgi middleware convert NotAuthorized exceptions
        context = req.environ['cinder.context']
        context.authorize(policy.UPDATE_POLICY)

        name = body['name']
        binary = body.get('binary', constants.VOLUME_BINARY)
        disabled_reason = body.get('disabled_reason')
        if disabled_reason:
            disabled_reason = disabled_reason.strip()

        show_replication_data = req.api_version_request.matches(
            mv.REPLICATION_CLUSTER)

        return self._update(
            context, name=name, binary=binary, disabled=True,
            disabled_reason=disabled_reason,
            show_replication_data=show_replication_data)

    @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
    @validation.schema(schema.enable_cluster)
    def enable(self, req, body):
        # NOTE(geguileo): This method tries to be consistent with services
        # enable endpoint API.

        # Let the wsgi middleware convert NotAuthorized exceptions
        context = req.environ['cinder.context']
        context.authorize(policy.UPDATE_POLICY)

        name = body['name']
        binary = body.get('binary', constants.VOLUME_BINARY)

        show_replication_data = req.api_version_request.matches(
            mv.REPLICATION_CLUSTER)

        return self._update(
            context, name=name, binary=binary, disabled=False,
            show_replication_data=show_replication_data)


def create_resource():
    return wsgi.Resource(ClusterController())
