# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.

"""
P1 tests for secondary storage domain limits

Test Plan: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Limit+Resources+to+domain+or+accounts

Issue Link: https://issues.apache.org/jira/browse/CLOUDSTACK-1466

Feature Specifications: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Limit+Resources+to+domains+and+accounts
"""
# Import Local Modules
from nose.plugins.attrib import attr
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.base import (Account,
                             Resources,
                             Domain,
                             Template)
from marvin.lib.common import (get_domain,
                               get_zone,
                               get_template,
                               get_builtin_template_info,
                               list_zones,
                               isDomainResourceCountEqualToExpectedCount)
from marvin.lib.utils import (cleanup_resources, validateList)
from marvin.codes import (PASS,
                          FAIL,
                          RESOURCE_SECONDARY_STORAGE)


class TestMultipleChildDomain(cloudstackTestCase):

    @classmethod
    def setUpClass(cls):
        cloudstackTestClient = super(TestMultipleChildDomain,
                                     cls).getClsTestClient()
        cls.api_client = cloudstackTestClient.getApiClient()
        cls.services = cloudstackTestClient.getParsedTestDataConfig()
        cls.domain = get_domain(cls.api_client)
        cls.zone = get_zone(cls.api_client, cloudstackTestClient.getZoneForTests())
        cls.services["mode"] = cls.zone.networktype

        cls.template = get_template(
                            cls.api_client,
                            cls.zone.id,
                            cls.services["ostype"]
                            )

        cls._cleanup = []
        return

    @classmethod
    def tearDownClass(cls):
        super(TestMultipleChildDomain,cls).tearDownClass()

    def setUp(self):
        self.apiclient = self.testClient.getApiClient()
        self.dbclient = self.testClient.getDbConnection()
        self.cleanup = []
        return

    def tearDown(self):
        super(TestMultipleChildDomain,self).tearDown()

    def updateDomainResourceLimits(self, parentdomainlimit, subdomainlimit):
        """Update secondary storage limits of the parent domain and its
        child domains"""

        try:
            Resources.updateLimit(self.apiclient, resourcetype=11,
                                  max=parentdomainlimit,
                                  domainid=self.parent_domain.id)

            Resources.updateLimit(self.apiclient, resourcetype=11,
                                  max=subdomainlimit,
                                  domainid=self.cadmin_1.domainid)

            Resources.updateLimit(self.apiclient, resourcetype=11,
                                  max=subdomainlimit,
                                  domainid=self.cadmin_2.domainid)
        except Exception as e:
            return [FAIL, e]
        return [PASS, None]

    def setupAccounts(self):
        try:
            self.parent_domain = Domain.create(self.apiclient,
                                               services=self.services["domain"],
                                               parentdomainid=self.domain.id)
            self.cleanup.append(self.parent_domain)
            self.parentd_admin = Account.create(self.apiclient, self.services["account"],
                                                admin=True, domainid=self.parent_domain.id)
            self.cleanup.append(self.parentd_admin)

            # Create sub-domains and their admin accounts
            self.cdomain_1 = Domain.create(self.apiclient,
                                           services=self.services["domain"],
                                           parentdomainid=self.parent_domain.id)
            self.cleanup.append(self.cdomain_1)
            self.cdomain_2 = Domain.create(self.apiclient,
                                           services=self.services["domain"],
                                           parentdomainid=self.parent_domain.id)
            self.cleanup.append(self.cdomain_2)

            self.cadmin_1 = Account.create(self.apiclient, self.services["account"],
                                           admin=True, domainid=self.cdomain_1.id)
            self.cleanup.append(self.cadmin_1)

            self.cadmin_2 = Account.create(self.apiclient, self.services["account"],
                                           admin=True, domainid=self.cdomain_2.id)
            self.cleanup.append(self.cadmin_2)

            users = {
                 self.cdomain_1: self.cadmin_1,
                 self.cdomain_2: self.cadmin_2
                 }
        except Exception as e:
            return [FAIL, e, None]
        return [PASS, None, users]

    @attr(tags=["advanced"], required_hardware="true")
    def test_01_multiple_domains_secondary_storage_limits(self):
        """Test secondary storage limit of domain and its sub-domains

        # Steps
        1. Create a parent domain and two sub-domains in it (also admin accounts
           of each domain)
        2. Register template in child domain 1 so that total secondary storage
           is less than the limit of child domain
        3. Set the child domain limit equal to template size and parent domain
           domain limit as double of the template size
        4. Repeat step 2 for child domain 2
        5. Try to register template in parent domain now so that the total secondary storage in
           parent domain (including that in sub-domains is more than the secondary
           storage limit of the parent domain)
        6. Delete the admin account of child domain 1 and check resource count
           of the parent domain
        7. Delete template in account 2 and check secondary storage count
           of parent domain

        # Validations:
        1. Step 2 and 4 should succeed
        2. Step 5 should fail as the resource limit exceeds in parent domain
        3. After step 6, resource count in parent domain should decrease by equivalent
           quantity
        4. After step 7, resource count in parent domain should be 0"""

        # Setting up account and domain hierarchy
        result = self.setupAccounts()
        self.assertEqual(result[0], PASS, result[1])

        try:
            builtin_info = get_builtin_template_info(self.apiclient, self.zone.id)
            self.services["template_2"]["url"] = builtin_info[0]
            self.services["template_2"]["hypervisor"] = builtin_info[1]
            self.services["template_2"]["format"] = builtin_info[2]
            self.services["template_2"]["ispublic"] = False

            templateChildAccount1 = Template.register(self.apiclient,
                                                      self.services["template_2"],
                                                      zoneid=self.zone.id,
                                                      account=self.cadmin_1.name,
                                                      domainid=self.cadmin_1.domainid)

            templateChildAccount1.download(self.apiclient)

            templates = Template.list(self.apiclient,
                                      templatefilter=self.services["template_2"]["templatefilter"],
                                      id=templateChildAccount1.id)
            if validateList(templates)[0] == FAIL:
                raise Exception("templates list validation failed")

            self.templateSize = int(int(templates[0].size) / (1024**3))
        except Exception as e:
            self.fail("Failed with exception as ee: %s" % e)

        subdomainlimit = (self.templateSize)

        result = self.updateDomainResourceLimits(((subdomainlimit*2)), subdomainlimit)
        self.assertEqual(result[0], PASS, result[1])

        # Checking Primary Storage count of Parent domain admin before deleting child domain user account
        result = isDomainResourceCountEqualToExpectedCount(
                        self.apiclient, self.cadmin_1.domainid,
                        self.templateSize, RESOURCE_SECONDARY_STORAGE)
        self.assertFalse(result[0], result[1])
        self.assertTrue(result[2], "Resource count does not match")

        try:
            templateChildAccount2 = Template.register(self.apiclient,
                                                      self.services["template_2"],
                                                      zoneid=self.zone.id,
                                                      account=self.cadmin_2.name,
                                                      domainid=self.cadmin_2.domainid)

            templateChildAccount2.download(self.apiclient)
        except Exception as e:
            self.fail("Failed while registering/downloading template: %s" % e)

        result = isDomainResourceCountEqualToExpectedCount(
                        self.apiclient, self.cadmin_2.domainid,
                        self.templateSize, RESOURCE_SECONDARY_STORAGE)
        self.assertFalse(result[0], result[1])
        self.assertTrue(result[2], "Resource count does not match")

        with self.assertRaises(Exception):
            Template.register(self.apiclient,
                              self.services["template_2"],
                              zoneid=self.zone.id,
                              account=self.parentd_admin.name,
                              domainid=self.parentd_admin.domainid)

        self.cadmin_1.delete(self.apiclient)
        self.cleanup.remove(self.cadmin_1)

        result = isDomainResourceCountEqualToExpectedCount(
                        self.apiclient, self.parent_domain.id,
                        self.templateSize, RESOURCE_SECONDARY_STORAGE)
        self.assertFalse(result[0], result[1])
        self.assertTrue(result[2], "Resource count does not match")

        try:
            templateChildAccount2.delete(self.apiclient)
        except Exception as e:
            self.fail("Failed to delete template: %s" % e)

        result = isDomainResourceCountEqualToExpectedCount(
                        self.apiclient, self.parent_domain.id,
                        0, RESOURCE_SECONDARY_STORAGE)
        self.assertFalse(result[0], result[1])
        self.assertTrue(result[2], "Resource count does not match")
        return

    @attr(tags=["advanced"], required_hardware="true")
    def test_02_multiple_domains_secondary_storage_counts(self):
        """Test secondary storage counts in multiple child domains
        # Steps
        1. Create a parent domain and two sub-domains in it (also admin accounts
           of each domain)
        Repeat following steps for both the child domains
        2. Register template in child domain
        3. Check if the resource count for domain is updated correctly
        4. Delete the template
        5. Verify that the resource count for the domain is 0

        """
        users = None
        # Setting up account and domain hierarchy
        result = self.setupAccounts()
        self.assertEqual(result[0], PASS, result[1])
        users = result[2]

        for domain, admin in list(users.items()):
            self.account = admin
            self.domain = domain

            try:
                builtin_info = get_builtin_template_info(self.apiclient, self.zone.id)
                self.services["template_2"]["url"] = builtin_info[0]
                self.services["template_2"]["hypervisor"] = builtin_info[1]
                self.services["template_2"]["format"] = builtin_info[2]
                self.services["template_2"]["ispublic"] = False

                template = Template.register(self.apiclient,
                                             self.services["template_2"],
                                             zoneid=self.zone.id,
                                             account=self.account.name,
                                             domainid=self.account.domainid)

                template.download(self.apiclient)

                templates = Template.list(self.apiclient,
                                          templatefilter=self.services["template_2"]["templatefilter"],
                                          id=template.id)
                if validateList(templates)[0] == FAIL:
                    raise Exception("templates list validation failed")

                templateSize = int(int(templates[0].size) / (1024**3))
                expectedCount = templateSize
                result = isDomainResourceCountEqualToExpectedCount(
                                    self.apiclient, self.domain.id,
                                    expectedCount, RESOURCE_SECONDARY_STORAGE)
                self.assertFalse(result[0], result[1])
                self.assertTrue(result[2], "Resource count does not match")

                template.delete(self.apiclient)

                expectedCount = 0
                result = isDomainResourceCountEqualToExpectedCount(
                                    self.apiclient, self.domain.id,
                                    expectedCount, RESOURCE_SECONDARY_STORAGE)
                self.assertFalse(result[0], result[1])
                self.assertTrue(result[2], "Resource count does not match")
            except Exception as e:
                self.fail("Failed to get zone list: %s" % e)
        return

    @attr(tags=["advanced"], required_hardware="true")
    def test_03_copy_template(self):
        """Test secondary storage counts in multiple child domains
        # Steps
        1. Create a parent domain and two sub-domains in it (also admin accounts
           of each domain)
        Repeat following steps for both the child domains
        2. Register template in child domain
        3. Check if the resource count for domain is updated correctly
        4. Copy template to other zone
        5. Verify that secondary storage count for the domain is doubled
           as there are two templates now
        """

        zones = list_zones(self.apiclient)
        self.assertEqual(validateList(zones)[0], PASS, "zones list validation faield")

        if len(zones) < 2:
            self.skipTest("At least 2 zones should be present for this test case")

        users = None
        # Setting up account and domain hierarchy
        result = self.setupAccounts()
        self.assertEqual(result[0], PASS, result[1])
        users = result[2]

        for domain, admin in list(users.items()):
            self.account = admin
            self.domain = domain

            try:
                builtin_info = get_builtin_template_info(self.apiclient, self.zone.id)
                self.services["template_2"]["url"] = builtin_info[0]
                self.services["template_2"]["hypervisor"] = builtin_info[1]
                self.services["template_2"]["format"] = builtin_info[2]
                self.services["template_2"]["ispublic"] = False

                template = Template.register(self.apiclient,
                                             self.services["template_2"],
                                             zoneid=self.zone.id,
                                             account=self.account.name,
                                             domainid=self.account.domainid)

                template.download(self.apiclient)

                templates = Template.list(self.apiclient,
                                          templatefilter=self.services["template_2"]["templatefilter"],
                                          id=template.id)
                if validateList(templates)[0] == FAIL:
                    raise Exception("templates list validation failed")

                templateSize = int(int(templates[0].size) / (1024**3))
                expectedCount = templateSize
                result = isDomainResourceCountEqualToExpectedCount(
                                    self.apiclient, self.domain.id,
                                    expectedCount, RESOURCE_SECONDARY_STORAGE)
                self.assertFalse(result[0], result[1])
                self.assertTrue(result[2], "Resource count does not match")

                templateDestinationZoneId = None
                for zone in zones:
                    if template.zoneid != zone.id:
                        templateDestinationZoneId = zone.id
                        break

                template.copy(self.apiclient, destzoneid=templateDestinationZoneId,
                              sourcezoneid=template.zoneid)

                expectedCount *= 2
                result = isDomainResourceCountEqualToExpectedCount(
                                    self.apiclient, self.domain.id,
                                    expectedCount, RESOURCE_SECONDARY_STORAGE)
                self.assertFalse(result[0], result[1])
                self.assertTrue(result[2], "Resource count does not match")
            except Exception as e:
                self.fail("Failed to copy template cross zones: %s" % e)
        return


class TestDeleteAccount(cloudstackTestCase):

    @classmethod
    def setUpClass(cls):
        cloudstackTestClient = super(TestDeleteAccount,
                                     cls).getClsTestClient()
        cls.api_client = cloudstackTestClient.getApiClient()
        # Fill services from the external config file
        cls.services = cloudstackTestClient.getParsedTestDataConfig()
        # Get Zone, Domain and templates
        cls.domain = get_domain(cls.api_client)
        cls.zone = get_zone(cls.api_client)
        cls.services["mode"] = cls.zone.networktype
        cls.template = get_template(
                            cls.api_client,
                            cls.zone.id,
                            cls.services["ostype"]
                            )
        cls._cleanup = []
        return

    @classmethod
    def tearDownClass(cls):
        super(TestDeleteAccount,cls).tearDownClass()

    def setUp(self):
        self.apiclient = self.testClient.getApiClient()
        self.dbclient = self.testClient.getDbConnection()
        self.cleanup = []
        return

    def tearDown(self):
        super(TestDeleteAccount,self).tearDown()

    def setupAccounts(self):
        try:
            self.parent_domain = Domain.create(self.apiclient,
                                               services=self.services["domain"],
                                               parentdomainid=self.domain.id)
            self.cleanup.append(self.parent_domain)
            self.parentd_admin = Account.create(self.apiclient, self.services["account"],
                                                admin=True, domainid=self.parent_domain.id)
            self.cleanup.append(self.parentd_admin)

            self.cdomain_1 = Domain.create(self.apiclient,
                                           services=self.services["domain"],
                                           parentdomainid=self.parent_domain.id)
            self.cleanup.append(self.cdomain_1)
            self.cdomain_2 = Domain.create(self.apiclient,
                                           services=self.services["domain"],
                                           parentdomainid=self.parent_domain.id)
            self.cleanup.append(self.cdomain_2)

            self.cadmin_1 = Account.create(self.apiclient, self.services["account"],
                                           admin=True, domainid=self.cdomain_1.id)
            self.cleanup.append(self.cadmin_1)

            self.cadmin_2 = Account.create(self.apiclient, self.services["account"],
                                           admin=True, domainid=self.cdomain_2.id)
            self.cleanup.append(self.cadmin_2)

            users = {
                 self.cdomain_1: self.cadmin_1,
                 self.cdomain_2: self.cadmin_2
                 }
        except Exception as e:
            return [FAIL, e, None]
        return [PASS, None, users]

    @attr(tags=["advanced"], required_hardware="true")
    def test_04_create_template_delete_account(self):
        """Test secondary storage limit of domain and its sub-domains

        # Steps
        1. Create a parent domain and two sub-domains in it (also admin accounts
           of each domain)
        2. Register template in child domain 1
        3. Verify that the secondary storage count for child domain 1 equals
           the template size
        4. Register template in child domain 2
        5. Vreify that the seconday storage count for child domain 2 equals
           the template size
        6. Verify that the secondary storage count for parent domain equals
           double of template size
        7. Delete child domain 1 admin account
        8. Verify that secondary storage count for parent domain now equals
           to only 1 template size
        """

        # Setting up account and domain hierarchy
        result = self.setupAccounts()
        self.assertEqual(result[0], PASS, result[1])

        try:
            builtin_info = get_builtin_template_info(self.apiclient, self.zone.id)
            self.services["template_2"]["url"] = builtin_info[0]
            self.services["template_2"]["hypervisor"] = builtin_info[1]
            self.services["template_2"]["format"] = builtin_info[2]
            self.services["template_2"]["ispublic"] = False

            template = Template.register(self.apiclient,
                                         self.services["template_2"],
                                         zoneid=self.zone.id,
                                         account=self.cadmin_1.name,
                                         domainid=self.cadmin_1.domainid)

            template.download(self.apiclient)

            templates = Template.list(self.apiclient,
                                      templatefilter=self.services["template_2"]["templatefilter"],
                                      id=template.id)
            if validateList(templates)[0] == FAIL:
                raise Exception("templates list validation failed")

            self.templateSize = int(int(templates[0].size) / (1024**3))
        except Exception as e:
            self.fail("Failed with exception as ee: %s" % e)

        # Checking Primary Storage count of Parent domain admin before deleting child domain user account
        expectedCount = self.templateSize
        result = isDomainResourceCountEqualToExpectedCount(
                                    self.apiclient, self.cadmin_1.domainid,
                                    expectedCount, RESOURCE_SECONDARY_STORAGE)
        self.assertFalse(result[0], result[1])
        self.assertTrue(result[2], "Resource count does not match")

        try:
            template = Template.register(self.apiclient,
                                         self.services["template_2"],
                                         zoneid=self.zone.id,
                                         account=self.cadmin_2.name,
                                         domainid=self.cadmin_2.domainid)

            template.download(self.apiclient)
        except Exception as e:
            self.fail("Failed while registering/downloading template: %s" % e)

        result = isDomainResourceCountEqualToExpectedCount(
                                    self.apiclient, self.cadmin_2.domainid,
                                    expectedCount, RESOURCE_SECONDARY_STORAGE)
        self.assertFalse(result[0], result[1])
        self.assertTrue(result[2], "Resource count does not match")

        self.templateSize = int((int(templates[0].size)*2) / (1024**3))

        expectedCount = self.templateSize
        result = isDomainResourceCountEqualToExpectedCount(
                                    self.apiclient, self.parent_domain.id,
                                    expectedCount, RESOURCE_SECONDARY_STORAGE)
        self.assertFalse(result[0], result[1])
        self.assertTrue(result[2], "Resource count does not match")

        try:
            self.cadmin_1.delete(self.apiclient)
            self.cleanup.remove(self.cadmin_1)
        except Exception as e:
            self.fail("Failed to delete account: %s" % e)

        expectedCount /= 2
        result = isDomainResourceCountEqualToExpectedCount(
                                    self.apiclient, self.parent_domain.id,
                                    expectedCount, RESOURCE_SECONDARY_STORAGE)
        self.assertFalse(result[0], result[1])
        self.assertTrue(result[2], "Resource count does not match")
        return
