# coding=utf-8
# vim: tabstop=4 shiftwidth=4 softtabstop=4

#    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.

"""
Unittest for the Compute Backend.
"""

#pylint: disable=W0102,C0103,R0904

import mox
import unittest

# dependency from nova :-)

from nova.compute import vm_states

from occi import core_model
from occi.extensions import infrastructure

from occi_os_api import nova_glue
from occi_os_api.backends import compute
from occi_os_api.extensions import os_mixins


class TestComputeBackend(unittest.TestCase):
    """
    Tests the compute backend.
    """

    os_template = os_mixins.OsTemplate('http://example.com', 'unix')
    os_template2 = os_mixins.OsTemplate('http://example.com', 'windows')

    res_template = os_mixins.ResourceTemplate('http://example.com', 'itsy')
    res_template2 = os_mixins.ResourceTemplate('http://example.com', 'bitsy')

    def setUp(self):
        """
        Setup tests.
        """
        self.backend = compute.ComputeBackend()
        self.sec_obj = {'nova_ctx': None}
        self.mox = mox.Mox()

    def tearDown(self):
        """
        Cleanup mocks.
        """
        self.mox.UnsetStubs()

    # Test for failure

    def test_create_for_failure(self):
        """
        Test for proper error handling.
        """
        # msg OS template
        res = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])

        self.assertRaises(AttributeError, self.backend.create, res,
                          self.sec_obj)

        # provide immutable attr
        res = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
                                  [self.os_template])
        res.attributes = {'occi.compute.cores': 2}

        self.assertRaises(AttributeError, self.backend.create, res,
                          self.sec_obj)

    def test_update_for_failure(self):
        """
        Test if correct errors are thrown.
        """
        # msg mixin
        res1 = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])
        res1.attributes = {'occi.core.id': 'bar'}
        res2 = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])

        self.assertRaises(AttributeError, self.backend.update, res1, res2,
                          self.sec_obj)

        res2 = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
                                   [core_model.Category('http://foo.com',
                                                        'bar', '', '', '')])

        self.assertRaises(AttributeError, self.backend.update, res1, res2,
                          self.sec_obj)

    def test_action_for_failure(self):
        """
        Test if correct errors are thrown.
        """
        # wrong action
        res1 = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])
        res1.attributes = {'occi.core.id': 'bar'}
        self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
        nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
            {
                'vm_state': vm_states.STOPPED
            })
        self.mox.ReplayAll()
        self.assertRaises(AttributeError, self.backend.action, res1,
                          infrastructure.STOP, {}, self.sec_obj)
        self.mox.VerifyAll()

        # missing method!
        self.mox.UnsetStubs()
        self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
        nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
            {
                'vm_state': vm_states.ACTIVE
            })
        self.mox.ReplayAll()
        self.assertRaises(AttributeError, self.backend.action, res1,
                          infrastructure.RESTART, {}, self.sec_obj)
        self.mox.VerifyAll()

    # Test for Sanity

    def test_create_for_sanity(self):
        """
        Simulate a create call!
        """
        res = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
                                  [self.os_template])

        self.mox.StubOutWithMock(nova_glue.vm, 'create_vm')
        nova_glue.vm.create_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
            {
                'uuid': 'foo',
                'hostname': 'Server foo',
                'vcpus': 1,
                'memory_mb': 256
            })
#        self.mox.StubOutWithMock(nova_glue.storage, 'get_image_architecture')
#        nova_glue.storage.get_image_architecture(mox.IsA(object),
#                                                 mox.IsA(object)).\
#            AndReturn('foo')

        self.mox.ReplayAll()

        self.backend.create(res, self.sec_obj)

        # check if all attrs are there!
        self.assertIn('occi.compute.hostname', res.attributes)
        self.assertIn('occi.compute.architecture', res.attributes)
        self.assertIn('occi.compute.cores', res.attributes)
        self.assertIn('occi.compute.speed', res.attributes)
        self.assertIn('occi.compute.memory', res.attributes)
        self.assertIn('occi.compute.state', res.attributes)

        self.assertEqual('inactive', res.attributes['occi.compute.state'])

        self.assertListEqual([infrastructure.STOP, infrastructure.SUSPEND,
                              infrastructure.RESTART], res.actions)

        self.mox.VerifyAll()

    def test_retrieve_for_sanity(self):
        """
        Simulate a retrieve call!
        """
        res = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
                                  [self.os_template])
        res.attributes = {'occi.core.id': 'bar'}

        self.mox.StubOutWithMock(nova_glue.vm, 'get_vm_state')
        nova_glue.vm.get_vm_state(mox.IsA(object),
                                  mox.IsA(object)).\
            AndReturn(('active', [infrastructure.STOP,
                                  infrastructure.SUSPEND,
                                  infrastructure.RESTART]))
        self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
        nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
            {
                'hostname': 'bar',
                'vcpus': 1,
                'memory_mb': 256
            })
#        self.mox.StubOutWithMock(nova_glue.storage, 'get_image_architecture')
#        nova_glue.storage.get_image_architecture(mox.IsA(object),
#                                                 mox.IsA(object)).\
#            AndReturn('foo')
        self.mox.ReplayAll()

        self.backend.retrieve(res, self.sec_obj)

        # check if all attrs are there!
        self.assertIn('occi.compute.hostname', res.attributes)
        self.assertIn('occi.compute.architecture', res.attributes)
        self.assertIn('occi.compute.cores', res.attributes)
        self.assertIn('occi.compute.speed', res.attributes)
        self.assertIn('occi.compute.memory', res.attributes)
        self.assertIn('occi.compute.state', res.attributes)

        self.assertIn('occi.core.id', res.attributes)

        self.assertEqual('active', res.attributes['occi.compute.state'])

        self.assertListEqual([infrastructure.STOP, infrastructure.SUSPEND,
                              infrastructure.RESTART], res.actions)

        self.mox.VerifyAll()

    def test_update_for_sanity(self):
        """
        Simulate a update call!
        """
        res1 = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
                                   [self.os_template])
        res1.attributes = {'occi.core.id': 'bar'}

        # case 1 - rebuild VM with different OS
        res2 = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
                                   [self.os_template2])

        self.mox.StubOutWithMock(nova_glue.vm, 'rebuild_vm')
        nova_glue.vm.rebuild_vm(mox.IsA(object), mox.IsA(object),
                                mox.IsA(object))
        self.mox.ReplayAll()
        self.backend.update(res1, res2, self.sec_obj)

        self.assertIn(self.os_template2, res1.mixins)

        self.mox.VerifyAll()

        # case 2 - resize the VM
        res2 = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
                                   [self.res_template2])

        self.mox.StubOutWithMock(nova_glue.vm, 'resize_vm')
        nova_glue.vm.resize_vm(mox.IsA(object), mox.IsA(object),
                               mox.IsA(object))
        self.mox.ReplayAll()
        self.backend.update(res1, res2, self.sec_obj)

        self.assertIn(self.res_template2, res1.mixins)

        self.mox.VerifyAll()

    def test_replace_for_sanity(self):
        """
        Simulate a replace call - does nothing atm.
        """
        self.backend.replace(None, None, self.sec_obj)

    def test_delete_for_sanity(self):
        """
        Simulate a delete call.
        """
        res = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
                                  [self.os_template])
        res.attributes = {'occi.core.id': 'bar'}

        self.mox.StubOutWithMock(nova_glue.vm, 'delete_vm')
        nova_glue.vm.delete_vm(mox.IsA(object), mox.IsA(object))
        self.mox.ReplayAll()
        self.backend.delete(res, self.sec_obj)

        self.mox.VerifyAll()

    def test_action_for_sanity(self):
        """
        Test actions
        """
        res1 = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])
        res1.attributes = {'occi.core.id': 'bar'}

        # start
        self.mox.StubOutWithMock(nova_glue.vm, 'start_vm')
        nova_glue.vm.start_vm(mox.IsA(object), mox.IsA(object))
        self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
        nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
            {
                'vm_state': vm_states.STOPPED
            })
        self.mox.ReplayAll()
        self.backend.action(res1, infrastructure.START, {}, self.sec_obj)
        self.mox.VerifyAll()

        # stop
        self.mox.UnsetStubs()
        self.mox.StubOutWithMock(nova_glue.vm, 'stop_vm')
        nova_glue.vm.stop_vm(mox.IsA(object), mox.IsA(object))
        self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
        nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
            {
                'vm_state': vm_states.ACTIVE
            })
        self.mox.ReplayAll()
        self.backend.action(res1, infrastructure.STOP, {}, self.sec_obj)
        self.mox.VerifyAll()

        # reboot
        self.mox.UnsetStubs()
        self.mox.StubOutWithMock(nova_glue.vm, 'restart_vm')
        nova_glue.vm.restart_vm(mox.IsA(object), mox.IsA(str),
                                mox.IsA(object))
        self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
        nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
            {
                'vm_state': vm_states.ACTIVE
            })
        self.mox.ReplayAll()
        self.backend.action(res1, infrastructure.RESTART,
                            {'method': 'graceful'}, self.sec_obj)
        self.mox.VerifyAll()

        # suspend
        self.mox.UnsetStubs()
        self.mox.StubOutWithMock(nova_glue.vm, 'suspend_vm')
        nova_glue.vm.suspend_vm(mox.IsA(object), mox.IsA(object))
        self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
        nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
            {
                'vm_state': vm_states.ACTIVE
            })
        self.mox.ReplayAll()
        self.backend.action(res1, infrastructure.SUSPEND, {}, self.sec_obj)
        self.mox.VerifyAll()
