########
# Copyright (c) 2013 GigaSpaces Technologies Ltd. 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 testenv import TestCase
from testenv.utils import get_resource as resource
from testenv.utils import deploy_application as deploy
from testenv.utils import execute_workflow


class TestScaleWorkflow(TestCase):

    def test_compute_scale_out_compute(self):
        expectations = self.deploy('scale1')
        expectations['compute']['new']['install'] = 1
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute'})
        expectations['compute']['new']['install'] = 1
        expectations['compute']['existing']['install'] = 1
        self.deployment_assertions(expectations)

    def test_compute_scale_in_compute(self):
        expectations = self.deploy('scale4')
        expectations['compute']['new']['install'] = 3
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute',
                                              'delta': -1})
        expectations['compute']['existing']['install'] = 2
        expectations['compute']['removed']['install'] = 1
        expectations['compute']['removed']['uninstall'] = 1
        self.deployment_assertions(expectations)

    def test_compute_scale_out_and_in_compute_from_0(self):
        expectations = self.deploy('scale10')
        expectations['compute']['new']['install'] = 0
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute'})
        expectations['compute']['new']['install'] = 1
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute',
                                              'delta': -1})
        expectations['compute']['new']['install'] = 0
        expectations['compute']['existing']['install'] = 0
        expectations['compute']['removed']['install'] = 1
        expectations['compute']['removed']['uninstall'] = 1
        self.deployment_assertions(expectations)

    def test_compute_scale_out_2_compute(self):
        expectations = self.deploy('scale1')
        expectations['compute']['new']['install'] = 1
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute',
                                              'delta': 2})
        expectations['compute']['new']['install'] = 2
        expectations['compute']['existing']['install'] = 1
        self.deployment_assertions(expectations)

    def test_compute_scale_in_2_compute(self):
        expectations = self.deploy('scale4')
        expectations['compute']['new']['install'] = 3
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute',
                                              'delta': -2})
        expectations['compute']['existing']['install'] = 1
        expectations['compute']['removed']['install'] = 2
        expectations['compute']['removed']['uninstall'] = 2
        self.deployment_assertions(expectations)

    def test_db_contained_in_compute_scale_out_compute(self):
        expectations = self.deploy('scale2')
        expectations['compute']['new']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute'})
        expectations['compute']['new']['install'] = 1
        expectations['compute']['existing']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        expectations['db']['existing']['install'] = 1
        expectations['db']['existing']['rel_install'] = 2
        self.deployment_assertions(expectations)

    def test_db_contained_in_compute_scale_in_compute(self):
        expectations = self.deploy('scale5')
        expectations['compute']['new']['install'] = 2
        expectations['db']['new']['install'] = 4
        expectations['db']['new']['rel_install'] = 8
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute',
                                              'delta': -1})
        expectations['compute']['existing']['install'] = 1
        expectations['compute']['removed']['install'] = 1
        expectations['compute']['removed']['uninstall'] = 1
        expectations['db']['existing']['install'] = 2
        expectations['db']['existing']['rel_install'] = 4
        expectations['db']['removed']['install'] = 2
        expectations['db']['removed']['uninstall'] = 2
        expectations['db']['removed']['rel_install'] = 4
        expectations['db']['removed']['rel_uninstall'] = 4
        self.deployment_assertions(expectations)

    def test_db_contained_in_compute_scale_out_db(self):
        expectations = self.deploy('scale2')
        expectations['compute']['new']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'db'})
        expectations['compute']['new']['install'] = 1
        expectations['compute']['existing']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        expectations['db']['existing']['install'] = 1
        expectations['db']['existing']['rel_install'] = 2
        self.deployment_assertions(expectations)

    def test_db_contained_in_compute_scale_in_db(self):
        expectations = self.deploy('scale5')
        expectations['compute']['new']['install'] = 2
        expectations['db']['new']['install'] = 4
        expectations['db']['new']['rel_install'] = 8
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'db',
                                              'delta': -1})
        expectations['compute']['existing']['install'] = 1
        expectations['compute']['removed']['install'] = 1
        expectations['compute']['removed']['uninstall'] = 1
        expectations['db']['existing']['install'] = 2
        expectations['db']['existing']['rel_install'] = 4
        expectations['db']['removed']['install'] = 2
        expectations['db']['removed']['uninstall'] = 2
        expectations['db']['removed']['rel_install'] = 4
        expectations['db']['removed']['rel_uninstall'] = 4
        self.deployment_assertions(expectations)

    def test_db_contained_in_compute_scale_out_db_scale_db(self):
        expectations = self.deploy('scale2')
        expectations['compute']['new']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={
            'node_id': 'db', 'scale_compute': False})
        expectations['compute']['existing']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        expectations['db']['existing']['install'] = 1
        expectations['db']['existing']['rel_install'] = 2
        self.deployment_assertions(expectations)

    def test_db_contained_in_compute_scale_in_db_scale_db(self):
        expectations = self.deploy('scale5')
        expectations['compute']['new']['install'] = 2
        expectations['db']['new']['install'] = 4
        expectations['db']['new']['rel_install'] = 8
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'db',
                                              'delta': -1,
                                              'scale_compute': False})
        expectations['compute']['existing']['install'] = 2
        expectations['db']['existing']['install'] = 2
        expectations['db']['existing']['rel_install'] = 4
        expectations['db']['removed']['install'] = 2
        expectations['db']['removed']['uninstall'] = 2
        expectations['db']['removed']['rel_install'] = 4
        expectations['db']['removed']['rel_uninstall'] = 4
        self.deployment_assertions(expectations)

    def test_db_connected_to_compute_scale_out_compute(self):
        expectations = self.deploy('scale3')
        expectations['compute']['new']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute'})
        expectations['compute']['new']['install'] = 1
        expectations['compute']['existing']['install'] = 1
        expectations['db']['existing']['install'] = 1
        expectations['db']['existing']['rel_install'] = 2
        expectations['db']['existing']['scale_rel_install'] = 2
        self.deployment_assertions(expectations)

    def test_db_connected_to_compute_scale_in_compute(self):
        expectations = self.deploy('scale6')
        expectations['compute']['new']['install'] = 2
        expectations['db']['new']['install'] = 2
        expectations['db']['new']['rel_install'] = 8
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute',
                                              'delta': -1})
        expectations['compute']['existing']['install'] = 1
        expectations['compute']['removed']['install'] = 1
        expectations['compute']['removed']['uninstall'] = 1
        expectations['db']['existing']['install'] = 2
        expectations['db']['existing']['rel_install'] = 8
        expectations['db']['existing']['rel_uninstall'] = 4
        self.deployment_assertions(expectations)

    def test_db_connected_to_compute_scale_in_and_out_compute_from_0(self):
        expectations = self.deploy('scale11')
        expectations['compute']['new']['install'] = 0
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 0
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute',
                                              'delta': 1})
        expectations['compute']['new']['install'] = 1
        expectations['compute']['existing']['install'] = 0
        expectations['db']['existing']['install'] = 1
        expectations['db']['existing']['rel_install'] = 0
        expectations['db']['existing']['scale_rel_install'] = 2
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'compute',
                                              'delta': -1})
        expectations['compute']['new']['install'] = 0
        expectations['compute']['existing']['install'] = 0
        expectations['compute']['removed']['install'] = 1
        expectations['compute']['removed']['uninstall'] = 1
        expectations['db']['existing']['install'] = 1
        expectations['db']['existing']['scale_rel_install'] = 2
        expectations['db']['existing']['rel_uninstall'] = 2
        self.deployment_assertions(expectations)

    def test_db_connected_to_compute_scale_out_db(self):
        expectations = self.deploy('scale3')
        expectations['compute']['new']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'db'})
        expectations['compute']['existing']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        expectations['db']['existing']['install'] = 1
        expectations['db']['existing']['rel_install'] = 2
        self.deployment_assertions(expectations)

    def test_db_connected_to_compute_scale_in_db(self):
        expectations = self.deploy('scale6')
        expectations['compute']['new']['install'] = 2
        expectations['db']['new']['install'] = 2
        expectations['db']['new']['rel_install'] = 8
        self.deployment_assertions(expectations)

        expectations = self.scale(parameters={'node_id': 'db',
                                              'delta': -1})
        expectations['compute']['existing']['install'] = 2
        expectations['db']['existing']['install'] = 1
        expectations['db']['existing']['rel_install'] = 4
        expectations['db']['removed']['install'] = 1
        expectations['db']['removed']['uninstall'] = 1
        expectations['db']['removed']['rel_install'] = 4
        expectations['db']['removed']['rel_uninstall'] = 4
        self.deployment_assertions(expectations)

    def test_compute_scale_out_compute_rollback(self):
        fail_operations = [{
            'workflow': 'scale',
            'node': 'compute',
            'operation': 'cloudify.interfaces.lifecycle.start'
        }]

        expectations = self.deploy('scale7', inputs={'fail': fail_operations})
        expectations['compute']['new']['install'] = 1
        self.deployment_assertions(expectations)

        with self.assertRaises(RuntimeError) as e:
            self.scale(parameters={'node_id': 'compute'})
        self.assertIn('TEST_EXPECTED_FAIL', str(e.exception))
        expectations = self.expectations()
        expectations['compute']['new']['install'] = 1
        expectations['compute']['new']['uninstall'] = 1
        expectations['compute']['existing']['install'] = 1
        self.deployment_assertions(expectations, rollback=True)

    def test_db_contained_in_compute_scale_out_compute_rollback(self):
        fail_operations = [{
            'workflow': 'scale',
            'node': 'db',
            'operation': 'cloudify.interfaces.lifecycle.start'
        }]

        expectations = self.deploy('scale8', inputs={'fail': fail_operations})
        expectations['compute']['new']['install'] = 1
        expectations['db']['new']['install'] = 1
        expectations['db']['new']['rel_install'] = 2
        self.deployment_assertions(expectations)