# Copyright (c) 2017 Ericsson AB.
# 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.
#
# Copyright (c) 2017 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#

from oslo_config import cfg
from oslo_log import log as logging
from oslo_messaging import RemoteError

import pecan
from pecan import expose
from pecan import request

from dcmanager.api.controllers import restcomm
from dcmanager.common import consts
from dcmanager.common import exceptions
from dcmanager.common.i18n import _
from dcmanager.db import api as db_api
from dcmanager.rpc import client as rpc_client

CONF = cfg.CONF
LOG = logging.getLogger(__name__)


class SwUpdateStrategyController(object):

    def __init__(self):
        super(SwUpdateStrategyController, self).__init__()
        self.rpc_client = rpc_client.ManagerClient()

    @expose(generic=True, template='json')
    def index(self):
        # Route the request to specific methods with parameters
        pass

    @index.when(method='GET', template='json')
    def get(self, steps=None, cloud_name=None):
        """Get details about software update strategy.

        :param steps: get the steps for this strategy (optional)
        :param cloud_name: name of cloud (optional)
        """
        context = restcomm.extract_context_from_environ()

        if steps is None:
            # Strategy requested
            strategy = None
            try:
                strategy = db_api.sw_update_strategy_get(context)
            except exceptions.NotFound:
                pecan.abort(404, _('Strategy not found'))

            strategy_dict = db_api.sw_update_strategy_db_model_to_dict(
                strategy)
            return strategy_dict

        elif steps == "steps":
            # Steps for the strategy requested
            if cloud_name is None:
                # List of steps requested
                result = dict()
                result['strategy-steps'] = list()
                strategy_steps = db_api.strategy_step_get_all(context)
                for strategy_step in strategy_steps:
                    result['strategy-steps'].append(
                        db_api.strategy_step_db_model_to_dict(strategy_step))

                return result
            else:
                # Single step requested
                strategy_step = None
                if cloud_name == consts.SYSTEM_CONTROLLER_NAME:
                    # The system controller step does not map to a subcloud,
                    # so has no name.
                    try:
                        strategy_step = db_api.strategy_step_get(context, None)
                    except exceptions.StrategyStepNotFound:
                        pecan.abort(404, _('Strategy step not found'))
                else:
                    try:
                        strategy_step = db_api.strategy_step_get_by_name(
                            context, cloud_name)
                    except exceptions.StrategyStepNameNotFound:
                        pecan.abort(404, _('Strategy step not found'))

                strategy_step_dict = db_api.strategy_step_db_model_to_dict(
                    strategy_step)
                return strategy_step_dict

    @index.when(method='POST', template='json')
    def post(self, actions=None):
        """Create a new software update strategy."""
        context = restcomm.extract_context_from_environ()

        payload = eval(request.body)
        if not payload:
            pecan.abort(400, _('Body required'))

        if actions is None:
            # Validate any options that were supplied
            strategy_type = payload.get('type')
            if not strategy_type:
                pecan.abort(400, _('type required'))
            if strategy_type not in consts.SW_UPDATE_TYPE_PATCH:
                pecan.abort(400, _('type invalid'))

            subcloud_apply_type = payload.get('subcloud-apply-type')
            if subcloud_apply_type is not None:
                if subcloud_apply_type not in [
                        consts.SUBCLOUD_APPLY_TYPE_PARALLEL,
                        consts.SUBCLOUD_APPLY_TYPE_SERIAL]:
                    pecan.abort(400, _('subcloud-apply-type invalid'))

            max_parallel_subclouds_str = payload.get('max-parallel-subclouds')
            if max_parallel_subclouds_str is not None:
                max_parallel_subclouds = None
                try:
                    max_parallel_subclouds = int(max_parallel_subclouds_str)
                except ValueError:
                    pecan.abort(400, _('max-parallel-subclouds invalid'))
                # TODO(Bart): Decide on a maximum
                if max_parallel_subclouds < 1 or max_parallel_subclouds > 100:
                    pecan.abort(400, _('max-parallel-subclouds invalid'))

            stop_on_failure = payload.get('stop-on-failure')
            if stop_on_failure is not None:
                if stop_on_failure not in ["true", "false"]:
                    pecan.abort(400, _('stop-on-failure invalid'))

            try:
                # Ask dcmanager-manager to create the strategy.
                # It will do all the real work...
                return self.rpc_client.create_sw_update_strategy(context,
                                                                 payload)
            except RemoteError as e:
                pecan.abort(422, e.value)
            except Exception as e:
                LOG.exception(e)
                pecan.abort(500, _('Unable to create strategy'))
        elif actions == 'actions':
            # Apply or abort a strategy
            action = payload.get('action')
            if not action:
                pecan.abort(400, _('action required'))
            if action == consts.SW_UPDATE_ACTION_APPLY:
                try:
                    # Ask dcmanager-manager to apply the strategy.
                    # It will do all the real work...
                    return self.rpc_client.apply_sw_update_strategy(context)
                except RemoteError as e:
                    pecan.abort(422, e.value)
                except Exception as e:
                    LOG.exception(e)
                    pecan.abort(500, _('Unable to apply strategy'))
            elif action == consts.SW_UPDATE_ACTION_ABORT:
                try:
                    # Ask dcmanager-manager to abort the strategy.
                    # It will do all the real work...
                    return self.rpc_client.abort_sw_update_strategy(context)
                except RemoteError as e:
                    pecan.abort(422, e.value)
                except Exception as e:
                    LOG.exception(e)
                    pecan.abort(500, _('Unable to abort strategy'))

    @index.when(method='delete', template='json')
    def delete(self):
        """Delete the software update strategy."""
        context = restcomm.extract_context_from_environ()

        try:
            # Ask dcmanager-manager to delete the strategy.
            # It will do all the real work...
            return self.rpc_client.delete_sw_update_strategy(context)
        except RemoteError as e:
            pecan.abort(422, e.value)
        except Exception as e:
            LOG.exception(e)
            pecan.abort(500, _('Unable to delete strategy'))

# Copyright (c) 2017 Ericsson AB.
# 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.
#
# Copyright (c) 2017 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#

from oslo_config import cfg
from oslo_log import log as logging
from oslo_messaging import RemoteError

import pecan
from pecan import expose
from pecan import request

from dcmanager.api.controllers import restcomm
from dcmanager.common import consts
from dcmanager.common import exceptions
from dcmanager.common.i18n import _
from dcmanager.db import api as db_api
from dcmanager.rpc import client as rpc_client

CONF = cfg.CONF
LOG = logging.getLogger(__name__)


class SwUpdateStrategyController(object):

    def __init__(self):
        super(SwUpdateStrategyController, self).__init__()
        self.rpc_client = rpc_client.ManagerClient()

    @expose(generic=True, template='json')
    def index(self):
        # Route the request to specific methods with parameters
        pass

    @index.when(method='GET', template='json')
    def get(self, steps=None, cloud_name=None):
        """Get details about software update strategy.

        :param steps: get the steps for this strategy (optional)
        :param cloud_name: name of cloud (optional)
        """
        context = restcomm.extract_context_from_environ()

        if steps is None:
            # Strategy requested
            strategy = None
            try:
                strategy = db_api.sw_update_strategy_get(context)
            except exceptions.NotFound:
                pecan.abort(404, _('Strategy not found'))

            strategy_dict = db_api.sw_update_strategy_db_model_to_dict(
                strategy)
            return strategy_dict

        elif steps == "steps":
            # Steps for the strategy requested
            if cloud_name is None:
                # List of steps requested
                result = dict()
                result['strategy-steps'] = list()
                strategy_steps = db_api.strategy_step_get_all(context)
                for strategy_step in strategy_steps:
                    result['strategy-steps'].append(
                        db_api.strategy_step_db_model_to_dict(strategy_step))

                return result
            else:
                # Single step requested
                strategy_step = None
                if cloud_name == consts.SYSTEM_CONTROLLER_NAME:
                    # The system controller step does not map to a subcloud,
                    # so has no name.
                    try:
                        strategy_step = db_api.strategy_step_get(context, None)
                    except exceptions.StrategyStepNotFound:
                        pecan.abort(404, _('Strategy step not found'))
                else:
                    try:
                        strategy_step = db_api.strategy_step_get_by_name(
                            context, cloud_name)
                    except exceptions.StrategyStepNameNotFound:
                        pecan.abort(404, _('Strategy step not found'))

                strategy_step_dict = db_api.strategy_step_db_model_to_dict(
                    strategy_step)
                return strategy_step_dict

    @index.when(method='POST', template='json')
    def post(self, actions=None):
        """Create a new software update strategy."""
        context = restcomm.extract_context_from_environ()

        payload = eval(request.body)
        if not payload:
            pecan.abort(400, _('Body required'))

        if actions is None:
            # Validate any options that were supplied
            strategy_type = payload.get('type')
            if not strategy_type:
                pecan.abort(400, _('type required'))
            if strategy_type not in consts.SW_UPDATE_TYPE_PATCH:
                pecan.abort(400, _('type invalid'))

            subcloud_apply_type = payload.get('subcloud-apply-type')
            if subcloud_apply_type is not None:
                if subcloud_apply_type not in [
                        consts.SUBCLOUD_APPLY_TYPE_PARALLEL,
                        consts.SUBCLOUD_APPLY_TYPE_SERIAL]:
                    pecan.abort(400, _('subcloud-apply-type invalid'))

            max_parallel_subclouds_str = payload.get('max-parallel-subclouds')
            if max_parallel_subclouds_str is not None:
                max_parallel_subclouds = None
                try:
                    max_parallel_subclouds = int(max_parallel_subclouds_str)
                except ValueError:
                    pecan.abort(400, _('max-parallel-subclouds invalid'))
                # TODO(Bart): Decide on a maximum
                if max_parallel_subclouds < 1 or max_parallel_subclouds > 100:
                    pecan.abort(400, _('max-parallel-subclouds invalid'))

            stop_on_failure = payload.get('stop-on-failure')
            if stop_on_failure is not None:
                if stop_on_failure not in ["true", "false"]:
                    pecan.abort(400, _('stop-on-failure invalid'))

            try:
                # Ask dcmanager-manager to create the strategy.
                # It will do all the real work...
                return self.rpc_client.create_sw_update_strategy(context,
                                                                 payload)
            except RemoteError as e:
                pecan.abort(422, e.value)
            except Exception as e:
                LOG.exception(e)
                pecan.abort(500, _('Unable to create strategy'))
        elif actions == 'actions':
            # Apply or abort a strategy
            action = payload.get('action')
            if not action:
                pecan.abort(400, _('action required'))
            if action == consts.SW_UPDATE_ACTION_APPLY:
                try:
                    # Ask dcmanager-manager to apply the strategy.
                    # It will do all the real work...
                    return self.rpc_client.apply_sw_update_strategy(context)
                except RemoteError as e:
                    pecan.abort(422, e.value)
                except Exception as e:
                    LOG.exception(e)
                    pecan.abort(500, _('Unable to apply strategy'))
            elif action == consts.SW_UPDATE_ACTION_ABORT:
                try:
                    # Ask dcmanager-manager to abort the strategy.
                    # It will do all the real work...
                    return self.rpc_client.abort_sw_update_strategy(context)
                except RemoteError as e:
                    pecan.abort(422, e.value)
                except Exception as e:
                    LOG.exception(e)
                    pecan.abort(500, _('Unable to abort strategy'))

    @index.when(method='delete', template='json')
    def delete(self):
        """Delete the software update strategy."""
        context = restcomm.extract_context_from_environ()

        try:
            # Ask dcmanager-manager to delete the strategy.
            # It will do all the real work...
            return self.rpc_client.delete_sw_update_strategy(context)
        except RemoteError as e:
            pecan.abort(422, e.value)
        except Exception as e:
            LOG.exception(e)
            pecan.abort(500, _('Unable to delete strategy'))

# Copyright (c) 2017 Ericsson AB. # 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. # # Copyright (c) 2017 Wind River Systems, Inc. # # The right to copy, distribute, modify, or otherwise make use # of this software may be licensed only pursuant to the terms # of an applicable Wind River license agreement. # # Route the request to specific methods with parameters Get details about software update strategy.          :param steps: get the steps for this strategy (optional)         :param cloud_name: name of cloud (optional) # Strategy requested # Steps for the strategy requested # List of steps requested # Single step requested # The system controller step does not map to a subcloud, # so has no name. Create a new software update strategy. # Validate any options that were supplied # TODO(Bart): Decide on a maximum # Ask dcmanager-manager to create the strategy. # It will do all the real work... # Apply or abort a strategy # Ask dcmanager-manager to apply the strategy. # It will do all the real work... # Ask dcmanager-manager to abort the strategy. # It will do all the real work... Delete the software update strategy. # Ask dcmanager-manager to delete the strategy. # It will do all the real work...