diff --git a/test/integration/smoke/test_backup_recovery.py b/test/integration/smoke/test_backup_recovery.py index cdc77c9fe56..f116693acf3 100644 --- a/test/integration/smoke/test_backup_recovery.py +++ b/test/integration/smoke/test_backup_recovery.py @@ -44,8 +44,7 @@ from marvin.lib.common import (get_domain, wait_for_cleanup) from nose.plugins.attrib import attr import logging -from marvin.cloudstackException import CloudstackAPIException -from marvin.codes import PASS +from marvin.codes import PASS, FAILED class TestBackupAndRecovery(cloudstackTestCase): @@ -61,7 +60,45 @@ class TestBackupAndRecovery(cloudstackTestCase): cls.services = cls.testClient.getParsedTestDataConfig() cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.services["mode"] = cls.zone.networktype cls.hypervisor = cls.testClient.getHypervisorInfo() + cls.domain = get_domain(cls.api_client) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + if cls.template == FAILED: + assert False, "get_template() failed to return template with description %s" % cls.services["ostype"] + + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["small"]["template"] = cls.template.id + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offerings"]["small"] + ) + + cls.vm = VirtualMachine.create( + cls.api_client, + cls.services["small"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.offering.id, + mode=cls.services["mode"] + ) + + cls._cleanup = [ + cls.offering, + cls.account + ] backup_enabled_cfg = Configurations.list( cls.api_client, @@ -76,7 +113,12 @@ class TestBackupAndRecovery(cloudstackTestCase): cls.backup_available = cls.backup_enabled and cls.backup_provider == "dummy" - cls._cleanup = [] + if cls.backup_available: + cls.external_policies = BackupPolicy.listExternal(cls.api_client, cls.zone.id) + cls.logger.debug("Importing backup policy %s - %s" % (cls.external_policies[0].policyid, cls.external_policies[0].name)) + cls.policy = BackupPolicy.importExisting(cls.api_client, cls.zone.id, cls.external_policies[0].policyid, cls.external_policies[0].name) + cls._cleanup.append(cls.policy) + return @classmethod @@ -110,18 +152,64 @@ class TestBackupAndRecovery(cloudstackTestCase): if not self.backup_available: self.skipTest("This test is only available when backup is enabled and dummy provider selected") - external_policies = BackupPolicy.listExternal(self.apiclient, self.zone.id) - self.logger.debug("External policies on dummy provider") - self.logger.debug(external_policies) - - for p in external_policies: - self.logger.debug("Importing backup policy %s - %s" % (p.policyid, p.name)) - policy = BackupPolicy.importExisting(self.apiclient, self.zone.id, p.policyid, p.name) + ext_policy = self.external_policies[1] + self.logger.debug("Importing backup policy %s - %s" % (ext_policy.policyid, ext_policy.name)) + policy = BackupPolicy.importExisting(self.apiclient, self.zone.id, ext_policy.policyid, ext_policy.name) imported_policies = BackupPolicy.listInternal(self.apiclient, self.zone.id) + self.assertIsInstance( + imported_policies, + list, + "List Backup Policies should return a valid response" + ) + self.assertNotEqual( + len(imported_policies), + 0, + "Check if the list API returns a non-empty response" + ) + self.logger.debug("Listing internal backup policies") self.logger.debug(imported_policies) - for p in imported_policies: - self.logger.debug("Deleting backup policy %s" % p.id) - BackupPolicy.delete(self.apiclient, p.id) \ No newline at end of file + self.logger.debug("Deleting backup policy %s" % policy.id) + policy.delete(self.apiclient) + + @attr(tags=["advanced", "backup"], required_hardware="false") + def test_AssignVMToBackupPolicy(self): + """ + Assign a VM to a backup policy + """ + if not self.backup_available: + self.skipTest("This test is only available when backup is enabled and dummy provider selected") + + self.logger.debug("Assigning VM %s to backup policy %s" % (self.vm.id, self.policy.id)) + + self.policy.assignVM( + self.apiclient, + self.vm.id, + self.zone.id + ) + + qresultset = self.dbclient.execute( + "select id from vm_instance where uuid='%s';" + % self.vm.id + ) + vm_id = qresultset[0][0] + + qresultset = self.dbclient.execute( + "select id from backup_policy where uuid='%s';" + % self.policy.id + ) + policy_id = qresultset[0][0] + + qresultset = self.dbclient.execute( + "select id from backup_policy_vm_map where policy_id='%d' and vm_id = '%d';" + % (policy_id, vm_id) + ) + + map = qresultset[0] + self.assertNotEqual( + map[0], + None, + "A mapping between VM and backup policy should exist on DB" + ) diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 1ca2bd96c0b..a1f5f1b6dd0 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -5391,7 +5391,7 @@ class BackupPolicy: cmd.zoneid = zoneid cmd.externalid = externalid cmd.name = name - return (apiclient.importBackupPolicy(cmd)) + return BackupPolicy(apiclient.importBackupPolicy(cmd).__dict__) @classmethod def listInternal(self, apiclient, zoneid): @@ -5410,19 +5410,18 @@ class BackupPolicy: cmd.external = True return (apiclient.listBackupPolicies(cmd)) - @classmethod - def delete(self, apiclient, policyid): + def delete(self, apiclient): """Delete an imported backup policy""" cmd = deleteBackupPolicy.deleteBackupPolicyCmd() - cmd.backuppolicyid = policyid + cmd.backuppolicyid = self.id return (apiclient.deleteBackupPolicy(cmd)) - @classmethod - def assignVM(self, apiclient, policyid, vmid): + def assignVM(self, apiclient, vmid, zoneid): """Assign a VM to a backup policy""" cmd = assignBackupPolicy.assignBackupPolicyCmd() - cmd.backuppolicyid = policyid + cmd.backuppolicyid = self.id cmd.virtualmachineid = vmid + cmd.zoneid = zoneid return (apiclient.assignBackupPolicy(cmd))